Skip to content

Instantly share code, notes, and snippets.

@eguiraud
Last active April 25, 2017 09:22
Show Gist options
  • Select an option

  • Save eguiraud/44d75348a14e30cc847fc616a2b38b93 to your computer and use it in GitHub Desktop.

Select an option

Save eguiraud/44d75348a14e30cc847fc616a2b38b93 to your computer and use it in GitHub Desktop.
misc metaprogramming utilities
#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
#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;
}
@eguiraud
Copy link
Author

wish-list:

  • zip
  • apply_to_each (both with variadic number of arguments and with variadic number of tuples)
  • fn_not

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment