Skip to content

Instantly share code, notes, and snippets.

@whanhee
Last active August 7, 2019 09:25
  • 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/6432970 to your computer and use it in GitHub Desktop.
Type class: mappable, intro. For explanation, see: http://functionalcpp.wordpress.com/2013/09/04/type-class-mappable-intro/
/*
* 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>
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>
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 "container_traits.h"
#include "eval.h"
#include "mappable.h"
namespace
{
template<int X>
int increment_by(int n){return n+X;}
}
int main()
{
// function
std::string words = "hello, world!";
auto string_length_plus_one = mappable<char(const std::string&)>::map(&increment_by<1>,&std::string::size);
std::cout << "size + 1: " << string_length_plus_one(words) << "\n";
// function with convenience function
std::cout << "1 plus 2: " << map(&increment_by<1>,&increment_by<1>)(1) << "\n";
// container
std::vector<int> numbers{0,1,2,3,4};
std::cout << "increment by 10: ";
for (int n : map(&increment_by<10>,numbers))
std::cout << n << " ";
std::cout << "\n";
// container with convenience function
auto shout = [](char c){return std::toupper(c,std::locale::classic());};
std::cout << "shout: " << map(shout,words) << "\n";
// container to different container
std::cout << "characters used: ";
for (char c : mappable<std::set<char>>::map([](char a){return a;},words))
std::cout << "(" << c << ") ";
std::cout << "\n";
// shared_ptr
std::shared_ptr<int> p_num = std::make_shared<int>(10);
std::cout << "increment by 5: " << *mappable<std::shared_ptr<int>>::map(&increment_by<5>,p_num) << "\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 "eval.h"
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>
auto map(F&& f, const T& in)
{
return mappable<T>::map(std::forward<F>(f),in);
}
// Function instances
template<class Result,class... Params>
struct mappable<Result(Params...)>
{
// Type value_type
using value_type = Result;
// mappable<R> map(R(A),mappable<A>)
template<class F,class A>
static auto map(F&& f, const A& in)
{
return [=](Params... params)
{
return eval(f,eval(in,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),mappable<A>)
template<class F,class A>
static auto map(F&& f, const std::shared_ptr<A>& in) -> typename std::shared_ptr<decltype(eval(f,*in))>
{
if (!in)
return std::shared_ptr<decltype(eval(f,*in))>();
return std::make_shared<decltype(eval(f,*in))>(std::move(eval(std::forward<F>(f),*in)));
}
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),mappable<A>)
template<class F,class A>
static auto map(F&& f, const A& in)
-> typename container_traits<T>::template rebind<
decltype(eval(f,std::declval<typename container_traits<A>::value_type>()))>
{
using B = decltype(eval(f,std::declval<typename container_traits<A>::value_type>()));
using T_B = typename container_traits<T>::template rebind<B>;
T_B out;
for (auto& a : in)
container_traits<T_B>::add_element(out,eval(std::forward<F>(f),a));
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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment