I wrote some code to implement a way to filter the arguments of a variadic template based on their type.
My current solution requires many helper meta-functions and I'm sure that there must be a better way. How would you improve this?
#include <type_traits>
#include <tuple>
//Type for storing a variadic template
template <typename ...>
struct list {};
//helper meta-functions
//list_rename function. Renames any template type. Needed to convert list to tuple later
template <typename source, template <typename ...> class TargetT>
struct list_rename_impl;
template <template <typename ...> class SourceT, typename ... Args, template <typename ...> class TargetT>
struct list_rename_impl<SourceT<Args...>, TargetT>
{
using type = TargetT<Args...>;
};
template <typename source, template <typename ...> class TargetT>
using list_rename = typename list_rename_impl<source, TargetT>::type;
//~END list_rename
//list_append function. Appends List U to T. Needed for the list_on_condition function
template <typename T, typename U>
struct list_append_impl
{};
template <typename ... LHS, typename ... RHS>
struct list_append_impl<list<LHS...>, list<RHS...>>
{
using type = list<LHS..., RHS...>;
};
template <typename T, typename U>
using list_append = typename list_append_impl<T, U>::type;
//~END list_append
//list_on_condition function. Creates a list of all passed Elements for which Condition<Element>::value is true.
//This way a parameter pack can be filtered on a condition and stored
template <template <typename> class Condition, typename ... Elements>
struct list_on_condition_impl
{};
template <template <typename> class Condition>
struct list_on_condition_impl<Condition>
{
using type = list<>; //No more elements to left, return empty list
};
template <template <typename> class Condition, typename Element>
struct list_on_condition_impl<Condition, Element>
{
using type = typename std::conditional //Only one element left. Return it, if it fullfills the Condition
<
Condition<Element>::value,
list<Element>,
list<>
>::type;
};
template <template <typename> class Condition, typename Element, typename ... Elements>
struct list_on_condition_impl<Condition, Element, Elements...>
{
using type = list_append //Check the first element and append it to the others recursively
<
typename std::conditional
//Current element gets wrapped in a list, if it fullfills the Condition,
//otherwise it resolves to an empty list
<
Condition<Element>::value,
list<Element>,
list<>
>::type,
//self recursion
typename list_on_condition_impl
<
Condition,
Elements...
>::type
>;
};
template <template <typename> class Condition, typename ... Elements>
using list_on_condition = typename list_on_condition_impl<Condition, Elements...>::type;
//~ END helpers
//A dummy struct
template <typename ... Args>
struct A
{};
//Another dummy struct
template <typename ... Args>
struct B {};
//The actual testcase struct
template <typename ... Args> //This variadic template has to be filtered
class C
{
private:
//condition meta-functions; Decide if the template parameter is an A<...> or a B<...>
template <typename T>
struct IsA : std::false_type {};
template <typename ... Elements>
struct IsA<A<Elements...>> : std::true_type{};
template <typename T>
struct IsB : std::false_type {};
template <typename ... Elements>
struct IsB<B<Elements...>> : std::true_type{};
public:
using As = list_on_condition<IsA, Args...>; //Filtering using the helper functions from above
using Bs = list_on_condition<IsB, Args...>;
using ATuple = list_rename<As, std::tuple>; //Converting the list container to a tuple type
using BTuple = list_rename<Bs, std::tuple>;
ATuple m_ATuple; //The template arguments in separated tuples
BTuple m_BTuple;
};
int main()
{
//Some checks to proof, that it works
using t1 = C<A<int>, B<int>>;
static_assert( std::is_same
<
typename t1::As,
list<A<int>>
>::value
&& std::is_same
<
typename t1::Bs,
list<B<int>>
>::value, "!!");
using t2 = C<A<int>, B<int>, A<double, int>, A<float>, B<long>>;
static_assert( std::is_same
<
typename t2::As,
list<A<int>, A<double, int>, A<float>>
>::value
&& std::is_same
<
typename t2::Bs,
list<B<int>, B<long>>
>::value, "!!");
//Is there a more elegant way of filtering variadic templates?
return 0;
}