Extract template class default parameters - c++

Is there a way to extract a template class' default parameters only knowing the unspecialized template class at compile time?
I know how to extract an instantiated template class' parameters, like this:
// Just an example class for the demonstration
template<class A, class B=void>
struct example {};
// Template parameters storage class
template<class...An>
struct args;
// MPL utility that extracts the template arguments from a template class
template<class C>
struct get_args
{
typedef args<> type;
};
template<template<class...> class C, class...An>
struct get_args< C<An...> >
{
typedef args<An...> type;
};
// And the assertion
static_assert(
std::is_same
< args<int,void>,
get_args< example<int> >::type
>::value,
"Check this out"
);
Now what I would like to know is if decltype or anything else could be used to retrieve the default template parameters only from the unspecialized template class:
// MPL utility that extract template default arguments from a template class
template<template<class...> class C>
struct get_default_args
{
typedef /* what goes here? */ type;
};
// And the assertion
static_assert(
std::is_same
< args<void>,
get_default_args< example >::type
>::value,
"Check this out"
);
For now, I only figured out how to extract the number of parameters of a template class, not their default value:
namespace detail {
template< template<class> class >
boost::mpl::size_t<1> deduce_nb_args();
template< template<class,class> class >
boost::mpl::size_t<2> deduce_nb_args();
template< template<class,class,class> class >
boost::mpl::size_t<3> deduce_nb_args();
/* ... and so on ... */
}
// MPL utility that extract the number of template arguments of a template class
template<template<class...> class C>
struct get_nb_args :
decltype(detail::deduce_nb_args<C>()) {};
// And the assertion
static_assert(
get_nb_args< example >::value == 2,
"Check this out"
);
Edit
It seems that at the end, and once again, MSVC prevents me to perform this operation.
Something like the following makes the compiler crash with a fatal error C1001: An internal error has occurred in the compiler.
template<template<class...> class D> static
boost::boost::mpl::int_<0> mandatory(D<>*)
{ return boost::boost::mpl::int_<0>(); }
template<template<class...> class D> static
boost::mpl::int_<1> mandatory(D<void>*)
{ return boost::mpl::int_<0>(); }
template<template<class...> class D> static
boost::mpl::int_<2> mandatory(D<void,void>*)
{ return boost::mpl::int_<0>(); }
template<template<typename...> class D> static
boost::mpl::int_<-1> mandatory(...)
{ return boost::mpl::int_<-1>(); }
int check()
{
return mandatory<example>(nullptr);
}
Trying next one results in error C2976: 'D' : too few template arguments
template<template<class,class> class D> static
boost::mpl::int_<0> mandatory2(D<>*)
{ return boost::mpl::int_<0>(); }
template<template<class,class> class D> static
boost::mpl::int_<1> mandatory2(D<void>*)
{ return boost::mpl::int_<0>(); }
template<template<class,class> class D> static
boost::mpl::int_<2> mandatory2(D<void,void>*)
{ return boost::mpl::int_<0>(); }
int check2()
{
return mandatory2<example>(nullptr);
}
So to me it seems that no matter the approach, MSVC forbids programmatic instantiation of a template class making use of default parameters.
In turn, it looks impossible to me to use a SFINAE technique to extract:
1. the mandatory number of parameters;
2. the types of default parameters.
Edit 2
Ok, after several tests it seems to be a bug with MSVC occurring when trying to programmatically instantiate a template class only using default arguments.
I filed a bug report here and another one here.
Here is a traits class allowing to check if a class is instantiable using given template parameters that does not make the compiler crash, but do not evaluates to true for fully default instantiable classes.
namespace detail {
typedef std::true_type true_;
typedef std::false_type false_;
template< template<class...> class D, class...An >
true_ instantiable_test(D<An...>*);
template< template<class...> class D, class...An >
false_ instantiable_test(...);
}
template< template<class...> class C, class...An >
struct is_instantiable : decltype(detail::instantiable_test<C,An...>(nullptr)) {};
That being said, it seems impossible with MSVC to retrieve the template type instantiated with default parameters. Typically the following does not compile:
template< template<class...> class T, class...An >
struct get_default_v0
{
typedef T<An...> type;
};
namespace detail {
template< template<class...> class T, class...An >
T<An...> try_instantiate();
} // namespace detail
template< template<class...> class T, class...An >
struct get_default_v1
{
typedef decltype(detail::try_instantiate<T,An...>()) type;
};
// C2976
static_assert(
std::is_same< get_default_v0<example,int> , example<int,void> >::value,
"v0"
);
// C2976
static_assert(
std::is_same< get_default_v1<example,int> , example<int,void> >::value,
"v1"
);

I'd try something like this:
template <typename ...> struct Get2;
template <template <typename...> class Tmpl,
typename A, typename B, typename ...Rest>
struct Get2<Tmpl<A, B, Rest...>>
{
using type = B;
};
template <template <typename...> class Tmpl> struct GetDefault2
{
using type = typename Get2<Tmpl<void>>::type;
};

I realize this is a long answer, but here is a possible approach:
#include <type_traits>
namespace tmpl
{
namespace detail
{
template<template<typename...> class C, typename... T>
struct is_valid_specialization_impl
{
template<template<typename...> class D>
static std::true_type test(D<T...>*);
template<template<typename...> class D>
static std::false_type test(...);
using type = decltype(test<C>(0));
};
} // namespace detail
template<template<typename...> class C, typename... T>
using is_valid_specialization = typename detail::is_valid_specialization_impl<C, T...>::type;
} // namespace tmpl
The following is a partial copy/paste from my github repository, dont worry too much about it, most of the code is to find the minimum/maximum number of template arguments required (in this case we only care about the minimum number):
#if !defined(TEMPLATE_ARGS_MAX_RECURSION)
#define TEMPLATE_ARGS_MAX_RECURSION 30
#endif
namespace tmpl
{
namespace detail
{
enum class specialization_state {
invalid,
valid,
invalid_again
};
template<bool, template<typename...> class C, typename... T>
struct num_arguments_min
: std::integral_constant<int, sizeof...(T)>
{ };
template<template<typename...> class C, typename... T>
struct num_arguments_min<false, C, T...>
: num_arguments_min<is_valid_specialization<C, T..., char>::value, C, T..., char>
{ };
template<specialization_state, template<typename...> class C, typename... T>
struct num_arguments_max;
template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::invalid, C, T...>
: num_arguments_max<
is_valid_specialization<C, T..., char>::value
? specialization_state::valid
: specialization_state::invalid,
C,
T..., char
>
{ };
template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::valid, C, T...>
: std::conditional<
((sizeof...(T) == 0) || (sizeof...(T) == TEMPLATE_ARGS_MAX_RECURSION)),
std::integral_constant<int, -1>,
num_arguments_max<
is_valid_specialization<C, T..., char>::value
? specialization_state::valid
: specialization_state::invalid_again,
C,
T..., char
>
>::type
{ };
template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::invalid_again, C, T...>
: std::integral_constant<int, (sizeof...(T) - 1)>
{ };
} // namespace detail
template<template<typename...> class C>
struct template_traits
{
constexpr static int args_min = detail::num_arguments_min<is_valid_specialization<C>::value, C>::value;
constexpr static int args_max = detail::num_arguments_max<is_valid_specialization<C>::value
? detail::specialization_state::valid
: detail::specialization_state::invalid,
C>::value;
constexpr static bool is_variadic = (args_max < args_min);
template<typename... T>
using specializable_with = is_valid_specialization<C, T...>;
};
} // namespace tmpl
Some helper types specifically for your question:
template<typename... Ts>
struct type_sequence { };
namespace detail
{
template<int N, typename...>
struct skip_n_types;
template<int N, typename H, typename... Tail>
struct skip_n_types<N, H, Tail...>
: skip_n_types<(N - 1), Tail...> { };
template<typename H, typename... Tail>
struct skip_n_types<0, H, Tail...>
{
using type = type_sequence<H, Tail...>;
};
} // namespace detail
template<int N, typename... T>
using skip_n_types = typename detail::skip_n_types<N, T...>::type;
namespace detail
{
template<typename T>
struct get_default_args;
template<template<typename...> class T, typename... A>
struct get_default_args<T<A...> >
{
using type = typename skip_n_types<
tmpl::template_traits<T>::args_min,
A...>::type;
};
} // namespace detail
template<typename T>
using get_default_args = typename detail::get_default_args<T>::type;
To put it all together:
template<typename>
struct dependant { };
template<typename T, typename U = void>
struct example { };
template<typename T, typename U = dependant<T> >
struct dependant_example { };
template<typename T>
void print_type(T)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main(int argc, char** argv)
{
{
using example_type = example<int>;
using default_args = get_default_args<example_type>;
print_type(example_type{});
print_type(default_args{});
}
{
using example_type = dependant_example<int>;
using default_args = get_default_args<example_type>;
print_type(example_type{});
print_type(default_args{});
}
}
Output:
void print_type(T) [T = example<int, void>]
void print_type(T) [T = type_sequence<void>]
void print_type(T) [T = dependant_example<int, dependant<int> >]
void print_type(T) [T = type_sequence<dependant<int> >]

Related

Resolving to different types based on the argument types of a c++ template function

I'm doing some metaprogramming and I have ran into the following problem:
I have a class that takes one template parameter T, T can be assumed to be a function with an arbitary signature. The class a member variable V, that should have the type std::tuple<> if T takes no arguments or the first argument is not a std::tuple. If the first argument is an std::tuple, V should instead have the same type as first argument.
Example:
void f() // Should resolve to std::tuple<>
void f(int) // Should resolve to std::tuple<>
void f(std::tuple<int, float>) // Should resolve to std::tuple<int, float>
void f(std::tuple<float>, int) // Should resolve to std::tuple<float>
I have been trying something similar to this, but with no success. As it fails when indexing the first arguement on the argument free function, without selecting any of the other alternatives in spite of those being available. I'm using MSVC 2019 16.8.4
#include <functional>
#include <concepts>
namespace detail
{
template<typename... ArgTs>
struct HasArgs : public std::conditional<(sizeof... (ArgTs) > 0), std::true_type, std::false_type>::type {};
}
//!
//! Provides argument function information
//! Based on: https://stackoverflow.com/a/9065203
//!
template<typename T>
class FunctionTraits;
template<typename R, typename... Args>
class FunctionTraits<R(Args...)>
{
public:
static const size_t arg_count = sizeof...(Args);
using HasArguments = detail::HasArgs<Args...>;
using ReturnType = R;
using ArgTypes = std::tuple<Args...>;
template <size_t i>
struct arg
{
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
namespace detail
{
template <typename T>
struct is_tuple : std::false_type {};
template <typename... Args>
struct is_tuple<std::tuple<Args...>>: std::true_type {};
}
template <typename T>
concept is_tuple = requires() { detail::is_tuple<T>::value; };
class TestMemberFunctions
{
public:
static int test_f1(std::tuple<int, float>, int)
{
return 0;
}
static int test_f2(int)
{
return 0;
}
static int test_f3()
{
return 0;
}
};
template <typename CreateT> requires (!FunctionTraits<CreateT>::HasArguments::value)
std::tuple<> TypeDeductionDummyFunction();
template <typename CreateT> requires FunctionTraits<CreateT>::HasArguments::value
auto TypeDeductionDummyFunction() -> std::conditional<is_tuple<typename FunctionTraits<CreateT>::template arg<0>::type>,
typename FunctionTraits<CreateT>::template arg<0>::type,
std::tuple<>>;
template <typename T>
class SampleClass
{
decltype(TypeDeductionDummyFunction<T>()) m_member;
};
SampleClass<decltype(TestMemberFunctions::test_f1)> c1;
SampleClass<decltype(TestMemberFunctions::test_f2)> c2;
SampleClass<decltype(TestMemberFunctions::test_f3)> c3;
Something along these lines, perhaps:
template <typename T> struct ExtractFirstTuple;
template <typename R>
struct ExtractFirstTuple<R()> {
using type = std::tuple<>;
};
template <typename R, typename... Ts, typename... Args>
struct ExtractFirstTuple<R(std::tuple<Ts...>, Args...)> {
using type = std::tuple<Ts...>;
};
template <typename R, typename First, typename... Args>
struct ExtractFirstTuple<R(First, Args...)> {
using type = std::tuple<>;
};
Demo
An attempt to build what you want from more primitive operations.
template<typename T, std::size_t N>
struct FunctionArgument {
static constexpr bool exists = false;
};
template<typename R, typename A0, typename... Args>
struct FunctionArgument<R(A0, Args...), 0>{
using type=A0;
static constexpr bool exists = true;
};
template<typename R, typename A0, typename... Args, std::size_t N>
struct FunctionArgument<R(A0, Args...), N>:
FunctionArgument<R(Args...), N-1>
{};
template<class Sig, std::size_t N>
using FuncArg_type = typename FunctionArgument<Sig, N>::type;
template<class Sig, std::size_t N>
constexpr bool FuncArg_exists = FunctionArgument<Sig, N>::exists;
template<class Sig, class Otherwise>
using FirstArgIfExists =
typename std::conditional_t<
FuncArg_exists<Sig,0>,
FunctionArgument<Sig, 0>,
std::type_identity<Otherwise>
>::type;
template<class T, class Otherwise>
struct TypeIfTuple {
using type=Otherwise;
};
template<class...Ts, class Otherwise>
struct TypeIfTuple<std::tuple<Ts...>, Otherwise> {
using type=std::tuple<Ts...>;
};
template<class T, class Otherwise>
using TypeIfTuple_t = typename TypeIfTuple<T,Otherwise>::type;
template<class Sig>
using TheTypeYouWant = TypeIfTuple_t<
FirstArgIfExists<Sig, std::tuple<>>,
std::tuple<>
>;

Properly align in-memory template, invariant of order of parameters

Take a look at this template.
template < typename T1, typename T2, typename T3 >
struct Alignement {
T1 first;
T2 second;
T3 third;
};
int main() {
Alignement<char, int, double> a1;
Alignement<char, double, int> a2;
assert( sizeof(a1) < sizeof(a2) );
return 0;
}
Obviously assertion holds. Sub-optimal ordering leads to 50% more memory usage in this case.
My question is, what are the ways of combating it and properly ordering types in template structure, other than kindly asking user to take care of it himself (which would leave him with the same problem if he didn't know sizes of his types beforehand)?
My idea is to generate optimal ordering dynamically at compile time with macros or TMP, but I have no proper knowledge of these techniques. Or perhaps an army of partially specialized templates would get the job done?
The key aspect is preserving the AlignedObject.first syntax for client.
For my specific case, I'm looking for solution for exactly 3 parameters (3! possible orderings) but general solution (including variadic-length-templates) would be interesting to see.
For my specific case, I'm looking for solution for exactly 3 parameters (3! possible orderings) but general solution (including variadic-length-templates) would be interesting to see.
I propose a general solution: a variadic type sorter and a variadic Alignement that use it.
Following the Peter's suggestion, the idea is to sort the type putting biggers types first.
I use C++17 because the new template folding permit I use C++11 because the OP must use a C++11 only compliant compiler to make extremely simple a type traits that say if the first type of a list is the bigger one (according sizeof()). I maintain, commented, the original C++17 version
// iftb = is first type bigger ?
// original C++17 version
//
// template <typename T0, typename ... Ts>
// struct iftb
// : public std::integral_constant<bool,((sizeof(Ts) <= sizeof(T0)) && ...)>
// { };
template <typename ...>
struct iftb;
template <typename T0>
struct iftb<T0> : public std::true_type
{ };
template <typename T0, typename T1, typename ... Ts>
struct iftb<T0, T1, Ts...>
: public std::integral_constant<bool,
(sizeof(T1) <= sizeof(T0)) && iftb<T0, Ts...>::value>
{ };
Now a type traits to know if a type container contain a list of ordered types
// ifctb = is first contained type bigger ?
template <typename>
struct ifctb;
template <template <typename ...> class C, typename ... Tc>
struct ifctb<C<Tc...>> : public iftb<Tc...>
{ };
Now the type orderer is simple to write (but not particularly efficient; sorry)
// to = type orderer
template <typename, typename Cd, bool = ifctb<Cd>::value>
struct to;
template <template <typename...> class C, typename ... To,
typename T0, typename ... Tu>
struct to<C<To...>, C<T0, Tu...>, true> : public to<C<To..., T0>, C<Tu...>>
{ };
template <template <typename...> class C, typename ... To,
typename T0, typename ... Tu>
struct to<C<To...>, C<T0, Tu...>, false> : public to<C<To...>, C<Tu..., T0>>
{ };
template <template <typename...> class C, typename ... To, typename T>
struct to<C<To...>, C<T>, true>
{ using type = C<To..., T>; };
Now I propose an indexed wrapper that must be defined through partial specialization to define first, second and third (etc., if you want extend the solution)
template <std::size_t, typename>
struct wrapper;
template <typename T>
struct wrapper<0U, T>
{ T first; };
template <typename T>
struct wrapper<1U, T>
{ T second; };
template <typename T>
struct wrapper<2U, T>
{ T third; };
We need std::index_sequence and std::make_index_sequence that are available only starting from C++14; but the OP must compile this code in a C++11 only compliant compiler so I propose a simple emulation C++11 compliant
// std::index_sequence and std::make_index_sequence simplified emulators
template <std::size_t...>
struct indexSequence
{ using type = indexSequence; };
template <typename, typename>
struct concatSequences;
template <std::size_t... S1, std::size_t... S2>
struct concatSequences<indexSequence<S1...>, indexSequence<S2...>>
: public indexSequence<S1..., ( sizeof...(S1) + S2 )...>
{ };
template <std::size_t N>
struct makeIndexSequenceH
: public concatSequences<
typename makeIndexSequenceH<(N>>1)>::type,
typename makeIndexSequenceH<N-(N>>1)>::type>::type
{ };
template<>
struct makeIndexSequenceH<0> : public indexSequence<>
{ };
template<>
struct makeIndexSequenceH<1> : public indexSequence<0>
{ };
template <std::size_t N>
using makeIndexSequence = typename makeIndexSequenceH<N>::type;
With the help of std::tuple, std::index_sequence and std::make_index_sequence indexSequence and makeIndexSequence (C++11 compliant simplified emulations of std::index_sequence and std::make_index_sequence), I add a couples of helper structs for Alignement
template <typename>
struct AlH2;
template <typename ... Ts>
struct AlH2<std::tuple<Ts...>> : public Ts...
{ };
template <typename...>
struct AlH1;
template <std::size_t ... Is, typename ... Ts>
struct AlH1<indexSequence<Is...>, Ts...>
: public AlH2<typename to<std::tuple<>,
std::tuple<wrapper<Is, Ts>...>>::type>
{ };
Now Alignement can be written as
template <typename ... Ts>
struct Alignement
: public AlH1<makeIndexSequence<sizeof...(Ts)>, Ts...>
{ };
The following is a full (I remember: C++17) C++11 compiling example with some assert()'s to verify the correct ordering.
#include <tuple>
#include <cassert>
#include <iostream>
#include <type_traits>
// std::index_sequence and std::make_index_sequence simplified emulators
template <std::size_t...>
struct indexSequence
{ using type = indexSequence; };
template <typename, typename>
struct concatSequences;
template <std::size_t... S1, std::size_t... S2>
struct concatSequences<indexSequence<S1...>, indexSequence<S2...>>
: public indexSequence<S1..., ( sizeof...(S1) + S2 )...>
{ };
template <std::size_t N>
struct makeIndexSequenceH
: public concatSequences<
typename makeIndexSequenceH<(N>>1)>::type,
typename makeIndexSequenceH<N-(N>>1)>::type>::type
{ };
template<>
struct makeIndexSequenceH<0> : public indexSequence<>
{ };
template<>
struct makeIndexSequenceH<1> : public indexSequence<0>
{ };
template <std::size_t N>
using makeIndexSequence = typename makeIndexSequenceH<N>::type;
// iftb = is first type bigger ?
// original C++17 version
//
// template <typename T0, typename ... Ts>
// struct iftb
// : public std::integral_constant<bool,((sizeof(Ts) <= sizeof(T0)) && ...)>
// { };
template <typename ...>
struct iftb;
template <typename T0>
struct iftb<T0> : public std::true_type
{ };
template <typename T0, typename T1, typename ... Ts>
struct iftb<T0, T1, Ts...>
: public std::integral_constant<bool,
(sizeof(T1) <= sizeof(T0)) && iftb<T0, Ts...>::value>
{ };
// ifctb = is first contained type bigger ?
template <typename>
struct ifctb;
template <template <typename ...> class C, typename ... Tc>
struct ifctb<C<Tc...>>
: public iftb<Tc...>
{ };
// to = type orderer
template <typename, typename Cd, bool = ifctb<Cd>::value>
struct to;
template <template <typename...> class C, typename ... To,
typename T0, typename ... Tu>
struct to<C<To...>, C<T0, Tu...>, true> : public to<C<To..., T0>, C<Tu...>>
{ };
template <template <typename...> class C, typename ... To,
typename T0, typename ... Tu>
struct to<C<To...>, C<T0, Tu...>, false> : public to<C<To...>, C<Tu..., T0>>
{ };
template <template <typename...> class C, typename ... To, typename T>
struct to<C<To...>, C<T>, true>
{ using type = C<To..., T>; };
template <std::size_t, typename>
struct wrapper;
template <typename T>
struct wrapper<0U, T>
{ T first; };
template <typename T>
struct wrapper<1U, T>
{ T second; };
template <typename T>
struct wrapper<2U, T>
{ T third; };
template <typename>
struct AlH2;
template <typename ... Ts>
struct AlH2<std::tuple<Ts...>> : public Ts...
{ };
template <typename...>
struct AlH1;
template <std::size_t ... Is, typename ... Ts>
struct AlH1<indexSequence<Is...>, Ts...>
: public AlH2<typename to<std::tuple<>,
std::tuple<wrapper<Is, Ts>...>>::type>
{ };
template <typename ... Ts>
struct Alignement
: public AlH1<makeIndexSequence<sizeof...(Ts)>, Ts...>
{ };
int main ()
{
Alignement<char, int, long long> a0;
a0.first = '0';
a0.second = 1;
a0.third = 2LL;
assert( (std::size_t)&a0.third < (std::size_t)&a0.first );
assert( (std::size_t)&a0.third < (std::size_t)&a0.second );
assert( (std::size_t)&a0.second < (std::size_t)&a0.first );
}
-- EDIT --
The OP ask
using your solution, if I want to achieve N-argument template class, I need to define N wrapper classes, each containing single field name for n-th argument. Different Alignement<>'s should have different field names == set of N wrappers for each of them. Any good idea for a macro (or template...) to achieve that?
For me, C-style macros are distilled evil (and I don't know they very well), but...
What I propose isn't a full solution; only a draft.
If you define the following set of macros
#define WrpNum(wName, num, fName) \
template <typename T>\
struct wrapper_ ## wName <num, T> \
{ T fName; };
#define Foo_1(wName, tot, fName) \
WrpNum(wName, tot-1U, fName)
#define Foo_2(wName, tot, fName, ...) \
WrpNum(wName, tot-2U, fName) \
Foo_1(wName, tot, __VA_ARGS__)
#define Foo_3(wName, tot, fName, ...) \
WrpNum(wName, tot-3U, fName) \
Foo_2(wName, tot, __VA_ARGS__)
// Foo_4(), Foo_5(), ...
#define Foo(wName, num, ...) \
template <std::size_t, typename> \
struct wrapper_ ## wName; \
Foo_ ## num(wName, num, __VA_ARGS__)
you can define a template indexed struct wrapper_wrp1 with specializations and a first member in wrapper_wrp1<0U, T> specialization, a second member in wrapper_wrp1<1U, T>, etc, calling
Foo(wrp1, 3, first, second, third)
Observe that you need the total number of specializations as second parameter.
Maybe is possible to make better (with recursive variadic macro?) but, frankly, I'm not interested too much in macros.
Given this call
Foo(wrp1, 3, first, second, third)
you could (caution: not tested) modify AlH1 the specific wrapper struct (wrapper_wrp1)
template <std::size_t ... Is, typename ... Ts>
struct AlH1<std::index_sequence<Is...>, Ts...>
: public AlH2<typename to<std::tuple<>,
std::tuple<wrapper_wrp1<Is, Ts>...>>::type>
{ };
#include <iostream>
#include <type_traits>
template <typename... O>
struct SizeOrder;
template <typename T1, typename T2, typename... Rest>
struct SizeOrder<T1, T2, Rest...> {
using type = typename std::conditional<(T1::size::value > T2::size::value || (T1::size::value == T2::size::value && T1::order::value < T2::order::value)), typename SizeOrder<T2, Rest...>::type, int>::type;
};
template <typename T1, typename T2>
struct SizeOrder<T1, T2> {
using type = typename std::conditional<(T1::size::value > T2::size::value || (T1::size::value == T2::size::value && T1::order::value < T2::order::value)), void, int>::type;
};
template <typename... T>
using Order = typename SizeOrder<T...>::type;
template <typename T1, int T2>
struct DeclarationOrder {
using size = typename std::alignment_of<T1>;
using order = typename std::integral_constant<int, T2>;
};
template <typename A, typename B, typename C, typename = void>
struct Alignement;
#define AO DeclarationOrder<A,1>
#define BO DeclarationOrder<B,2>
#define CO DeclarationOrder<C,3>
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<AO, BO, CO>> {
A first;
B second;
C third;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<AO, CO, BO>> {
A first;
C third;
B second;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<BO, AO, CO>> {
B second;
A first;
C third;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<BO, CO, AO>> {
B second;
C third;
A first;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<CO, AO, BO>> {
C third;
A first;
B second;
};
template <typename A, typename B, typename C>
struct Alignement<A, B, C, Order<CO, BO, AO>> {
C third;
B second;
A first;
};
int main() {
Alignement<char, int, double> t1;
std::cout << sizeof(t1) << std::endl << sizeof(t1.first) << std::endl << sizeof(t1.second) << std::endl << std::endl;
Alignement<char, double, int> t2;
std::cout << sizeof(t2) << std::endl << sizeof(t2.first) << std::endl << sizeof(t2.second) << std::endl << std::endl;
return 0;
}
EDIT: Added a Order<> template to recursively check the size-order of any amount of parameters and extended Aligenment to hold 3 variables.
EDIT2: Template deduction failed when using types with same size, so I changed the SizeOrder template to take a DeclarationOrder template to remove the ambiguity from having 2 possible orders for something like Alignement<int, int, double>
With some testing on godbolt to figure out the macro part we can condense the whole thing to this.
#include <iostream>
#include <type_traits>
template <typename... O>
struct SizeOrder;
template <typename T1, typename T2, typename... Rest>
struct SizeOrder<T1, T2, Rest...> {
using type = typename std::conditional<(T1::size::value > T2::size::value || (T1::size::value == T2::size::value && T1::order::value < T2::order::value)), typename SizeOrder<T2, Rest...>::type, int>::type;
};
template <typename T1, typename T2>
struct SizeOrder<T1, T2> {
using type = typename std::conditional<(T1::size::value > T2::size::value || (T1::size::value == T2::size::value && T1::order::value < T2::order::value)), void, int>::type;
};
template <typename... T>
using Order = typename SizeOrder<T...>::type;
template <typename T1, int T2>
struct DeclarationOrder {
using size = typename std::alignment_of<T1>;
using order = typename std::integral_constant<int, T2>;
};
template <typename A, typename B, typename C, typename = void>
struct Alignement;
#define AO DeclarationOrder<A,1>
#define BO DeclarationOrder<B,2>
#define CO DeclarationOrder<C,3>
#define Aname first
#define Bname second
#define Cname third
#define MAKE_SPECIALIZATION(FIRST, SECOND, THIRD) \
template <typename A, typename B, typename C> \
struct Alignement<A, B, C, Order<FIRST ## O, SECOND ## O, THIRD ## O>> { \
FIRST FIRST ## name; \
SECOND SECOND ## name; \
THIRD THIRD ## name; \
};
MAKE_SPECIALIZATION(A,B,C)
MAKE_SPECIALIZATION(A,C,B)
MAKE_SPECIALIZATION(B,A,C)
MAKE_SPECIALIZATION(B,C,A)
MAKE_SPECIALIZATION(C,A,B)
MAKE_SPECIALIZATION(C,B,A)
int main() {
Alignement<char, int, double> t1;
std::cout << sizeof(t1) << std::endl << sizeof(t1.first) << std::endl << sizeof(t1.second) << std::endl << std::endl;
Alignement<char, double, int> t2;
std::cout << sizeof(t2) << std::endl << sizeof(t2.first) << std::endl << sizeof(t2.second) << std::endl << std::endl;
return 0;
}
To extend it to 4, 5 or 6 variables we need to update struct Alignement to add template D before template = void. Then we #define DO DeclarationOrder<D,4> and #define Dname fourth.
Then we add a D and FOURTH to the MAKE_SPECIALIZATION macro and define all (16?) possible layouts.
Far from squeaky clean, but doable.
For the three-member case (or any other fixed number) you can use a sorting network to efficiently reduce the number of specializations (at best, log^2n swaps AFAIR); in C++11, something like (not tested):
template <typename T,std::size_t> struct MemberSpec: std::alignment_of<T> {};
struct NoMember{};
template<typename, typename = NoMember> struct MemberDecl{};
template<typename T, typename B> struct MemberDecl<MemberSpec<T,0>,B>: B { T first; };
template<typename T, typename B> struct MemberDecl<MemberSpec<T,1>,B>: B { T second; };
template<typename T, typename B> struct MemberDecl<MemberSpec<T,2>,B>: B { T third; };
template<typename M0,typename M1,typename M2>
struct Alignement_01: std::conditional_t<( M0::value < M1::value ),
MemberDecl<M0,MemberDecl<M1,MemberDecl<M2>>>, MemberDecl<M1,MemberDecl<M0,MemberDecl<M2>>> >{};
template<typename M0,typename M1,typename M2>
struct Alignement_02: std::conditional_t<( M0::value < M2::value ),
Alignement_01<M0,M1,M2>, Alignement_01<M2,M1,M0> >{};
template<typename M0,typename M1,typename M2>
struct Alignement_12: std::conditional_t<( M1::value < M2::value ),
Alignement_02<M0,M1,M2>, Alignement_02<M0,M2,M1> >{};
template<typename T0,typename T1,typename T2>
struct Alignement: Alignement_12<MemberSpec<T0,0>,MemberSpec<T1,1>,MemberSpec<T2,2>> {};
in the above, the resulting Alignment<T0,T1,T2> is standard layout since C++14 (and an aggregate since C++17), whenever the Tj are. This means that you'll need to place asserts to check proper field ordering and total size in pre-C++14.
EDIT: I forgot that, even in >=C++14, at most a base class can have non static data members; so, my Alignment<> is almost never standard layout; anyway, any decent compiler should place the fields the expected way, or better if it fits so. This may be acceptable considering that your goal is to help the compiler producing a more optimized layout.
The general case is solved similarly by implementing a sorting algorithm, or generalizing the above to work over some sorting-network abstraction; anyway, you'll still need to specialize something like MemberDecl to get your data member naming right (first,second,third,fourth,... whatever).

find typename of typename in template parameter

I would like the following code to compile when foo gets anything derived from base, otherwise a compile error ensues. I have written the type-trait class is_Base because the std::is_base_of does not work well with my template stuff. I am close. I got it to work using a static_passoff thing, but I would like to not have to use it. So how can write the enable_if without the static_passoff hack? Here is the running version: http://coliru.stacked-crooked.com/a/6de5171b6d3e12ff
#include <iostream>
#include <memory>
using namespace std;
template < typename D >
class Base
{
public:
typedef D EType;
};
template<class T>
struct is_Base
{
using base_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template<class U>
static constexpr std::true_type test(Base<U> *) { return std::true_type(); }
static constexpr std::false_type test(...) { return std::false_type(); }
using value = decltype( test((T*)0) );
};
template < typename A >
using static_passoff = std::integral_constant< bool, A::value >;
template <typename T, typename = typename std::enable_if< static_passoff< typename is_Base< T >::value >::value >::type >
void foo(T const&)
{
}
class Derived : public Base<Derived> {};
class NotDerived {};
int main()
{
Derived d;
//NotDerived nd;
foo(d);
//foo(nd); // <-- Should cause compile error
return 0;
}
I'm not entirely sure I understand your question given that your code does work. But stylistically, for metafunctions that yield a type, that type should be named type. So you should have:
using type = decltype( test((T*)0) );
^^^^
Or, to avoid the zero-pointer-cast-hack:
using type = decltype(test(std::declval<T*>()));
Also, your test doesn't need a definition. Just the declaration. We're not actually calling it, just checking its return type. It doesn't have to be constexpr either, so this suffices:
template<class U>
static std::true_type test(Base<U> *);
static std::false_type test(...);
Once you have that, you can alias it:
template <typename T>
using is_Base_t = typename is_Base<T>::type;
And use the alias:
template <typename T,
typename = std::enable_if_t< is_Base_t<T>::value>>
void foo(T const&)
{
}
After stumbling blindly into the answer in the comments, I found out I can just use is_Base<T>::type::value without any typename keywords. When trying to remove the static_passoff before, I kept putting in typename. I have always been mixed up with that one. Anyway, here is the final code with a few teaks from Barry's answer:
#include <iostream>
#include <memory>
using namespace std;
template < typename D >
class Base
{
public:
typedef D EType;
};
template<class T>
struct is_Base
{
using base_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template<class U>
static constexpr std::true_type test(Base<U> *) { return std::true_type(); }
static constexpr std::false_type test(...) { return std::false_type(); }
using type = decltype(test(std::declval<T*>()));
};
template <typename T, typename = typename std::enable_if< is_Base< T >::type::value >::type >
void foo(T const&)
{
}
class Derived : public Base<Derived> {};
class NotDerived {};
int main()
{
Derived d;
//NotDerived nd;
foo(d);
//foo(nd); // <-- Should cause compile error
return 0;
}

Technique to distinguish between ordinary template parameters and template template parameters using SFINAE resolution

The question inspired by recently arised question about extended std::is_base_of type trait.
Is there any technique, which allows us to distinguish between ordinary template parameter and template template parameter in modern C++ or its extensions (say, -std=gnu++1z clang++/g++)?
namespace details
{
template< /* ??? */ base >
struct is_derived_from;
template< typaneme base >
struct is_derived_from< base >
{
static std::true_type test(base *);
static std::false_type test(void *);
};
template< template< typename ...formal > base >
struct is_derived_from< /* ??? */ >
{
template< typename ...actual > // actual parameters must be here!
static std::true_type test(base< actual... > *);
static std::false_type test(void *);
};
} // namespace details
template< typename derived, /* ??? */ base >
using is_derived_from = decltype(details::is_derived_from< /* ? base< ? > */ >::test(std::declval< typename std::remove_cv< derived >::type * >()));
In positive case it allows us to make some of useful type traits much more powerfull (for example, STL's std::is_base_of).
I think it requires a language feature as a "generalized typenames", isn't it?
There can be only one set of template parameters for class templates, but you can use overloading constexpr function templates instead that dispatches to the appropriate class template. Take the is_derived_from trait in the linked question, with an extra SFINAE parameter so that you don't get a hard error when B is an inaccessible or ambiguous base:
#include <type_traits>
namespace detail
{
template <template <class...> class B, typename Derived>
struct is_derived_from
{
using U = typename std::remove_cv<Derived>::type;
template <typename... Args,
typename = std::enable_if_t<
std::is_convertible<U*, Base<Args...>*>::value>>
static auto test(B<Args...>*)
-> typename std::integral_constant<bool
, !std::is_same<U, B<Args...>>::value>;
static std::false_type test(void*);
using type = decltype(test(std::declval<U*>()));
};
using std::is_base_of; // may want to use is_convertible instead to match
// the semantics of is_derived_from
}
template <template <class...> class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_derived_from<B, Derived>::type::value; }
template <class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_base_of<B,Derived>::value; }
struct B {};
struct D : B {};
template<class ...>
struct B2 {};
struct D2 : B2<int, double> { };
int main() {
static_assert(my_is_base_of<B2, D2>(), "Oops");
static_assert(my_is_base_of<B, D>(), "Oops");
static_assert(my_is_base_of<B2<int, double>, D2>(), "Oops");
static_assert(!my_is_base_of<B, D2>(), "Oops");
}
Demo.
You asked:
Is there any technique, which allows us to distinct between ordinary template parameter and template template parameter in modern C++ or its extensions (say, -std=gnu++1z clang++/g++)?
Seems to me like you need something like:
template <typename T>
struct is_template_template : public std::false_type
{
};
template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};
Example Program
#include <iostream>
template <typename T>
struct is_template_template : public std::false_type
{
};
template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};
template <typename T> struct A {};
struct B {};
int main()
{
std::cout << std::boolalpha;
std::cout << is_template_template<A<int>>::value << std::endl;
std::cout << is_template_template<B>::value << std::endl;
return 0;
}
Output:
true
false

How to create the Cartesian product of a type list?

I'd like to create the cross product of a list of types using variadic templates.
Here's what I have so far:
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
template<typename...> struct type_list {};
template<typename T1, typename T2> struct type_pair {};
template<typename T, typename... Rest>
struct row
{
typedef type_list<type_pair<T,Rest>...> type;
};
template<typename... T>
struct cross_product
{
typedef type_list<typename row<T,T...>::type...> type;
};
int main()
{
int s;
typedef cross_product<int, float, short>::type result;
std::cout << abi::__cxa_demangle(typeid(result).name(), 0, 0, &s) << std::endl;
return 0;
}
This program outputs:
$ g++ -std=c++0x cross_product.cpp ; ./a.out
type_list<type_list<type_pair<int, int>, type_pair<int, float>, type_pair<int, short> >, type_list<type_pair<float, int>, type_pair<float, float>, type_pair<float, short> >, type_list<type_pair<short, int>, type_pair<short, float>, type_pair<short, short> > >
But I'd like it to output:
type_list<type_pair<int,int>, type_pair<int,float>, type_pair<int,short>, type_pair<float,int>,...>
That is, without the nested type_lists.
Is there a direct way to do this without the row helper, or should the solution "unwrap" the nested type_lists somehow?
A nice clean version I think:
cross_product.cpp:
#include "type_printer.hpp"
#include <iostream>
template<typename ...Ts> struct type_list {};
template<typename T1, typename T2> struct pair {};
// Concatenation
template <typename ... T> struct concat;
template <typename ... Ts, typename ... Us>
struct concat<type_list<Ts...>, type_list<Us...>>
{
typedef type_list<Ts..., Us...> type;
};
// Cross Product
template <typename T, typename U> struct cross_product;
// Partially specialise the empty case for the first type_list.
template <typename ...Us>
struct cross_product<type_list<>, type_list<Us...>> {
typedef type_list<> type;
};
// The general case for two type_lists. Process:
// 1. Expand out the head of the first type_list with the full second type_list.
// 2. Recurse the tail of the first type_list.
// 3. Concatenate the two type_lists.
template <typename T, typename ...Ts, typename ...Us>
struct cross_product<type_list<T, Ts...>, type_list<Us...>> {
typedef typename concat<
type_list<pair<T, Us>...>,
typename cross_product<type_list<Ts...>, type_list<Us...>>::type
>::type type;
};
struct A {};
struct B {};
struct C {};
struct D {};
struct E {};
struct F {};
template <typename T, typename U>
void test()
{
std::cout << print_type<T>() << " \u2a2f " << print_type<U>() << " = "
<< print_type<typename cross_product<T, U>::type>() << std::endl;
}
int main()
{
std::cout << "Cartesian product of type lists\n";
test<type_list<>, type_list<>>();
test<type_list<>, type_list<A>>();
test<type_list<>, type_list<A, B>>();
test<type_list<A, B>, type_list<>>();
test<type_list<A>, type_list<B>>();
test<type_list<A>, type_list<B, C, D>>();
test<type_list<A, B>, type_list<B, C, D>>();
test<type_list<A, B, C>, type_list<D>>();
test<type_list<A, B, C>, type_list<D, E, F>>();
return 0;
}
type_printer.hpp:
#ifndef TYPE_PRINTER_HPP
#define TYPE_PRINTER_HPP
#include "detail/type_printer_detail.hpp"
template <typename T>
std::string print_type()
{
return detail::type_printer<T>()();
}
#endif
detail/type_printer_detail.hpp:
#ifndef DETAIL__TYPE_PRINTER_DETAIL_HPP
#define DETAIL__TYPE_PRINTER_DETAIL_HPP
#include <typeinfo>
#include <cxxabi.h>
#include <string>
template <typename ...Ts> struct type_list;
template <typename T1, typename T2> struct pair;
namespace detail {
// print scalar types
template <typename T>
struct type_printer {
std::string operator()() const {
int s;
return abi::__cxa_demangle(typeid(T).name(), 0, 0, &s);
}
};
// print pair<T, U> types
template <typename T, typename U>
struct type_printer<pair<T, U>> {
std::string operator()() const {
return "(" + type_printer<T>()() + "," + type_printer<U>()() + ")";
}
};
// print type_list<T>
template <>
struct type_printer<type_list<>> {
std::string operator()() const {
return "\u2205";
}
};
template <typename T>
struct type_printer<type_list<T>> {
std::string operator()() const {
return "{" + type_printer<T>()() + "}";
}
std::string operator()(const std::string& sep) const {
return sep + type_printer<T>()();
}
};
template <typename T, typename ...Ts>
struct type_printer<type_list<T, Ts...>> {
std::string operator()() const {
return "{" + type_printer<T>()() + type_printer<type_list<Ts...>>()(std::string(", ")) + "}";
}
std::string operator()(const std::string& sep) const {
return sep + type_printer<T>()() + type_printer<type_list<Ts...>>()(sep);
}
};
}
#endif
Run:
g++ -std=c++0x cross_product.cpp && ./a.out
Output:
Cartesian product of type lists
∅ ⨯ ∅ = ∅
∅ ⨯ {A} = ∅
∅ ⨯ {A, B} = ∅
{A, B} ⨯ ∅ = ∅
{A} ⨯ {B} = {(A,B)}
{A} ⨯ {B, C, D} = {(A,B), (A,C), (A,D)}
{A, B} ⨯ {B, C, D} = {(A,B), (A,C), (A,D), (B,B), (B,C), (B,D)}
{A, B, C} ⨯ {D} = {(A,D), (B,D), (C,D)}
{A, B, C} ⨯ {D, E, F} = {(A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F)}
(I noticed on Windows using Chrome that the cross product unicode character is not coming out well. Sorry, I don't know how to fix that.)
Somehow my brain is fried: I think I'm using more code than is needed but, at least, it has the desired results (although I didn't fix the memory leak):
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
template<typename...> struct type_list {};
template<typename T1, typename T2> struct type_pair {};
template<typename T, typename... Rest>
struct row
{
typedef type_list<type_pair<T,Rest>...> type;
};
template <typename... T> struct concat;
template <typename... S, typename... T>
struct concat<type_list<S...>, type_list<T...>>
{
typedef type_list<S..., T...> type;
};
template <typename... T>
struct expand
{
typedef type_list<T...> type;
};
template <> struct expand<> { typedef type_list<> type; };
template <typename... T, typename... L>
struct expand<type_list<T...>, L...>
{
typedef typename concat<typename expand<T...>::type, typename expand<L...>::type>::type type;
};
template<typename... T>
struct cross_product
{
typedef typename expand<type_list<typename row<T,T...>::type...>>::type type;
};
int main()
{
int s;
typedef cross_product<int, float, short>::type result;
std::cout << abi::__cxa_demangle(typeid(result).name(), 0, 0, &s) << std::endl;
return 0;
}
Maybe something like this:
template <typename ...Args> struct typelist { };
template <typename S, typename T> struct typelist_cat;
template <typename ...Ss, typename ...Ts>
struct typelist_cat<typelist<Ss...>, typelist<Ts...>>
{
typedef typelist<Ss..., Ts...> type;
};
template <typename S, typename T> struct product;
template <typename S, typename ...Ss, typename ...Ts>
struct product<typelist<S, Ss...>, typelist<Ts...>>
{
// the cartesian product of {S} and {Ts...}
// is a list of pairs -- here: a typelist of 2-element typelists
typedef typelist<typelist<S, Ts>...> S_cross_Ts;
// the cartesian product of {Ss...} and {Ts...} (computed recursively)
typedef typename product<typelist<Ss...>, typelist<Ts...>>::type
Ss_cross_Ts;
// concatenate both products
typedef typename typelist_cat<S_cross_Ts, Ss_cross_Ts>::type type;
};
// end the recursion
template <typename ...Ts>
struct product<typelist<>, typelist<Ts...>>
{
typedef typelist<> type;
};
Now you should be able to use product<typelist<A,B,C>, typelist<D,E,F>>::type.
C++17
Working Demo
Logic to concatenate type_lists to avoid nested type_list like you are asking for:
// base case: 2 type_lists
template<class... Ts, class... Us>
auto concat(type_list<Ts...>, type_list<Us...>) -> type_list<Ts..., Us...>;
// recursive case: more than 2 type_lists
template<class... Ts, class... Us, class... Rest>
auto concat(type_list<Ts...>, type_list<Us...>, Rest...) -> decltype(concat(type_list<Ts..., Us...>{}, Rest{}...));
Note that these functions don't have (or need) implementations; this is a trick to avoid class template specialization (I learned it from Hana Dusikova's compile time regular expressions)
Then, simplifying your row and cross_product impls as pairs and cross_product_impl, respectively:
template<class T, class... Ts>
using pairs = type_list<type_pair<T, Ts>...>;
template<class... T>
auto cross_product_impl()
{
if constexpr(sizeof...(T) == 0)
return type_list<> {};
if constexpr(sizeof...(T) == 1)
return type_list<type_pair<T, T>...>{};
if constexpr(sizeof...(T) > 1)
return concat(pairs<T, T...>{}...);
}
if constexpr allows us to more easily express the logic, I think.
Finally a type alias for cross_product that gives us what the type would be if we theoretically invoked cross_product_impl:
template<class... T>
using cross_product = decltype(cross_product_impl<T...>());
Usage basically the same as before:
cross_product<int, float, short> result;
So far all solutions have drawbacks, unnecessary dependencies, unnecessary helpers and all are restricted to the Cartesian power of two. The following solution has no such drawbacks and supports:
Any cartesian power including 0.
Returning the empty set if any of the factors is an empty set.
The code is self contained and does not depend on any include files.
The inputs of the function can be of any template type.
The type of the output list can be specified via the first template
parameter.
It was actually to harder to implement (but good as homework) then I thought. I am actually thinking about creating a little generator which allows me an extended template syntax which makes these things really easy.
Simplified the code works as follows: product converts an input list tuple<A...>,tuple<B...>,tuple<C...> into tuple<tuple<A>...>, tuple<B...>, tuple<C...>. This second list is then passed to product_helper which does the recursive Cartesian product computation.
template <typename... T> struct cat2;
template <template<typename...> class R, typename... As, typename... Bs>
struct cat2 <R<As...>, R<Bs...> > {
using type = R <As..., Bs...>;
};
template <typename... Ts> struct product_helper;
template <template<typename...> class R, typename... Ts>
struct product_helper < R<Ts...> > { // stop condition
using type = R< Ts...>;
};
template <template<typename...> class R, typename... Ts>
struct product_helper < R<R<> >, Ts... > { // catches first empty tuple
using type = R<>;
};
template <template<typename...> class R, typename... Ts, typename... Rests>
struct product_helper < R<Ts...>, R<>, Rests... > { // catches any empty tuple except first
using type = R<>;
};
template <template<typename...> class R, typename... X, typename H, typename... Rests>
struct product_helper < R<X...>, R<H>, Rests... > {
using type1 = R <typename cat2<X,R<H> >::type...>;
using type = typename product_helper<type1, Rests...>::type;
};
template <template<typename...> class R, typename... X, template<typename...> class Head, typename T, typename... Ts, typename... Rests>
struct product_helper < R<X...>, Head<T, Ts...>, Rests... > {
using type1 = R <typename cat2<X,R<T> >::type...>;
using type2 = typename product_helper<R<X...> , R<Ts...> >::type;
using type3 = typename cat2<type1,type2>::type;
using type = typename product_helper<type3, Rests...>::type;
};
template <template<typename...> class R, typename... Ts> struct product;
template <template<typename...> class R>
struct product < R > { // no input, R specifies the return type
using type = R<>;
};
template <template<typename...> class R, template<typename...> class Head, typename... Ts, typename... Tail>
struct product <R, Head<Ts...>, Tail... > { // R is the return type, Head<A...> is the first input list
using type = typename product_helper< R<R<Ts>...>, Tail... >::type;
};
Here is a compilable example of how the code can be used.
Here's another solution.
#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
template <typename ...Args> struct typelist { };
template <typename, typename> struct typepair { };
template <typename S, typename T> struct product;
template <typename S, typename T> struct append;
template<typename ...Ss, typename ...Ts>
struct append<typelist<Ss...>, typelist<Ts...>> {
typedef typelist<Ss..., Ts...> type;
};
template<>
struct product<typelist<>, typelist<>> {
typedef typelist<> type;
};
template<typename ...Ts>
struct product<typelist<>, typelist<Ts...>> {
typedef typelist<> type;
};
template<typename ...Ts>
struct product<typelist<Ts...>, typelist<>> {
typedef typelist<> type;
};
template<typename S, typename T, typename ...Ss, typename ...Ts>
struct product<typelist<S, Ss...>, typelist<T, Ts...>> {
typedef typename
append<typelist<typepair<S, T>,
typepair<S, Ts>...,
typepair<Ss, T>...>,
typename product<typelist<Ss...>, typelist<Ts...>>::type>::type type;
};
int main(void)
{
int s;
std::cout << abi::__cxa_demangle(
typeid(product<typelist<int, float>, typelist<short, double>>::type).name(), 0, 0, &s) << "\n";
return 0;
}
Note: This is NOT what the OP asked for... but may be of relevance to others (like me) who stumble upon this question. Here is how it can be done using a Loki::TypeList (i.e. prior C++-11), perhaps of historical interest or for compatability sake.
Also, perhaps it is presumptuous of me to pollute loki's namespace. YMMV.
crossproduct.h
#include "loki/NullType.h"
#include "loki/Typelist.h"
namespace Loki {
namespace TL {
/// a pair of two types
template <typename A_t, typename B_t>
struct TypePair
{
typedef A_t A;
typedef B_t B;
};
/// a template which takes one type and pairs it with all other types
/// in another typelist
template <class T, class TList > struct DistributePair;
/// specialization of Distribute for the nulltype
template < class TList >
struct DistributePair< NullType, TList >
{
typedef NullType type;
};
/// specialization of Distribute where the second parameter is nulltype
template <class T >
struct DistributePair< T, NullType >
{
typedef NullType type;
};
/// specialization of Distribute where the first parameter is a
/// typelist
template <class T, class Head, class Tail >
struct DistributePair< T, Typelist<Head,Tail> >
{
typedef Typelist<
TypePair<T,Head>,
typename DistributePair<T,Tail>::type
> type;
};
/// performs cartesion product of two typelists
template <class TListA, class TListB> struct CrossProduct;
/// specialization of CrossProduct for NullType
template <class TListB>
struct CrossProduct< NullType, TListB >
{
typedef NullType type;
};
/// specialization of CrossProduct for recursion
template <class Head, class Tail, class TListB>
struct CrossProduct< Typelist<Head,Tail>, TListB >
{
typedef typename Append<
typename DistributePair< Head,TListB >::type,
typename CrossProduct< Tail, TListB >::type
>::Result type;
};
} // namespace TL
} // namespace Loki
test.cpp
#include <crossproduct.h>
#include <loki/HierarchyGenerators.h>
#include <iostream>
struct A{};
struct B{};
struct C{};
struct D{};
struct E{};
struct F{};
typedef LOKI_TYPELIST_3(A,B,C) TypeListA_t;
typedef LOKI_TYPELIST_3(D,E,F) TypeListB_t;
typedef typename Loki::TL::CrossProduct< TypeListA_t, TypeListB_t >::type Cross_t;
template <typename T> const char* toString();
template <> const char* toString<A>(){ return "A"; };
template <> const char* toString<B>(){ return "B"; };
template <> const char* toString<C>(){ return "C"; };
template <> const char* toString<D>(){ return "D"; };
template <> const char* toString<E>(){ return "E"; };
template <> const char* toString<F>(){ return "F"; };
template <typename T> struct Printer
{
Printer()
{
std::cout << toString<T>() << ", ";
}
};
template <typename T1, typename T2>
struct Printer< Loki::TL::TypePair<T1,T2> >
{
Printer()
{
std::cout << "(" << toString<T1>() << "," << toString<T2>() << "), ";
}
};
typedef Loki::GenScatterHierarchy< TypeListA_t, Printer > PrinterA_t;
typedef Loki::GenScatterHierarchy< TypeListB_t, Printer > PrinterB_t;
typedef Loki::GenScatterHierarchy< Cross_t, Printer > PrinterCross_t;
int main(int argc, char** argv)
{
std::cout << "\nType list A: \n ";
PrinterA_t a;
std::cout << "\nType list B: \n ";
PrinterB_t b;
std::cout << "\nType list Cross: \n ";
PrinterCross_t cross;
return 0;
}
output
Type list A:
A, B, C,
Type list B:
D, E, F,
Type list Cross:
(A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F),
With Boost.Mp11, this is a short one-liner (as always):
using input = type_list<int, float, short>;
using result = mp_product<
type_pair,
input, input>;
Demo.
We can generalize this to picking N things, with repetition, from that input. We can't use type_pair anymore to group our elements, so we'll just have a list of type_list of elements. To do that, we basically need to write:
mp_product<type_list, input, input, ..., input>
// ~~~~~~~ N times ~~~~~~~~
Which is also the same as:
mp_product_q<mp_quote<type_list>, input, input, ..., input>
// ~~~~~~~ N times ~~~~~~~~
One way to do that is:
template <int N>
using product = mp_apply<
mp_product_q,
mp_append<
mp_list<mp_quote<type_list>>,
mp_repeat_c<mp_list<input>, N>
>>;
Demo.
Really enjoyed this "homework" assignment :)
Both solutions below create a class full of type_list typedefs, along with member functions that will check to see if a given list of types exist in the class as a type_list.
The first solution creates all possible combinations of types from 1 to N types per type_list (the width parameter defines N). The second solution creates only pairs of types.
First Solution
template<typename... Ts> struct type_list { typedef type_list<Ts...> type; };
template<size_t, typename...> struct xprod_tlist_ {};
template<typename... Ts, typename... Us>
struct xprod_tlist_<1, type_list<Ts...>, Us...> {};
template<size_t width, typename... Ts, typename... Us>
struct xprod_tlist_<width, type_list<Ts...>, Us...>
: type_list<Ts..., Us>...
, xprod_tlist_<width - 1, type_list<Ts..., Us>, Us...>... {};
template<size_t width, typename... Ts> struct xprod_tlist
: type_list<Ts>..., xprod_tlist_<width, type_list<Ts>, Ts...>... {
template<typename... Us> struct exists
: std::is_base_of<type_list<Us...>, xprod_tlist<width, Ts...>> {};
template<typename... Us> struct assert_exists {
static_assert(exists<Us...>::value, "Type not present in list");
};
};
Usage:
typedef xprod_tlist<5, int, char, string, float, double, long> X;
//these pass
X::assert_exists<int, int, int, int, int> assert_test1;
X::assert_exists<double, float, char, int, string> assert_test2;
//these fail
X::assert_exists<char, char, char, char, char, char> assert_test3;
X::assert_exists<int, bool> assert_test4;
//true
auto test1 = X::exists<int, int, int, int, int>::value;
auto test2 = X::exists<double, float, char, int, string>::value;
//false
auto test3 = X::exists<char, char, char, char, char, char>::value;
auto test4 = X::exists<int, bool>::value;
Second Solution
template<class T, class U> struct type_pair { typedef type_pair<T, U> type; };
template<class... Ts> struct type_list {};
template<class...> struct xprod_tlist_ {};
template<class T, class... Ts, class... Us>
struct xprod_tlist_<type_list<T, Ts...>, type_list<Us...>>
: type_pair<T, Us>..., xprod_tlist_<type_list<Ts...>, type_list<Us...>> {};
template<class... Ts>
struct xprod_tlist : xprod_tlist_<type_list<Ts...>, type_list<Ts...>> {
template<class T, class U> struct exists
: std::is_base_of<type_pair<T, U>, xprod_tlist<Ts...>> {};
template<class T, class U> struct assert_exists {
static_assert(exists<T, U>::value, "Type not present in list");
};
};
Usage:
typedef xprod_tlist<int, float, string> X;
//these pass
X::assert_exists<int, int> assert_test1;
X::assert_exists<int, float> assert_test2;
X::assert_exists<int, string> assert_test3;
X::assert_exists<float, int> assert_test4;
X::assert_exists<float, float> assert_test5;
X::assert_exists<float, string> assert_test6;
X::assert_exists<string, int> assert_test7;
X::assert_exists<string, float> assert_test8;
X::assert_exists<string, string> assert_test9;
//this fails
X::assert_exists<int, char> assert_test10;
//true
auto test1 = X::exists<int, int>::value;
auto test2 = X::exists<int, float>::value;
auto test3 = X::exists<int, string>::value;
auto test4 = X::exists<float, int>::value;
auto test5 = X::exists<float, float>::value;
auto test6 = X::exists<float, string>::value;
auto test7 = X::exists<string, int>::value;
auto test8 = X::exists<string, float>::value;
auto test9 = X::exists<string, string>::value;
//false
auto test10 = X::exists<int, char>::value;