Last active
April 25, 2017 09:22
-
-
Save eguiraud/44d75348a14e30cc847fc616a2b38b93 to your computer and use it in GitHub Desktop.
misc metaprogramming utilities
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
| #include <cstddef> // std::size_t | |
| #include <type_traits> // std::decay, std::is_same | |
| #include <vector> // need vector<bool> in is_container | |
| namespace meta { | |
| // lightweight storage for a collection of types | |
| // differently from std::tuple, no instantiation of objects of stored types is performed | |
| template <typename... Types> | |
| struct type_list { | |
| static constexpr std::size_t size = sizeof...(Types); | |
| }; | |
| // extract parameter types from a callable type | |
| template <typename T> | |
| struct callable_traits { | |
| using arg_types = typename callable_traits<decltype(&T::operator())>::arg_types; | |
| using arg_nodecay_types = typename callable_traits<decltype(&T::operator())>::arg_nodecay_types; | |
| using ret_type = typename callable_traits<decltype(&T::operator())>::ret_type; | |
| }; | |
| // spec for lambdas and std::function | |
| template <typename R, typename T, typename... Args> | |
| struct callable_traits<R (T::*)(Args...) const> { | |
| using arg_types = type_list<typename std::decay<Args>::type...>; | |
| using arg_nodecay_types = type_list<Args...>; | |
| using ret_type = R; | |
| }; | |
| // spec for mutable lambdas and functor classes | |
| template <typename R, typename T, typename... Args> | |
| struct callable_traits<R (T::*)(Args...)> { | |
| using arg_types = type_list<typename std::decay<Args>::type...>; | |
| using arg_nodecay_types = type_list<Args...>; | |
| using ret_type = R; | |
| }; | |
| // spec for function pointers | |
| template <typename R, typename... Args> | |
| struct callable_traits<R (*)(Args...)> { | |
| using arg_types = type_list<typename std::decay<Args>::type...>; | |
| using arg_nodecay_types = type_list<Args...>; | |
| using ret_type = R; | |
| }; | |
| // spec for free functions | |
| template <typename R, typename... Args> | |
| struct callable_traits<R(Args...)> { | |
| using arg_types = type_list<typename std::decay<Args>::type...>; | |
| using arg_nodecay_types = type_list<Args...>; | |
| using ret_type = R; | |
| }; | |
| // extract first type from a list of types | |
| template <typename T, typename...Rest> | |
| struct take_first { | |
| using type = T; | |
| }; | |
| template <typename...Types> | |
| using take_first_t = typename take_first<Types...>::type; | |
| // remove first type from a list of types, return a type_list containing the rest | |
| // e.g. remove_first_t<A,B,C> is type_list<B,C> | |
| template<typename T, typename...Rest> | |
| struct remove_first { | |
| using type = type_list<Rest...>; | |
| }; | |
| template<typename...Args> | |
| using remove_first_t = typename remove_first<Args...>::type; | |
| // return first of possibly many template parameters | |
| // for non-template types, the result is the type itself | |
| // e.g. take_first_param<U<A,B>> is A | |
| // take_first_param<T> is T | |
| template <typename T> | |
| struct take_first_param { | |
| using type = T; | |
| }; | |
| template <template <typename...> class Template, typename T, typename... Rest> | |
| struct take_first_param<Template<T, Rest...>> { | |
| using type = T; | |
| }; | |
| template <typename T> | |
| using take_first_param_t = typename take_first_param<T>::type; | |
| // remove first of possibly many template parameters | |
| // e.g. remove_first_param_t<U<A,B>> is U<B> | |
| template <typename> | |
| struct remove_first_param { | |
| }; | |
| template <typename T, template <typename...> class U, typename...Rest> | |
| struct remove_first_param<U<T, Rest...>> { | |
| using type = U<Rest...>; | |
| }; | |
| template <typename T> | |
| using remove_first_param_t = typename remove_first_param<T>::type; | |
| // compile-time integer sequence | |
| template <std::size_t...Ints> | |
| struct index_sequence { | |
| }; | |
| // compile-time integer sequence generator | |
| template <std::size_t N, std::size_t...I> | |
| struct make_index_sequence_impl : make_index_sequence_impl<N - 1, N - 1, I...> { | |
| }; | |
| template <std::size_t... I> | |
| struct make_index_sequence_impl<0, I...> { | |
| using type = index_sequence<I...>; | |
| }; | |
| template <std::size_t N> | |
| using make_index_sequence = typename make_index_sequence_impl<N>::type; | |
| // check for container traits | |
| template <typename T> | |
| struct is_container { | |
| using test_t = typename std::decay<T>::type; | |
| template <typename A> | |
| static constexpr bool Test(A *pt, A const *cpt = nullptr, decltype(pt->begin()) * = nullptr, | |
| decltype(pt->end()) * = nullptr, decltype(cpt->begin()) * = nullptr, | |
| decltype(cpt->end()) * = nullptr, typename A::iterator *pi = nullptr, | |
| typename A::const_iterator *pci = nullptr) | |
| { | |
| using It_t = typename A::iterator; | |
| using CIt_t = typename A::const_iterator; | |
| using V_t = typename A::value_type; | |
| return std::is_same<test_t, std::vector<bool>>::value || | |
| (std::is_same<decltype(pt->begin()), It_t>::value && std::is_same<decltype(pt->end()), It_t>::value && | |
| std::is_same<decltype(cpt->begin()), CIt_t>::value && std::is_same<decltype(cpt->end()), CIt_t>::value && | |
| std::is_same<decltype(**pi), V_t &>::value && std::is_same<decltype(**pci), V_t const &>::value); | |
| } | |
| template <typename A> | |
| static constexpr bool Test(...) | |
| { | |
| return false; | |
| } | |
| static const bool value = Test<test_t>(nullptr); | |
| }; | |
| } // namespace meta |
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
| #include "meta_utils.hpp" | |
| #include <memory> | |
| #include <vector> | |
| #include <functional> | |
| #include <type_traits> | |
| #include <tuple> | |
| struct Dummy { | |
| }; | |
| int freeFun1(int) | |
| { | |
| return 0; | |
| } | |
| Dummy freeFun2(Dummy &, int *, const std::vector<Dummy> &) | |
| { | |
| return Dummy(); | |
| } | |
| struct Functor1 { | |
| void operator()(int) { return; } | |
| }; | |
| struct Functor2 { | |
| Dummy operator()(Dummy &, int *, const std::vector<Dummy> &) { return Dummy(); } | |
| }; | |
| int main() | |
| { | |
| using namespace meta; | |
| // type_list | |
| static_assert(type_list<>::size == 0, ""); | |
| static_assert(type_list<void>::size == 1, ""); | |
| static_assert(type_list<int, double *>::size == 2, ""); | |
| // take_first | |
| static_assert(std::is_same<take_first_t<int, void, double*>, int>::value, ""); | |
| static_assert(std::is_same<take_first_t<type_list<int, int>, type_list<void>>, type_list<int, int>>::value, ""); | |
| // remove_first | |
| static_assert(std::is_same<remove_first_t<int, void, double *>, type_list<void, double *>>::value, ""); | |
| static_assert(std::is_same<remove_first_t<type_list<int, int>, type_list<void>>, type_list<type_list<void>>>::value, | |
| ""); | |
| // take_first_param | |
| static_assert(std::is_same<take_first_param_t<type_list<int,void>>,int>::value, ""); | |
| static_assert(std::is_same<take_first_param_t<std::tuple<void>>,void>::value, ""); | |
| // remove_first_param | |
| static_assert(std::is_same<remove_first_param_t<type_list<int,void>>,type_list<void>>::value, ""); | |
| static_assert(std::is_same<remove_first_param_t<std::tuple<void>>,std::tuple<>>::value, ""); | |
| // make_index_sequence | |
| static_assert(std::is_same<make_index_sequence<3>, index_sequence<0,1,2>>::value, ""); | |
| static_assert(std::is_same<make_index_sequence<0>, index_sequence<>>::value, ""); | |
| // is_container | |
| static_assert(is_container<std::vector<int>>::value, ""); | |
| static_assert(is_container<std::vector<bool>>::value, ""); | |
| static_assert(is_container<std::tuple<int,int>>::value == false, ""); | |
| /****** callable traits ******/ | |
| // free function | |
| static_assert(std::is_same<type_list<int>, callable_traits<decltype(freeFun1)>::arg_types>::value, ""); | |
| static_assert(std::is_same<type_list<int>, callable_traits<decltype(freeFun1)>::arg_nodecay_types>::value, ""); | |
| static_assert(std::is_same<int, callable_traits<decltype(freeFun1)>::ret_type>::value, ""); | |
| static_assert( | |
| std::is_same<type_list<Dummy, int *, std::vector<Dummy>>, callable_traits<decltype(freeFun2)>::arg_types>::value, | |
| ""); | |
| static_assert(std::is_same<type_list<Dummy &, int *, const std::vector<Dummy> &>, | |
| callable_traits<decltype(freeFun2)>::arg_nodecay_types>::value, | |
| ""); | |
| static_assert(std::is_same<Dummy, callable_traits<decltype(freeFun2)>::ret_type>::value, ""); | |
| // function pointer | |
| auto freeFun1Ptr = &freeFun1; | |
| auto freeFun2Ptr = &freeFun2; | |
| static_assert(std::is_same<type_list<int>, callable_traits<decltype(freeFun1Ptr)>::arg_types>::value, ""); | |
| static_assert(std::is_same<type_list<int>, callable_traits<decltype(freeFun1Ptr)>::arg_nodecay_types>::value, ""); | |
| static_assert(std::is_same<int, callable_traits<decltype(freeFun1Ptr)>::ret_type>::value, ""); | |
| static_assert(std::is_same<type_list<Dummy, int *, std::vector<Dummy>>, | |
| callable_traits<decltype(freeFun2Ptr)>::arg_types>::value, | |
| ""); | |
| static_assert(std::is_same<type_list<Dummy &, int *, const std::vector<Dummy> &>, | |
| callable_traits<decltype(freeFun2Ptr)>::arg_nodecay_types>::value, | |
| ""); | |
| static_assert(std::is_same<Dummy, callable_traits<decltype(freeFun2Ptr)>::ret_type>::value, ""); | |
| // functor class | |
| static_assert(std::is_same<type_list<int>, callable_traits<Functor1>::arg_types>::value, ""); | |
| static_assert(std::is_same<type_list<int>, callable_traits<Functor1>::arg_nodecay_types>::value, ""); | |
| static_assert(std::is_same<void, callable_traits<Functor1>::ret_type>::value, ""); | |
| static_assert(std::is_same<type_list<Dummy, int *, std::vector<Dummy>>, callable_traits<Functor2>::arg_types>::value, | |
| ""); | |
| static_assert(std::is_same<type_list<Dummy &, int *, const std::vector<Dummy> &>, | |
| callable_traits<Functor2>::arg_nodecay_types>::value, | |
| ""); | |
| static_assert(std::is_same<Dummy, callable_traits<Functor2>::ret_type>::value, ""); | |
| // mutable lambda | |
| auto boolvec = std::make_shared<std::vector<bool>>(2, false); | |
| auto lambda1 = [boolvec](const bool b) mutable -> std::vector<bool> & { | |
| boolvec->at(0) = b; | |
| return *boolvec; | |
| }; | |
| // lambda | |
| auto lambda2 = [](Dummy &, int *, const std::vector<Dummy> &) { return Dummy(); }; | |
| static_assert(std::is_same<type_list<bool>, callable_traits<decltype(lambda1)>::arg_types>::value, ""); | |
| static_assert(std::is_same<type_list<bool>, callable_traits<decltype(lambda1)>::arg_nodecay_types>::value, ""); | |
| static_assert(std::is_same<std::vector<bool> &, callable_traits<decltype(lambda1)>::ret_type>::value, ""); | |
| static_assert( | |
| std::is_same<type_list<Dummy, int *, std::vector<Dummy>>, callable_traits<decltype(lambda2)>::arg_types>::value, | |
| ""); | |
| static_assert(std::is_same<type_list<Dummy &, int *, const std::vector<Dummy> &>, | |
| callable_traits<decltype(lambda2)>::arg_nodecay_types>::value, | |
| ""); | |
| static_assert(std::is_same<Dummy, callable_traits<decltype(lambda2)>::ret_type>::value, ""); | |
| // std::function | |
| // masking signature int(int) of freeFunc1 | |
| std::function<int(double)> stdFun1(freeFun1); | |
| // masking signature of lambda2 | |
| std::function<Dummy(Dummy &, int *, const std::vector<Dummy> &)> stdFun2(lambda2); | |
| static_assert(std::is_same<type_list<double>, callable_traits<decltype(stdFun1)>::arg_types>::value, ""); | |
| static_assert(std::is_same<type_list<double>, callable_traits<decltype(stdFun1)>::arg_nodecay_types>::value, ""); | |
| static_assert(std::is_same<int, callable_traits<decltype(stdFun1)>::ret_type>::value, ""); | |
| static_assert( | |
| std::is_same<type_list<Dummy, int *, std::vector<Dummy>>, callable_traits<decltype(stdFun2)>::arg_types>::value, | |
| ""); | |
| static_assert(std::is_same<type_list<Dummy &, int *, const std::vector<Dummy> &>, | |
| callable_traits<decltype(stdFun2)>::arg_nodecay_types>::value, | |
| ""); | |
| static_assert(std::is_same<Dummy, callable_traits<decltype(stdFun2)>::ret_type>::value, ""); | |
| return 0; | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
wish-list: