Last active
April 15, 2022 15:41
-
-
Save whanhee/6490199 to your computer and use it in GitHub Desktop.
Type class: mappable, variadic. For explanation, see: http://functionalcpp.wordpress.com/2013/09/08/variadic-mapping/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2013, Daniel Park | |
* All rights reserved. | |
* | |
* Permission to modify and redistribute this software is granted to | |
* anyone provided the above copyright notice, this condition and the | |
* following disclaimer are retained. | |
* | |
* This software is provided "as is", without and express or implied | |
* warranty. In no event shall the author be liable for damages arising | |
* from the use of this software. | |
*/ | |
#pragma once | |
#include <deque> | |
#include <list> | |
#include <set> | |
#include <string> | |
#include <unordered_set> | |
#include <vector> | |
namespace fc | |
{ | |
template <class C> | |
struct container_traits | |
{ | |
// Type value_type | |
// void add_element(C&,T) | |
// void concat(C&,C) | |
// Type rebind<U> | |
}; | |
template<class C> | |
struct sequence_container_traits; | |
template<template<class,class> class C, class T, class A> | |
struct sequence_container_traits<C<T,A>> | |
{ | |
using value_type = T; | |
static void add_element(C<T,A>& c, const T& t) | |
{ | |
c.push_back(t); | |
} | |
static void concat(C<T,A>& lhs, const C<T,A>& rhs) | |
{ | |
lhs.insert(lhs.end(),rhs.begin(),rhs.end()); | |
} | |
template<class U> | |
using rebind = C<U,typename A::template rebind<U>::other>; | |
}; | |
template<class... Args> | |
struct container_traits<std::deque<Args...>> : public sequence_container_traits<std::deque<Args...>> | |
{}; | |
template<class... Args> | |
struct container_traits<std::list<Args...>> : public sequence_container_traits<std::list<Args...>> | |
{}; | |
template<class... Args> | |
struct container_traits<std::vector<Args...>> : public sequence_container_traits<std::vector<Args...>> | |
{}; | |
template<class C> | |
struct associative_container_traits; | |
template<template<class,class,class> class C, class T, template<class> class O, class A> | |
struct associative_container_traits<C<T,O<T>,A>> | |
{ | |
using value_type = T; | |
static void add_element(C<T,O<T>,A>& c, const T& t) | |
{ | |
c.insert(t); | |
} | |
static void concat(C<T,O<T>,A>& lhs, const C<T,O<T>,A>& rhs) | |
{ | |
lhs.insert(rhs.begin(),rhs.end()); | |
} | |
template<class U> | |
using rebind = C<U,O<U>,typename A::template rebind<U>::other>; | |
}; | |
template<class... Args> | |
struct container_traits<std::multiset<Args...>> : public associative_container_traits<std::multiset<Args...>> | |
{}; | |
template<class... Args> | |
struct container_traits<std::set<Args...>> : public associative_container_traits<std::set<Args...>> | |
{}; | |
template<class C> | |
struct hash_container_traits; | |
template<template<class,class,class,class> class C, class T, template<class> class H, template<class> class O, class A> | |
struct hash_container_traits<C<T,H<T>,O<T>,A>> | |
{ | |
using value_type = T; | |
static void add_element(C<T,H<T>,O<T>,A>& c, const T& t) | |
{ | |
c.insert(t); | |
} | |
static void concat(C<T,H<T>,O<T>,A>& lhs, const C<T,H<T>,O<T>,A>& rhs) | |
{ | |
lhs.insert(rhs.begin(),rhs.end()); | |
} | |
template<class U> | |
using rebind = C<U,H<U>,O<U>,typename A::template rebind<U>::other>; | |
}; | |
template<class... Args> | |
struct container_traits<std::unordered_multiset<Args...>> : public associative_container_traits<std::unordered_multiset<Args...>> | |
{}; | |
template<class... Args> | |
struct container_traits<std::unordered_set<Args...>> : public associative_container_traits<std::unordered_set<Args...>> | |
{}; | |
// basic_string | |
template<class C> | |
struct string_container_traits; | |
template<template<class,class,class> class C, class T, template<class> class K, class A> | |
struct string_container_traits<C<T,K<T>,A>> | |
{ | |
using value_type = T; | |
static void add_element(C<T,K<T>,A>& c, const T& t) | |
{ | |
c.push_back(t); | |
} | |
static void concat(C<T,K<T>,A>& lhs, const C<T,K<T>,A>& rhs) | |
{ | |
lhs+=rhs; | |
} | |
template<class U> | |
using rebind = C<U,K<U>,typename A::template rebind<U>::other>; | |
}; | |
template<class... Args> | |
struct container_traits<std::basic_string<Args...>> : public string_container_traits<std::basic_string<Args...>> | |
{}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2013, Daniel Park | |
* All rights reserved. | |
* | |
* Permission to modify and redistribute this software is granted to | |
* anyone provided the above copyright notice, this condition and the | |
* following disclaimer are retained. | |
* | |
* This software is provided "as is", without and express or implied | |
* warranty. In no event shall the author be liable for damages arising | |
* from the use of this software. | |
*/ | |
#pragma once | |
#include <type_traits> | |
#include <utility> | |
namespace fc | |
{ | |
template< | |
class F, class... Args, | |
class = typename std::enable_if<!std::is_member_function_pointer<F>::value>::type, | |
class = typename std::enable_if<!std::is_member_object_pointer<F>::value>::type | |
> | |
auto eval(F&& f, Args&&... args) -> decltype(f(std::forward<Args>(args)...)) | |
{ | |
return f(std::forward<Args>(args)...); | |
} | |
template<class R, class C, class... Args> | |
auto eval(R(C::*f)() const, const C& c, Args&&... args) -> R | |
{ | |
return (c.*f)(std::forward<Args>(args)...); | |
} | |
template<class R, class C, class... Args> | |
auto eval(R(C::*f)() const, C& c, Args&&... args) -> R | |
{ | |
return (c.*f)(std::forward<Args>(args)...); | |
} | |
template<class R, class C, class... Args> | |
auto eval(R(C::*f)(), C& c, Args&&... args) -> R | |
{ | |
return (c.*f)(std::forward<Args>(args)...); | |
} | |
template<class R, class C> | |
auto eval(R(C::*m), const C& c) -> const R& | |
{ | |
return c.*m; | |
} | |
template<class R, class C> | |
auto eval(R(C::*m), C& c) -> R& | |
{ | |
return c.*m; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2013, Daniel Park | |
* All rights reserved. | |
* | |
* Permission to modify and redistribute this software is granted to | |
* anyone provided the above copyright notice, this condition and the | |
* following disclaimer are retained. | |
* | |
* This software is provided "as is", without and express or implied | |
* warranty. In no event shall the author be liable for damages arising | |
* from the use of this software. | |
*/ | |
// Build using -std=gnu++1y | |
#include <iostream> | |
#include <functional> | |
#include <locale> | |
#include "mappable.h" | |
int main() | |
{ | |
std::vector<int> a{1,2,3}; | |
std::set<int> b{4,7,6,5}; | |
// containers | |
for (int n : fc::map(std::plus<int>(),a,b) ) | |
std::cout << n << " "; | |
std::cout << "\n"; | |
// shared_ptr | |
std::cout << *fc::map(std::multiplies<int>(),std::make_shared<int>(4),std::make_shared<int>(6)) << "\n"; | |
// functions | |
auto func = fc::mappable<int(int,int)>::map(std::minus<int>(),std::multiplies<int>(),std::plus<int>()); | |
std::cout << func(3,4) << "\n"; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2013, Daniel Park | |
* All rights reserved. | |
* | |
* Permission to modify and redistribute this software is granted to | |
* anyone provided the above copyright notice, this condition and the | |
* following disclaimer are retained. | |
* | |
* This software is provided "as is", without and express or implied | |
* warranty. In no event shall the author be liable for damages arising | |
* from the use of this software. | |
*/ | |
#pragma once | |
#include <memory> | |
#include "container_traits.h" | |
#include "tuple.h" | |
#include "tuple_eval.h" | |
namespace fc | |
{ | |
template<class T> | |
struct mappable | |
{ | |
// Type value_type | |
// mappable<R> map(R(A),mappable<A>) | |
static constexpr bool is_instance = false; | |
}; | |
// Convenience functions | |
template<class F, class T, class... Args> | |
auto map(F&& f, const T& in, const Args&... args) | |
{ | |
return mappable<T>::map(std::forward<F>(f),in,args...); | |
} | |
// Function instances | |
template<class Result,class... Params> | |
struct mappable<Result(Params...)> | |
{ | |
// Type value_type | |
using value_type = Result; | |
// mappable<R> map(R(A,Args...),mappable<A>,mappable<Args>...) | |
template<class F,class A,class... Args> | |
static auto map(F&& f, const A& in, const Args&... args) | |
{ | |
auto t_funcs = std::make_tuple(in,args...); | |
return [f,t_funcs](Params... params) | |
{ | |
return tuple_eval(f, | |
multi_tuple_eval(t_funcs,std::forward_as_tuple(params...)) | |
); | |
}; | |
} | |
static constexpr bool is_instance = true; | |
}; | |
// const member function | |
template<class Result, class Class, class... Params> | |
struct mappable<Result(Class::*)(Params...) const> : | |
public mappable<Result(const Class&,Params...)> | |
{}; | |
// member function | |
template<class Result, class Class, class... Params> | |
struct mappable<Result(Class::*)(Params...)> : | |
public mappable<Result(Class&,Params...)> | |
{}; | |
// member object | |
template<class Result, class Class> | |
struct mappable<Result(Class::*)> : | |
public mappable<Result(const Class&)> | |
{}; | |
// free function | |
template<class Result, class... Params> | |
struct mappable<Result(*)(Params...)> : | |
public mappable<Result(Params...)> | |
{}; | |
// shared_ptr instance | |
template<class T> | |
struct mappable<std::shared_ptr<T>> | |
{ | |
// Type value_type | |
using value_type = T; | |
// mappable<R> map(R(A,Args...),mappable<A>,mappable<Args>...) | |
template<class F,class A,class... Args> | |
static auto map(F&& f, const std::shared_ptr<A>& in, const std::shared_ptr<Args>&... args) | |
-> typename std::shared_ptr<decltype(eval(f,*in,(*args)...))> | |
{ | |
if (!tuple_all_valid(std::forward_as_tuple(in,args...))) | |
return std::shared_ptr<decltype(eval(f,*in,(*args)...))>(); | |
return std::make_shared<decltype(eval(f,*in,(*args)...))>(std::move(eval(std::forward<F>(f),*in,(*args)...))); | |
} | |
static constexpr bool is_instance = true; | |
}; | |
// container instances | |
template<class T> | |
struct default_container_mappable | |
{ | |
// Type value_type | |
using value_type = typename container_traits<T>::value_type; | |
// mappable<R> map(R(A,Args...),mappable<A>,mappable<Args>...) | |
template<class F,class A,class... Args> | |
static auto map(F&& f, const A& in, const Args&... args) | |
-> typename container_traits<T>::template rebind< | |
decltype(eval(f, | |
std::declval<typename container_traits<A>::value_type>(), | |
std::declval<typename container_traits<Args>::value_type>()... | |
))> | |
{ | |
using B = decltype(eval(f, | |
std::declval<typename container_traits<A>::value_type>(), | |
std::declval<typename container_traits<Args>::value_type>()... | |
)); | |
using T_B = typename container_traits<T>::template rebind<B>; | |
T_B out; | |
const auto ends = std::make_tuple(in.end(),(args.end())...); | |
for (auto iters = std::make_tuple(in.begin(),(args.begin())...); | |
!tuple_any_equal(iters,ends); | |
tuple_increment(iters)) | |
{ | |
container_traits<T_B>::add_element(out,tuple_eval(std::forward<F>(f),tuple_dereference(iters))); | |
} | |
return out; | |
} | |
static constexpr bool is_instance = true; | |
}; | |
#define FC_DEFAULT_CONTAINER_MAPPABLE(T)\ | |
template<class... Args>\ | |
struct mappable<T<Args...>> : public default_container_mappable<T<Args...>>\ | |
{}; | |
FC_DEFAULT_CONTAINER_MAPPABLE(std::deque); | |
FC_DEFAULT_CONTAINER_MAPPABLE(std::list); | |
FC_DEFAULT_CONTAINER_MAPPABLE(std::multiset); | |
FC_DEFAULT_CONTAINER_MAPPABLE(std::set); | |
FC_DEFAULT_CONTAINER_MAPPABLE(std::basic_string); | |
FC_DEFAULT_CONTAINER_MAPPABLE(std::unordered_multiset); | |
FC_DEFAULT_CONTAINER_MAPPABLE(std::unordered_set); | |
FC_DEFAULT_CONTAINER_MAPPABLE(std::vector); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2013, Daniel Park | |
* All rights reserved. | |
* | |
* Permission to modify and redistribute this software is granted to | |
* anyone provided the above copyright notice, this condition and the | |
* following disclaimer are retained. | |
* | |
* This software is provided "as is", without and express or implied | |
* warranty. In no event shall the author be liable for damages arising | |
* from the use of this software. | |
*/ | |
#pragma once | |
#include <tuple> | |
namespace fc | |
{ | |
template<size_t index> | |
struct tuple_helper | |
{ | |
template<class... Args> | |
static bool any_equal(const std::tuple<Args...>& lhs, const std::tuple<Args...>& rhs) | |
{ | |
if (std::get<index>(lhs) == std::get<index>(rhs)) | |
return true; | |
return tuple_helper<index-1>::any_equal(lhs,rhs); | |
} | |
template<class... Args> | |
static void increment(std::tuple<Args...>& a) | |
{ | |
tuple_helper<index-1>::increment(a); | |
std::get<index>(a)++; | |
} | |
template<class Tuple,class... Args> | |
static auto dereference(Tuple&& t, Args&&... args) | |
{ | |
return tuple_helper<index-1>::dereference( | |
std::forward<Tuple>(t), | |
std::get<index>(std::forward<Tuple>(t)), | |
std::forward<Args>(args)... | |
); | |
} | |
template<class... Args> | |
static bool all_valid(const std::tuple<Args...>& a) | |
{ | |
return bool(std::get<index>(a)) && tuple_helper<index-1>::all_valid(a); | |
} | |
}; | |
template<> | |
struct tuple_helper<0> | |
{ | |
template<class... Args> | |
static bool any_equal(const std::tuple<Args...>& lhs, const std::tuple<Args...>& rhs) | |
{ | |
return std::get<0>(lhs) == std::get<0>(rhs); | |
} | |
template<class... Args> | |
static void increment(std::tuple<Args...>& a) | |
{ | |
std::get<0>(a)++; | |
} | |
template<class Tuple,class... Args> | |
static auto dereference(Tuple&& t, Args&&... args) | |
{ | |
return std::tuple<decltype(*std::get<0>(t)),decltype(*args)...>(*std::get<0>(t),(*args)...); | |
} | |
template<class... Args> | |
static bool all_valid(const std::tuple<Args...>& a) | |
{ | |
return bool(std::get<0>(a)); | |
} | |
}; | |
template<class A,class... Args> | |
bool tuple_any_equal(const std::tuple<A,Args...>& lhs, const std::tuple<A,Args...>& rhs) | |
{ | |
return tuple_helper<sizeof...(Args)>::any_equal(lhs,rhs); | |
} | |
template<class A,class... Args> | |
void tuple_increment(std::tuple<A,Args...>& a) | |
{ | |
tuple_helper<sizeof...(Args)>::increment(a); | |
} | |
template<class Tuple> | |
auto tuple_dereference(Tuple&& t) | |
{ | |
return tuple_helper<std::tuple_size<typename std::remove_reference<Tuple>::type>::value-1>::dereference(std::forward<Tuple>(t)); | |
} | |
template<class A,class... Args> | |
bool tuple_all_valid(const std::tuple<A,Args...>& a) | |
{ | |
return tuple_helper<sizeof...(Args)>::all_valid(a); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2013, Daniel Park | |
* All rights reserved. | |
* | |
* Permission to modify and redistribute this software is granted to | |
* anyone provided the above copyright notice, this condition and the | |
* following disclaimer are retained. | |
* | |
* This software is provided "as is", without and express or implied | |
* warranty. In no event shall the author be liable for damages arising | |
* from the use of this software. | |
*/ | |
#pragma once | |
#include <tuple> | |
#include "eval.h" | |
namespace fc | |
{ | |
template<size_t index> | |
struct tuple_eval_helper | |
{ | |
template<class Func,class TArgs,class... Args> | |
static auto evaluate(Func&& f, TArgs&& t_args, Args&&... args) | |
-> decltype(tuple_eval_helper<index-1>::evaluate( | |
std::forward<Func>(f), | |
std::forward<TArgs>(t_args), | |
std::get<index>(std::forward<TArgs>(t_args)), | |
std::forward<Args>(args)... | |
)) | |
{ | |
return tuple_eval_helper<index-1>::evaluate( | |
std::forward<Func>(f), | |
std::forward<TArgs>(t_args), | |
std::get<index>(std::forward<TArgs>(t_args)), | |
std::forward<Args>(args)... | |
); | |
} | |
}; | |
template<> | |
struct tuple_eval_helper<0> | |
{ | |
template<class Func,class TArgs,class... Args> | |
static auto evaluate(Func&& f, TArgs&& t_args, Args&&... args) | |
-> decltype(eval( | |
std::forward<Func>(f), | |
std::get<0>(std::forward<TArgs>(t_args)), | |
std::forward<Args>(args)... | |
)) | |
{ | |
return eval( | |
std::forward<Func>(f), | |
std::get<0>(std::forward<TArgs>(t_args)), | |
std::forward<Args>(args)... | |
); | |
} | |
}; | |
template<class Func,class TArgs> | |
auto tuple_eval(Func&& f, TArgs&& t_args) | |
-> decltype(tuple_eval_helper<std::tuple_size< | |
typename std::remove_reference<TArgs>::type>::value-1>::evaluate( | |
std::forward<Func>(f), | |
std::forward<TArgs>(t_args) | |
)) | |
{ | |
return tuple_eval_helper<std::tuple_size< | |
typename std::remove_reference<TArgs>::type>::value-1>::evaluate( | |
std::forward<Func>(f), | |
std::forward<TArgs>(t_args) | |
); | |
} | |
template<size_t index> | |
struct multi_tuple_eval_helper | |
{ | |
template<class TFuncs,class TArgs,class... Results> | |
static auto evaluate(TFuncs&& t_funcs, TArgs&& t_args, Results&&... results) | |
-> decltype(multi_tuple_eval_helper<index-1>::evaluate( | |
std::forward<TFuncs>(t_funcs), | |
std::forward<TArgs>(t_args), | |
tuple_eval(std::get<index>(t_funcs),t_args), | |
std::forward<Results>(results)... | |
)) | |
{ | |
return multi_tuple_eval_helper<index-1>::evaluate( | |
std::forward<TFuncs>(t_funcs), | |
std::forward<TArgs>(t_args), | |
tuple_eval(std::get<index>(t_funcs),t_args), | |
std::forward<Results>(results)... | |
); | |
} | |
}; | |
template<> | |
struct multi_tuple_eval_helper<0> | |
{ | |
template<class TFuncs,class TArgs,class... Results> | |
static auto evaluate(TFuncs&& t_funcs, TArgs&& t_args, Results&&... results) | |
-> decltype(std::tuple<decltype(tuple_eval(std::get<0>(t_funcs),t_args)),Results...>( | |
tuple_eval(std::get<0>(t_funcs),t_args), | |
std::forward<Results>(results)... | |
)) | |
{ | |
return std::tuple<decltype(tuple_eval(std::get<0>(t_funcs),t_args)),Results...>( | |
tuple_eval(std::get<0>(t_funcs),t_args), | |
std::forward<Results>(results)... | |
); | |
} | |
}; | |
template<class TFuncs,class TArgs> | |
auto multi_tuple_eval(TFuncs&& t_funcs, TArgs&& t_args) | |
-> decltype(multi_tuple_eval_helper<std::tuple_size< | |
typename std::remove_reference<TFuncs>::type>::value-1>::evaluate( | |
std::forward<TFuncs>(t_funcs), | |
std::forward<TArgs>(t_args) | |
)) | |
{ | |
return multi_tuple_eval_helper<std::tuple_size< | |
typename std::remove_reference<TFuncs>::type>::value-1>::evaluate( | |
std::forward<TFuncs>(t_funcs), | |
std::forward<TArgs>(t_args) | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment