Last active
January 1, 2016 17:33
-
-
Save Redchards/c7a454adb151e79f4e9d to your computer and use it in GitHub Desktop.
Simple and dirty metaprogramming library (C++14)
This file contains hidden or 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
| namespace meta | |
| { | |
| template<class T> | |
| using invoke = typename T::type; | |
| template<class T> | |
| struct always | |
| { | |
| template<class ...> | |
| using type = T; | |
| }; | |
| template<class ... Args> | |
| using void_t = typename always<void>::type<Args...>; | |
| template<class ... Args> | |
| using true_t = typename always<std::true_type>::type<Args...>; | |
| template<class ... Args> | |
| using false_t = typename always<std::false_type>::type<Args...>; | |
| // "auto" is always good, but if you want to be more precise you could use : | |
| // decltype(T::value), which is the same thing as constexpr (or such) members can't be | |
| // references or pointers. If you want to be even more sur : | |
| // typename std::remove_cv<decltype(T::value)>::type | |
| // But it's far more ugly. | |
| template<class T> | |
| static constexpr auto value_of = T::value; | |
| template<class T, class = void> | |
| struct is_type : std::false_type {}; | |
| template<class T> | |
| struct is_type<T, void_t<typename T::type>> : std::true_type {}; | |
| template<class T, class = void> | |
| struct is_value : std::false_type {}; | |
| template<class T> | |
| struct is_value<T, void_t<typename T::value>> : std::true_type {}; | |
| template<class T> | |
| struct is_template : std::false_type {}; | |
| template<template<class ...> class T, class ... Args> | |
| struct is_template<T<Args...>> : std::true_type {}; | |
| /* "all" and "any" metafunctions, building blocks for a lot of stuff. | |
| As these functions are so common, a lot of implementation exists for them. I chose | |
| the easiest to understand, but the shortest is as follow : | |
| template<class ...> | |
| struct all : std::false_type{}; | |
| template<class Head, class ... Tail> | |
| struct all : std::conditional<Head::value, | |
| */ | |
| template<class ... List> | |
| struct all | |
| { | |
| static constexpr bool value = true; | |
| }; | |
| template<class Head, class ... Tail> | |
| struct all<Head, Tail...> | |
| { | |
| static constexpr bool value = value_of<Head> && value_of<all<Tail...>>; | |
| }; | |
| template<class ... List> | |
| struct any | |
| { | |
| static constexpr bool value = false; | |
| }; | |
| template<class Head, class ... Tail> | |
| struct any<Head, Tail...> | |
| { | |
| static constexpr bool value = value_of<Head> || value_of<any<Tail...>>; | |
| }; | |
| /* Basically just a wrapper around the passed type. What is this useful for ? Consider a possible | |
| implementation of "remove_const" : | |
| template<class T> | |
| struct remove_const : T {}; | |
| template<class T> | |
| struct remove_const<T const> : T {}; | |
| This implementation is flawed. Of course, if the passed type was a primitive type, let say an int, | |
| this would not compile, because we can't inherit from primitive types. Hence the identity function : | |
| template<class T> | |
| struct remove_const : identity<T> {}; | |
| template<class T> | |
| struct remove_const<T const> : identity<T> {}; | |
| Now, everything is okay ! | |
| */ | |
| template<class T> | |
| struct identity | |
| { | |
| using type = T; | |
| }; | |
| // In fact, operator() is defined for the standard functions, even if they are not objects. | |
| // I can't find in the standard where it's specified though. | |
| template<class T> | |
| struct callable_traits : public callable_traits<decltype(&T::operator())> | |
| {}; | |
| template<class Ret, class ... Args> | |
| struct callable_traits<Ret(Args...)> | |
| { | |
| using return_type = Ret; | |
| using function_type = Ret(Args...); | |
| static constexpr size_t arity = sizeof...(Args); | |
| using parameter_list = std::tuple<Args...>; | |
| template<size_t Tindex> | |
| struct argument_type | |
| { | |
| using type = typename std::tuple_element<Tindex, parameter_list>::type; | |
| }; | |
| template<size_t Tindex> | |
| using argument_type_t = typename argument_type<Tindex>::type; | |
| }; | |
| template<class Ret, class ... Args> | |
| struct callable_traits<Ret(*)(Args...)> : public callable_traits<Ret(Args...)> | |
| {}; | |
| template<class FunctionType> | |
| struct callable_traits<std::function<FunctionType>> : public callable_traits<FunctionType> | |
| {}; | |
| template<class Ret, class ClassType, class ... Args> | |
| struct callable_traits<Ret(ClassType::*)(Args...)> : public callable_traits<Ret(Args...)> | |
| { | |
| using class_type = ClassType; | |
| }; | |
| template<class Ret, class ClassType, class ... Args> | |
| struct callable_traits<Ret(ClassType::*)(Args...) const> : public callable_traits<Ret(Args...)> | |
| { | |
| using class_type = const ClassType; | |
| }; | |
| template<class Ret, class ClassType, class ... Args> | |
| struct callable_traits<Ret(ClassType::*)(Args...) volatile> : public callable_traits<Ret(Args...)> | |
| { | |
| using class_type = volatile ClassType; | |
| }; | |
| template<class Ret, class ClassType, class ... Args> | |
| struct callable_traits<Ret(ClassType::*)(Args...) const volatile> : public callable_traits<Ret(Args...)> | |
| { | |
| using class_type = const volatile ClassType; | |
| }; | |
| template<class ... Args> | |
| struct typelist | |
| { | |
| static constexpr size_t length = sizeof...(Args); | |
| }; | |
| template<size_t n> | |
| using index_constant = std::integral_constant<size_t, n>; | |
| template<class T, class = void> | |
| struct is_callable : std::false_type | |
| {}; | |
| template<class T> | |
| struct is_callable<T, void_t<decltype(std::declval<T>()())>> : std::true_type | |
| {}; | |
| template<class T> | |
| static constexpr bool is_callable_v = is_callable<T>::value; | |
| // A commony used metafunction | |
| // Unfortunatly, do not work for non type parameters | |
| template<template<class ...> class Template, class Specialization> | |
| struct is_specialization_of : std::false_type | |
| {}; | |
| template<template<class ...> class Template, class ... Args> | |
| struct is_specialization_of<Template, Template<Args...>> : std::true_type | |
| {}; | |
| template<template<class ...> class Template, class Specialization> | |
| static constexpr bool is_specialization_of_v = is_specialization_of<Template, Specialization>::value; | |
| template<class T> | |
| struct is_tuple | |
| { | |
| static constexpr bool value = is_specialization_of_v<std::tuple, T>; | |
| }; | |
| template<class T> | |
| static constexpr bool is_tuple_v = is_tuple<T>::value; | |
| template<template<class ...> class, template<class ...> class> | |
| struct is_same_template : std::false_type | |
| {}; | |
| template<template<class ...> class T> | |
| struct is_same_template<T, T> : std::true_type | |
| {}; | |
| template<template<class ...> class Generic, class ... Args> | |
| struct as_generic_typelist : identity<Generic<Args...>> | |
| {}; | |
| template<template<class ...> class Generic, class ... Args, template<class ...> class typelist_t> | |
| struct as_generic_typelist<Generic, typelist_t<Args...>> : identity<Generic<Args...>> | |
| {}; | |
| template<class ... Args> | |
| struct as_tuple : as_generic_typelist<std::tuple, Args...> | |
| {}; | |
| template<class List> | |
| struct remove_first; | |
| template<class ... Args, template<class ...> class typelist_t> | |
| struct remove_first<typelist_t<Args...>> | |
| : identity< | |
| invoke< | |
| as_generic_typelist<typelist_t, invoke<remove_first<typelist<Args...>>>>>> | |
| {}; | |
| template<class Head, class ... Tail> | |
| struct remove_first<typelist<Head, Tail...>> : identity<typelist<Tail...>> | |
| {}; | |
| template<class, class = typelist<>> | |
| struct remove_last | |
| {}; | |
| template<class ... Args, template<class ...> class typelist_t> | |
| struct remove_last<typelist_t<Args...>, | |
| std::enable_if_t<!value_of<is_same_template<typelist, typelist_t>>, typelist<>>> | |
| : identity< | |
| invoke< | |
| as_generic_typelist<typelist_t, invoke<remove_last<typelist<Args...>>>>>> | |
| {}; | |
| template<> | |
| struct remove_last<typelist<>, typelist<>> : identity<typelist<>> | |
| {}; | |
| template<class T, class ... Args1, class ... Args2> | |
| struct remove_last<typelist<T, Args1...>, typelist<Args2...>> | |
| : remove_last<typelist<Args1...>, typelist<Args2..., T>> | |
| {}; | |
| template<class T, class ... Args> | |
| struct remove_last<typelist<T>, typelist<Args...>> | |
| : identity<typelist<Args...>> | |
| {}; | |
| template<template<class ...> class F, class List, class = void> | |
| struct can_invoke_with_list : std::false_type | |
| {}; | |
| template<template<class ...> class F, class ... Args> | |
| struct can_invoke_with_list<F, | |
| typelist<Args...>, | |
| void_t<F<Args...>> | |
| > : std::true_type | |
| {}; | |
| template<template<class ...> class F, class ... Args> | |
| using can_invoke = can_invoke_with_list<F, typelist<Args...>>; | |
| template<template<class ...> class F, bool prefix, class List, class = void> | |
| struct extract_valid_from_list : identity<typelist<>> | |
| {}; | |
| template<template<class ...> class F, bool prefix, class Head, class ... Args> | |
| struct extract_valid_from_list<F, | |
| prefix, | |
| typelist<Head, Args...>, | |
| std::enable_if_t<!value_of<can_invoke<F, Head, Args...>>> | |
| > : extract_valid_from_list<F, prefix, | |
| invoke<std::conditional_t< | |
| prefix, | |
| remove_last<typelist<Head, Args...>>, | |
| remove_first<typelist<Head, Args...>>>>> | |
| {}; | |
| template<template<class ...> class F, bool prefix, class Head, class ... Args> | |
| struct extract_valid_from_list<F, | |
| prefix, | |
| typelist<Head, Args...>, | |
| std::enable_if_t<value_of<can_invoke<F, Head, Args...>>> | |
| > : identity<typelist<Head, Args...>> | |
| {}; | |
| template<template<class ...> class F, class List> | |
| using extract_valid_prefix_from_list = extract_valid_from_list<F, true, List>; | |
| template<template<class ...> class F, class ... Args> | |
| using extract_valid_prefix = extract_valid_prefix_from_list<F, typelist<Args...>>; | |
| template<template<class ...> class F, class List> | |
| using extract_valid_suffix_from_list = extract_valid_from_list<F, false, List>; | |
| template<template<class ...> class F, class ... Args> | |
| using extract_valid_suffix = extract_valid_suffix_from_list<F, typelist<Args...>>; | |
| /*template<class List1, class List2> | |
| struct test_tup; | |
| template<class ... Args1, class ... Args2, template<class...> class typelist_type> | |
| struct test_tup<typelist_type<Args1...>, typelist_type<Args2...>> | |
| {};*/ | |
| template<class ... Lists> | |
| struct typelist_cat; | |
| template<class ... Args1, class ... Args2> | |
| struct typelist_cat<typelist<Args1...>, typelist<Args2...>> | |
| : identity<typelist<Args1..., Args2...>> | |
| {}; | |
| template<class ... Args1, class ... Tail> | |
| struct typelist_cat<typelist<Args1...>, Tail...> | |
| : typelist_cat<typelist<Args1...>, invoke<typelist_cat<Tail...>>> | |
| {}; | |
| // WARNING ! Template types must be wrapped inside a typelist when currying. The reason is that they can be deduced | |
| // as being typelist themselves, hence making the specialization ambiguous. | |
| template<template<class ...> class F, class List, class = void> | |
| struct curry; | |
| template<template<class ...> class F, class T> | |
| struct curry<F, T, std::enable_if_t<!value_of<is_specialization_of<typelist, T>>>> : curry<F, typelist<T>, void> | |
| {}; | |
| template<template<class ...> class F, class ... Args> | |
| struct curry<F, Args...> : curry<F, typelist<Args...>> | |
| {}; | |
| template<template<class ...> class F, class ... Args, template<class ...> class typelist_t> | |
| struct curry<F, typelist_t<Args...>, std::enable_if_t<!value_of<is_same_template<typelist, typelist_t>>>> | |
| : curry<F, typelist<Args...>, void> | |
| {}; | |
| template<template<class ...> class F, class ... Args> | |
| struct curry<F, typelist<Args...>, std::enable_if_t<value_of<can_invoke<F, Args...>>>> | |
| { | |
| using type = invoke<F<Args...>>; | |
| }; | |
| template<template<class ...> class F, class ... Args> | |
| struct curry<F, typelist<Args...>, std::enable_if_t<!value_of<can_invoke<F, Args...>>>> | |
| { | |
| /*static_assert(invoke<extract_valid_prefix<F, Args...>>::length > 0, | |
| "The passed types are not supported by the metafunction");*/ | |
| template<class ... TArgs> | |
| using type = F<Args ..., TArgs ...>; | |
| }; | |
| namespace details | |
| { | |
| template<size_t n, class ExtractedList, class Head, class ... Tail> | |
| struct take_first_n_impl : take_first_n_impl< | |
| n - 1, | |
| invoke<typelist_cat<ExtractedList, typelist<Head>>>, | |
| Tail...> | |
| {}; | |
| template<class ExtractedList, class ... Args> | |
| struct take_first_n_impl<0, ExtractedList, Args...> : identity<ExtractedList> | |
| {}; | |
| template<size_t n, class Head, class ... Tail> | |
| struct drop_first_n_impl : drop_first_n_impl< | |
| n - 1, | |
| Tail...> | |
| {}; | |
| template<class Head, class ... Tail> | |
| struct drop_first_n_impl<0, Head, Tail...> : identity<typelist<Tail...>> | |
| {}; | |
| } | |
| // The final "cast" is necessary to get back the original type. I prefer to only work with typelists when doing computations, but the interface needs | |
| // to be as generic as possible, and thus always returning a typelist is not an option. | |
| template<size_t n, class List> | |
| struct take_first_n; | |
| template<size_t n, class ... Args, template<class ...> class typelist_t> | |
| struct take_first_n<n, typelist_t<Args...>> : identity< | |
| invoke< | |
| as_generic_typelist<typelist_t, invoke<details::take_first_n_impl<n, typelist<>, Args...>>>>> | |
| {}; | |
| template<size_t n, class List> | |
| struct drop_first_n; | |
| template<size_t n, class ... Args, template<class ...> class typelist_t> | |
| struct drop_first_n<n, typelist_t<Args...>> : identity< | |
| invoke< | |
| as_generic_typelist<typelist_t, invoke<details::drop_first_n_impl<n, typelist<>, Args...>>>>> | |
| {}; | |
| template<size_t n, class List> | |
| struct take_last_n; | |
| template<size_t n, class ... Args, template<class ...> class typelist_t> | |
| struct take_last_n<n, typelist_t<Args...>> : drop_first_n<(sizeof...(Args) - n), typelist_t<Args...>> | |
| {}; | |
| template<size_t n, class List> | |
| struct drop_last_n; | |
| template<size_t n, class ... Args, template<class ...> class typelist_t> | |
| struct drop_last_n<n, typelist_t<Args...>> : take_first_n<(sizeof...(Args) - n), typelist_t<Args...>> | |
| {}; | |
| // TODO implement take_while and drop_while | |
| /*template<template<class ...> class, class> | |
| struct apply_valid_prefix; | |
| template<template<class ...> class F, class ... Args, template<class ...> class typelist_t> | |
| struct apply_valid_prefix<F, typelist_t<Args...>>*/ | |
| /*template<class T, class = void> | |
| struct ignore_if_nothing : identity<typelist<>> | |
| {}; | |
| template<class T> | |
| struct ignore_if_nothing<T, void_t<typename T::type>> : identity<typename T::type> | |
| {};*/ | |
| template<class T> | |
| struct to_int : identity<int> | |
| {}; | |
| template<class T1, class T2> | |
| struct to_pair : identity<std::pair<T1, T2>> | |
| {}; | |
| namespace details | |
| { | |
| template<template<class ...> class, template<class ...> class, class, class = void> | |
| struct apply_if_impl; | |
| template<class ... Args, template<class ...> class P, template<class ...> class F, template<class ...> class typelist_t> | |
| struct apply_if_impl<P, F, typelist_t<Args...>, std::enable_if_t<!value_of<is_same_template<typelist_t, typelist>>>> | |
| : identity< | |
| invoke< | |
| as_generic_typelist<typelist_t, invoke<apply_if_impl<P, F, typelist<Args...>, void>>>>> | |
| {}; | |
| template<template<class ...> class P, template<class ...> class F, class ... Args> | |
| struct apply_if_impl<P, F, typelist<Args...>, std::enable_if_t<value_of<can_invoke<F, Args...>>>> | |
| : identity<std::conditional_t<value_of<P<Args...>>, typelist<invoke<F<Args...>>>, typelist<Args...>>> | |
| {}; | |
| template<template<class ...> class P, template<class ...> class F, class ... Args> | |
| struct apply_if_impl<P, F, typelist<Args...>, std::enable_if_t<!value_of<can_invoke<F, Args...>>>> | |
| { | |
| using valid_prefix = invoke<extract_valid_prefix<F, Args...>>; | |
| using type = invoke<typelist_cat< | |
| invoke<apply_if_impl<P, F, valid_prefix, void>>, | |
| invoke<apply_if_impl<P, F, invoke<drop_first_n<valid_prefix::length, typelist<Args...>>>, void>> | |
| >>; | |
| }; | |
| } | |
| template<template<class ...> class P, template<class ...> class F, class ... Args> | |
| struct apply_if : details::apply_if_impl<P, F, typelist<Args...>> | |
| {}; | |
| template<template<class ...> class P, template<class ...> class F, class ... Args, template<class ...> class typelist_t> | |
| struct apply_if<P, F, typelist_t<Args...>> : details::apply_if_impl<P, F, typelist<Args...>> | |
| {}; | |
| template<template<class ...> class F, class ... Args> | |
| struct apply : apply_if<true_t, F, typelist<Args...>> | |
| {}; | |
| template<template<class ...> class F, class ... Args, template<class ...> class typelist_t> | |
| struct apply<F, typelist_t<Args...>> : apply_if<true_t, F, typelist_t<Args...>> | |
| {}; | |
| namespace details | |
| { | |
| template<template<class ...> class, class, class> | |
| struct filter_impl; | |
| template<template<class ...> class P, class Head, class ... Tail, class ... Args, template<class ...> class typelist_t> | |
| struct filter_impl<P, typelist_t<Head, Tail...>, typelist_t<Args...>> | |
| : filter_impl<P, typelist_t<Tail...>, std::conditional_t<value_of<P<Head>>, typelist_t<Args..., Head>, typelist_t<Args...>>> | |
| {}; | |
| template<template<class ...> class P, class ... Args, template<class ...> class typelist_t> | |
| struct filter_impl<P, typelist_t<>, typelist_t<Args...>> : identity<typelist_t<Args...>> | |
| {}; | |
| } | |
| template<template<class ...> class P, class ... Args> | |
| struct filter : details::filter_impl<P, typelist<Args...>, typelist<>> | |
| {}; | |
| template<template<class ...> class P, class ... Args, template<class ...> class typelist_t> | |
| struct filter<P, typelist_t<Args...>> : details::filter_impl<P, typelist_t<Args...>, typelist_t<>> | |
| {}; | |
| template<class T> | |
| struct build_index_sequence; | |
| template<size_t n> | |
| struct build_index_sequence<index_constant<n>> | |
| : identity<std::make_index_sequence<n>> | |
| {}; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment