Last active
October 28, 2017 04:16
-
-
Save gnaggnoyil/fe169933620b593cc7a422b685c2f251 to your computer and use it in GitHub Desktop.
C++11 conforming implementation of make_overload
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
/* | |
author: gnaggnoyil(gnagnoyil at gmail.com) | |
license: WTFPL | |
*/ | |
#include <type_traits> | |
#include <utility> | |
#include <functional> | |
#define NAMESPACE_STD_BEGIN namespace utility{ | |
#define NAMESPACE_STD_END } | |
#define NAMESPACE_STD utility | |
NAMESPACE_STD_BEGIN | |
namespace meta{ | |
namespace _detail{ | |
template <typename T, typename ...> | |
struct sfinae_context{ | |
using type = T; | |
}; | |
template <typename ...U> | |
using sfinae_context_t = typename sfinae_context<U...>::type; | |
template <class ...> | |
struct conjunction : public std::true_type{}; | |
template <class B> | |
struct conjunction<B> : public B{}; | |
template <class B1, class ...Bs> | |
struct conjunction<B1, Bs...> | |
: public std::conditional_t<bool(B1::value), conjunction<Bs...>, B1>{}; | |
template <class ...Bs> | |
constexpr bool conjunction_v = conjunction<Bs...>::value; | |
template <typename > | |
struct is_reference_wrapper : public std::false_type{}; | |
template <typename T> | |
struct is_reference_wrapper<std::reference_wrapper<T>> : public std::true_type{}; | |
template <typename T> | |
constexpr bool is_reference_wrapper_v = is_reference_wrapper<T>::value; | |
} // namespace _detail | |
namespace _detail{ | |
template <typename Callable, typename = void> | |
class func_holder; | |
template <typename Functor> | |
class func_holder<Functor, | |
typename std::enable_if<std::is_class<Functor>::value>::type> | |
: private Functor{ | |
private: | |
using base_t = Functor; | |
public: | |
template <typename Functorref, | |
typename std::enable_if_t<std::is_same<typename std::decay<Functorref>::type, Functor>::value>::type * = nullptr> | |
explicit func_holder(Functorref &&func) noexcept(noexcept(base_t(std::forward<Functorref &&>(func)))) | |
: base_t(std::forward<Functorref &&>(func)){} | |
func_holder(const func_holder &) = default; | |
func_holder(func_holder &&) = default; | |
func_holder &operator=(const func_holder &) = default; | |
func_holder &operator=(func_holder &&) = default; | |
~func_holder() = default; | |
using base_t::operator(); | |
}; // class func_holder | |
template <typename Ret, typename ...Args> | |
class func_holder<Ret (*)(Args...)>{ | |
private: | |
using func_ptr_t = Ret (*)(Args...); | |
func_ptr_t m_func_ptr; | |
public: | |
explicit func_holder(func_ptr_t func_ptr) noexcept | |
: m_func_ptr(func_ptr){} | |
func_holder(const func_holder &) = default; | |
func_holder(func_holder &&) = default; | |
func_holder &operator=(const func_holder &) = default; | |
func_holder &operator=(func_holder &&) = default; | |
~func_holder() = default; | |
template <typename ...Argref> | |
auto operator()(Argref &&...args) const noexcept(noexcept((*m_func_ptr)(std::forward<Argref>(args)...))) | |
-> sfinae_context_t<Ret, decltype((*m_func_ptr)(std::forward<Argref>(args)...))>{ | |
return (*m_func_ptr)(std::forward<Argref>(args)...); | |
} | |
}; //class func_holder; | |
template <typename T, typename Base> | |
class func_holder<T Base::*>{ | |
private: | |
using dm_ptr_t = T Base:: *; | |
dm_ptr_t m_dm_ptr; | |
public: | |
static_assert(!(std::is_function<T>::value)); | |
explicit func_holder(dm_ptr_t dm_ptr) noexcept | |
: m_dm_ptr(dm_ptr){} | |
func_holder(const func_holder &) = default; | |
func_holder(func_holder &&) = default; | |
func_holder &operator=(const func_holder &) = default; | |
func_holder &operator=(func_holder &&) = default; | |
~func_holder() = default; | |
template <typename Derived, | |
typename std::enable_if<std::is_base_of<Base, typename std::decay<Derived>::type>::value>::type * = nullptr> | |
auto operator()(Derived &&derived) const noexcept(noexcept(std::forward<Derived>(derived).*m_dm_ptr)) | |
-> decltype(std::forward<Derived>(derived).*m_dm_ptr){ | |
return std::forward<Derived>(derived).*m_dm_ptr; | |
} | |
template <typename Refwrap, | |
typename std::enable_if<is_reference_wrapper_v<typename std::decay<Refwrap>::type>>::type * = nullptr> | |
auto operator()(Refwrap &&refwrap) const noexcept(noexcept(refwrap.get().*m_dm_ptr)) | |
-> decltype(refwrap.get().*m_dm_ptr){ | |
return refwrap.get().*m_dm_ptr; | |
} | |
template <typename Pointer, | |
typename std::enable_if<!std::is_base_of<Base, typename std::decay<Pointer>::type>::value>::type * = nullptr, | |
typename std::enable_if<!is_reference_wrapper_v<typename std::decay<Pointer>::type>>::type * = nullptr> | |
auto operator()(Pointer &&pointer) const noexcept(noexcept((*std::forward<Pointer>(pointer)).*m_dm_ptr)) | |
-> decltype((*std::forward<Pointer>(pointer)).*m_dm_ptr){ | |
return (*std::forward<Pointer>(pointer)).*m_dm_ptr; | |
} | |
}; // class func_holder | |
template <typename Ret, typename Base, typename ...Args> | |
class func_holder<Ret (Base::*)(Args...)>{ | |
private: | |
using mf_ptr_t = Ret (Base::*)(Args...); | |
mf_ptr_t m_mf_ptr; | |
public: | |
explicit func_holder(mf_ptr_t mf_ptr) noexcept | |
: m_mf_ptr(mf_ptr){} | |
func_holder(const func_holder &) = default; | |
func_holder(func_holder &&) = default; | |
func_holder &operator=(const func_holder &) = default; | |
func_holder &operator=(func_holder &&) = default; | |
~func_holder() = default; | |
template <typename Derived, typename ...Argrefs, | |
typename std::enable_if<std::is_base_of<Base, typename std::decay<Derived>::type>::value>::type * = nullptr> | |
auto operator()(Derived &&derived, Argrefs &&...args) const | |
noexcept(noexcept((std::forward<Derived>(derived).*m_mf_ptr)(std::forward<Argrefs>(args)...))) | |
-> sfinae_context_t<Ret, decltype((std::forward<Derived>(derived).*m_mf_ptr)(std::forward<Argrefs>(args)...))>{ | |
return (std::forward<Derived>(derived).*m_mf_ptr)(std::forward<Argrefs>(args)...); | |
} | |
template <typename Refwrap, typename ...Argrefs, | |
typename std::enable_if<is_reference_wrapper_v<typename std::decay<Refwrap>::type>>::type * = nullptr> | |
auto operator()(Refwrap &&refwrap, Argrefs &&...args) const | |
noexcept(noexcept((refwrap.get().*m_mf_ptr)(std::forward<Argrefs>(args)...))) | |
-> sfinae_context_t<Ret, decltype((refwrap.get().*m_mf_ptr)(std::forward<Argrefs>(args)...))>{ | |
return (refwrap.get().*m_mf_ptr)(std::forward<Argrefs>(args)...); | |
} | |
template <typename Pointer, typename ...Argrefs, | |
typename std::enable_if<!std::is_base_of<Base, typename std::decay<Pointer>::type>::value>::type * = nullptr, | |
typename std::enable_if<!is_reference_wrapper_v<typename std::decay<Pointer>::type>>::type * = nullptr> | |
auto operator()(Pointer &&pointer, Argrefs &&...args) const | |
noexcept(noexcept(((*std::forward<Pointer>(pointer)).*m_mf_ptr)(std::forward<Argrefs>(args)...))) | |
-> sfinae_context_t<Ret, decltype(((*std::forward<Pointer>(pointer)).*m_mf_ptr)(std::forward<Argrefs>(args)...))>{ | |
return ((*std::forward<Pointer>(pointer)).*m_mf_ptr)(std::forward<Argrefs>(args)...); | |
} | |
}; // class func_holder | |
template <typename ...Callables> | |
class overload_set; | |
template <typename Callable> | |
class overload_set<Callable> : private func_holder<Callable>{ | |
private: | |
using base_t = func_holder<Callable>; | |
public: | |
template <typename Callableref, | |
typename std::enable_if<std::is_same<typename std::decay<Callableref>::type, Callable>::value>::type * = nullptr> | |
explicit overload_set(Callableref &&callable) noexcept(noexcept(base_t(std::forward<Callableref>(callable)))) | |
: base_t(std::forward<Callableref>(callable)){} | |
using base_t::operator(); | |
}; // class overload_set | |
template <typename Callable1, typename Callable2, typename ...Callables> | |
class overload_set<Callable1, Callable2, Callables...> | |
: private func_holder<Callable1>, private overload_set<Callable2, Callables...>{ | |
private: | |
using base_t = func_holder<Callable1>; | |
using rest_t = overload_set<Callable2, Callables...>; | |
public: | |
template <typename Callableref1, typename Callableref2, typename ...Callablerefs, | |
typename std::enable_if<conjunction_v< | |
std::is_same<typename std::decay<Callableref1>::type, Callable1>, | |
std::is_same<typename std::decay<Callableref2>::type, Callable2>, | |
std::is_same<typename std::decay<Callablerefs>::type, Callables>... | |
>>::type * = nullptr> | |
explicit overload_set(Callableref1 &&callable1, Callableref2 &&callable2, Callablerefs &&...callables) | |
noexcept( | |
noexcept(base_t(std::forward<Callableref1>(callable1))) && | |
noexcept(rest_t(std::forward<Callable2>(callable2), std::forward<Callablerefs>(callables)...)) | |
) | |
: base_t(std::forward<Callableref1>(callable1)), | |
rest_t(std::forward<Callable2>(callable2), std::forward<Callablerefs>(callables)...){} | |
using base_t::operator(); | |
using rest_t::operator(); | |
}; // class overload_set | |
} // namespace _detail | |
template <typename ...Callablerefs> | |
_detail::overload_set<typename std::decay<Callablerefs>::type...> make_overload(Callablerefs &&...callables) | |
noexcept(noexcept(_detail::overload_set<typename std::decay<Callablerefs>::type...>(std::forward<Callablerefs>(callables)...))){ | |
return _detail::overload_set<typename std::decay<Callablerefs>::type...>(std::forward<Callablerefs>(callables)...); | |
} | |
} // namespace meta | |
NAMESPACE_STD_END | |
/* | |
#include <iostream> | |
#include "make_overload.hpp" | |
using namespace NAMESPACE_STD; | |
int foo(int){ | |
return 1; | |
} | |
class Bar{ | |
public: | |
char x = 'a'; | |
double m_bar(double, double){ | |
return 3.0; | |
} | |
}; | |
int main(){ | |
auto f = meta::make_overload( | |
foo, | |
[](int &) -> int{ | |
return 0; | |
}, | |
&Bar::x, | |
&Bar::m_bar | |
); | |
std::cout << f(1) << std::endl; | |
int x = 1; | |
std::cout << f(x) << std::endl; | |
Bar bar; | |
std::cout << f(bar) << std::endl; | |
std::cout << f(bar, 0.0, 0.0) << std::endl; | |
return 0; | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment