Skip to content

Instantly share code, notes, and snippets.

@Redchards
Last active January 1, 2016 17:33
Show Gist options
  • Select an option

  • Save Redchards/c7a454adb151e79f4e9d to your computer and use it in GitHub Desktop.

Select an option

Save Redchards/c7a454adb151e79f4e9d to your computer and use it in GitHub Desktop.
Simple and dirty metaprogramming library (C++14)
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