Skip to content

Instantly share code, notes, and snippets.

@whanhee
Last active April 15, 2022 15:41
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
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/
/*
* 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...>>
{};
}
/*
* 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;
}
}
/*
* 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;
}
/*
* 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);
}
/*
* 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);
}
}
/*
* 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