Skip to content

Instantly share code, notes, and snippets.

@gnaggnoyil
Last active October 28, 2017 04:16
Show Gist options
  • Save gnaggnoyil/fe169933620b593cc7a422b685c2f251 to your computer and use it in GitHub Desktop.
Save gnaggnoyil/fe169933620b593cc7a422b685c2f251 to your computer and use it in GitHub Desktop.
C++11 conforming implementation of make_overload
/*
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