Getting templated template function declaration correct - c++

I've got a problem like this I am trying to make compile:
#include <vector>
#include <array>
struct Test
{
template <template <typename...> class Container, typename T, typename... Args>
void SetData(const Container<T, Args...>& data)
{
// compiles for vector but not array
// 'void Test::SetData(const Container<T,Args...> &)': could not deduce template argument for 'const Container<T,Args...> &' from 'std::array<float,3>'
}
};
int main()
{
Test test;
std::vector<int> vector{ 1,2,3 };
std::array<float, 3> arr{1.0f, 2.0f, 3.0f};
test.SetData(vector);
test.SetData(arr);
return 0;
}
Essentially I need a function signature that can accept any arbitary STL container (more specifically std::vector and std::array) and I need the type T visible in the function also (i.e int or float in this case). The other types (allocator or std::array size) I dont care about.
What is the correct signature?

What is the correct signature?
As far I know, there isn't possible to write a single correct signature to intercept all containers and only containers.
But if you accept to write a type traits that say if a type is a container and extract the contained type (require a generic version, a partial specialization for std::vector and similar typename ... containers and a specialization for std::array; other specializations can be added) as the following isCnt
template <typename>
struct isCnt : public std::false_type
{ };
// std::array case
template <template <typename, std::size_t> class C,
typename T, std::size_t N >
struct isCnt<C<T, N>> : public std::true_type
{ using containedType = T; };
// other container case
template <template <typename ...> class C,
typename T0, typename ... Ts>
struct isCnt<C<T0, Ts...>> : public std::true_type
{ using containedType = T0; };
you can construct setData() as follows
template ::containedType>
void SetData (C const & data)
{
// code here
}
Observe that setData() is SFINAE enabled (or disabled) by T = isCnt<C>::containedType, that is available only for containers.
The following is a full working example
#include <array>
#include <vector>
#include <iostream>
#include <type_traits>
template <typename>
struct isCnt : public std::false_type
{ };
// std::array case
template <template <typename, std::size_t> class C,
typename T, std::size_t N >
struct isCnt<C<T, N>> : public std::true_type
{ using containedType = T; };
// other container case
template <template <typename ...> class C,
typename T0, typename ... Ts>
struct isCnt<C<T0, Ts...>> : public std::true_type
{ using containedType = T0; };
struct Test
{
template <typename C, typename T = typename isCnt<C>::containedType>
void SetData (C const & data)
{
if ( std::is_same<T, int>::value )
std::cout << "- int case" << std::endl;
else if ( std::is_same<T, float>::value )
std::cout << "- float case" << std::endl;
}
};
int main ()
{
Test test;
std::vector<int> vector{ 1,2,3 };
std::array<float, 3> arr{ { 1.0f, 2.0f, 3.0f } };
test.SetData(vector); // print "int case"
test.SetData(arr); // print "float case"
//test.SetData(0); // compilation error
}

Related

Check if two types are of the same template

I want to check if two types are of the same template. As an example I want the following snippet of code to return true because both objects are vectors despite the inner elements being of different types.
It's important that the check is made at compile time (that's why the function is constexpr).
#include <iostream>
#include <type_traits>
#include <vector>
template <typename Container1, typename Container2> constexpr bool CheckTypes(Container1 c1, Container2 c2)
{
return std::is_same<Container1,Container2>::value;
}
int main()
{
std::vector<int> v1(100,0);
std::vector<double> v2(100,0);
std::cout << CheckTypes(v1,v2);
}
Here you go:
template <class T, class U>
struct are_same_template : std::is_same<T, U>
{};
template <template<class...> class T, class T1, class T2>
struct are_same_template<T<T1>, T<T2>> : std::true_type
{};
template <class T, class U>
constexpr bool CheckTypes(T, U)
{
return are_same_template<T, U>::value;
}
Demo: http://coliru.stacked-crooked.com/a/8533c694968f4dbb
This works by providing a specialization of are_same_template that discard the template argument types:
template <template<class...> class T, class T1, class T2>
struct are_same_template<T<T1>, T<T2>>
Even if T1 and T2 differ (the template argument types), are_same_template is a true type:
are_same_template<T<T1>, T<T2>> : std::true_type
About template<class...> instead of template<class>: this is to accomodate the fact than std:: containers have implicit template arguments. Thanks to ConstantinosGlynos for making me aware of it.
In case you would like to check if two template template classes are the same without specializing them:
template<template<typename...> class ATT, template<typename...> class BTT>
struct is_same_tt : std::false_type {};
template<template<typename...> class TT>
struct is_same_tt<TT, TT> : std::true_type {};
// example
static_assert(is_same_tt<std::tuple, std::tuple>());
static_assert(!is_same_tt<std::vector, std::list>());
Check this post. They provide a way to check if something is a specialization of a template class:
template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};
And probably do something like this to keep your original interface:
template <typename Container1, typename Container2>
constexpr bool CheckTypes(Container1 c1, Container2 c2) {
return is_specialization<Container1, std::vector>::value && is_specialization<Container2, std::vector>::value;
}
So you can do something like this:
int main() {
std::vector<int> v1(100,0);
std::vector<double> v2(100,0);
std::cout << CheckTypes(v1,v2);
return 0;
}
You can but it take a little of metaprog :
#include <iostream>
#include <type_traits>
#include <vector>
#include <set>
template <typename Container1, typename Container2>
struct CheckTypes_impl
{
constexpr static bool check (Container1 , Container2 ) { return false; }
};
template <
template <class...> class Container1, class... args1 , class... args2 >
struct CheckTypes_impl<Container1<args1...>,Container1<args2...>>
{
constexpr static bool check (Container1<args1...> , Container1<args2...> ) { return true; }
};
template <
template <class...> class Container1,
class ... args1,
template <class...> class Container2,
class ... args2
> constexpr bool CheckTypes(Container1<args1...> c1, Container2<args2...> c2)
{
return CheckTypes_impl<Container1<args1...>,Container2<args2...>>::check(c1,c2);
}
int main()
{
std::vector<int> v1(100,0);
std::vector<double> v2(100,0);
std::set<int> s;
std::cout << CheckTypes(v1,v2) << std::endl;
std::cout << CheckTypes(v1,s) << std::endl;
}
run :
https://wandbox.org/permlink/OTuQfl7UBlbxgtCO
Update : You need " template class Container1, class..." because vector don't take 1 template param, but 2. In the general case you don't know how many default parameters will be used

Improve compile-time error messages in templated classes

I have a templated class which is supposed to accepts some kind of containers (std::array and std::vector) of some kind of of objects (in this example string and doubles).
My goal is to provide some clear compilation error if the class is constructed from the wrong combination of objects.
The following code compiles in Visual Studio 17, version 15.9.0.
///Performing checks
namespace checks
{
template <typename T>
struct CorrectType {
enum { value = false };
};
template <>
struct CorrectType<std::string> {
enum { value = true };
};
template <>
struct CorrectType<double> {
enum { value = true };
};
template <typename T>
struct CorrectContainer {
enum { value = false };
};
template <typename T, typename A>
struct CorrectContainer<std::vector<T, A>> {
enum { value = CorrectType<T>::value };
};
template <typename T, std::size_t N>
struct CorrectContainer<std::array<T, N>> {
enum { value = CorrectType<T>::value };
};
template <class Container>
void constexpr check(){
static_assert(checks::CorrectContainer<Container>::value, "Wrong container: only vectors/arrays of doubles/strings are accepted");
}
}
template <typename Container>
class Wrapper
{
public:
explicit Wrapper(const Container &container) : container_(container), size_(container.size())
{
//type checking is performed
checks::check<Container>();
}
void display() const {
for (int i = 0; i < size_; i++)
std::cout << this->container_[i] << " ";
std::cout << std::endl;
}
private:
Container container_;
int size_ = 0;
};
int main()
{
//Ok
Wrapper array_wrapper(std::array<double, 5>{0.0,1.0,2.0,3.0,4.0});
array_wrapper.display();
//Ok
Wrapper string_wrapper(std::array<std::string, 3>{ "a","b","c" });
string_wrapper.display();
//Error - working as intended but not clear what went wrong
Wrapper<std::vector<int>> vector_wrapper({ 1,2,3});
vector_wrapper.display();
}
The code above works as intended, but the error is ambiguous: we are not able to understand if the container is wrong or the kind of object contained is. Moreover, if the templated object has not a size member function, it will fail too early.
As I understand your query, invalid type detection can probably be sketched out as
template<class> struct ValidType;
template<template<class...> class> struct ValidContainer: std::false_type {};
template<> struct ValidContainer<std::vector>: std::true_type {};
template<class> struct ValidType: std::false_type {};
template<class> struct ValidType<double>: std::true_type {};
template<class> struct ValidType<std::string>: std::true_type {};
template<class> struct ValidArgument {
static_assert(false, "Both container and element type are wrong");
};
template<template<class...> class Ctr, class T, class... Ts>
struct ValidArgument<Ctr<T, Ts...>> {
static_assert(ValidContainer<Ctr>::value || ValidType<T>::value
, "Both container and element type are wrong");
static_assert(ValidContainer<Ctr>::value, "Container type is wrong");
static_assert(ValidType<T>::value, "Element type is wrong");
};
template<class T, std::size_t n> struct ValidArgument<std::array<T, n>> {
static_assert(ValidType<T>::value, "Element type is wrong");
};
(Note this is not the real code, merely a demonstration of an idea.)
Arrays are still evil, in the sense that std::array has a non-type parameter, and thus you cannot have a single template that checks the container, the ultimate check is still for the kind 0-type, with container checking in generic case and std::array being treated separately.
Alternatively, ValidType can be slightly more compact:
template<class T> using ValidType = std::bool_constant<
std::is_same_v<T, double> || std::is_same_v<T, std::string>>;
Or
template<class T> using ValidType = std::disjunction<
std::is_same<T, double>, std::is_same<T, std::string>>;
Or a less-standard type matching class. For instance:
template<class T, class... Ts> inline constexpr bool is_one_of_v =
std::disjunction_v<std::is_same<T, Ts>...>;
template<class T> using ValidType =
std::bool_constant<is_one_of_v<T, double, std::string>>;
This way you're less likely to get rogue specializations later.
You might move your check inside class (and split your condition):
template <typename Container>
class Wrapper
{
static_assert(checks::CorrectContainer<Container>::value,
"only std::vector/std::array allowed");
static_assert(checks::CorrectType<typename Container::value_type>::value,
"only double and std::string");
public:
// ...
};
Demo

Template template parameter simple example

First I was learning about template template parameters, and I started wondering if I had a vector<vector<int>>, if I could make a template that extracts out the type int from there.
But, in the process of trying to build an example, I can't even get a single-level template parameter template function to work!
#include <iostream>
#include <vector>
template<
template<class> class C2,
class I
>
void for_2d(const C2<I>& container)
{
for( auto j : container ){
std::cout << j;
}
}
int main() {
std::vector<int> cont;
for_2d(cont);
return 0;
}
This produces:
17 : <source>:17:5: error: no matching function for call to 'for_2d'
for_2d(cont);
^~~~~~
8 : <source>:8:6: note: candidate template ignored: substitution failure : template template argument has different template parameters than its corresponding template template parameter
void for_2d(const C2<I>& container)
^
1 error generated.
Compiler exited with result code 1
The thing you are missing is that vector has multiple template arguments (most of them has default value).
You need to prepare your function for this
template<
template<class...> class C2,
class I
>
void for_2d(const C2<I>& container)
{
for( auto j : container ){
std::cout << j;
}
}
Notice the dots after class
+1 for the Bartosz Przybylski's answer, that explain why your example doesn't compile, but you want
extracts out the type int from there
You use auto j : container, so you're using (at least) C++11; so I suggest you the implementation of a specific, and recursive, type traits.
I propose the following firtType
First of all, the generic (not specialized) version (that is the recursion terminal)
template <typename T>
struct firstType
{ using type = T; };
Next the specialization for std::vector and other containers similar container (that receiving a seguence of types)
template <template <typename...> class C, typename T0, typename ... Ts>
struct firstType<C<T0, Ts...>>
{ using type = typename firstType<T0>::type; };
This specialization works with a lot of containers but not with std::array, that receive a type and a number; the following is a specialization for std::array
template <template <typename, std::size_t> class C, typename T, std::size_t N>
struct firstType<C<T, N>>
{ using type = typename firstType<T>::type; };
Other specializations may be required.
The following is a full working example
#include <array>
#include <vector>
#include <type_traits>
template <typename T>
struct firstType
{ using type = T; };
template <template <typename...> class C, typename T0, typename ... Ts>
struct firstType<C<T0, Ts...>>
{ using type = typename firstType<T0>::type; };
template <template <typename, std::size_t> class C, typename T, std::size_t N>
struct firstType<C<T, N>>
{ using type = typename firstType<T>::type; };
int main ()
{
std::vector<int> vi;
std::array<long, 42U> al;
std::vector<std::vector<short>> vvs;
static_assert( std::is_same<typename firstType<decltype(vi)>::type,
int>::value, "!" );
static_assert( std::is_same<typename firstType<decltype(al)>::type,
long>::value, "!" );
static_assert( std::is_same<typename firstType<decltype(vvs)>::type,
short>::value, "!" );
}

Universal and existential quantification using C++ template magic

Is there a way to implement universal and existential quantification using C++ template magic (maybe using SFINAE etc.)? Something like this:
template
<
template <typename Argument> class Predicate
>
struct UniversalQuantification
{
static const bool value =
/*for any Argument Predicate<Argument>::value == true ? true : false*/;
};
template
<
template <typename Argument> class Predicate
>
struct ExistentialQuantification
{
static const bool value =
/*for some Argument Predicate<Argument>::value == true ? true : false*/;
};
If you pass a finite set of possible template arguments to the template, it is indeed possible to evaluate this at compile-time. However, it is impossible to evaluate this for every argument the passed template has been used with, as these types/args are unknown.
This is the solution for a finite set of arguments for the ExistentialQuantification class. In order to achieve the behaviour of the UniversalQuantification class, you simple have to change the || to a &&:
template<typename Arg>
struct Pred1;
template<>
struct Pred1<float> { static const bool value = true; };
template<>
struct Pred1<double> { static const bool value = false; };
template<>
struct Pred1<long> { static const bool value = true; };
template<template <typename Argument> class Predicate, typename... Types>
struct ExistentialQuantification;
template<template <typename Argument> class Predicate, typename Arg>
struct ExistentialQuantification<Predicate, Arg>
{
static const bool value = Predicate<Arg>::value;
};
template<template <typename Argument> class Predicate, typename Arg, typename... Types>
struct ExistentialQuantification<Predicate, Arg, Types...>
{
static const bool value = Predicate<Arg>::value || ExistentialQuantification<Predicate, Types...>::value;
};
int main()
{
std::cout << ExistentialQuantification<Pred1, long, double, float>::value << std::endl;
}
In this example, value will evaluate to true || false || true and is thus true of course.
Ok, if we're just being smart here, and if we're allowed a couple of constraints, here are two constraints on the programmer, that really solve the problem, smoothly.
if a predicate is needed in the code that is always true, only use the following:
template<typename> struct always : std::true_type { };
if a predicate is needed in the code that is never true, only use the following:
template<typename> struct never : std::false_type { };
Now, the solution is simple:
template<template<typename> class>
struct UniversalQuantification : std::false_type { };
template<>
struct UniversalQuantification<always> : std::true_type { };
template<template<typename> class>
struct ExistentialQuantification : std::true_type { };
template<>
struct ExistentialQuantification<never> : std::false_type { };
Using a library like Boost.MPL, if you can put your universe of allowed types into a type list, e.g. in a boost::mpl::vector, then your quantifiers are just the compile-time version of std::all_of and std::any_of. You can define them as:
#include <ios>
#include <iostream>
#include <type_traits> // is_same, is_base_of
#include <boost/mpl/end.hpp> // end
#include <boost/mpl/find_if.hpp> // find_if
#include <boost/mpl/lambda.hpp> // lambda
#include <boost/mpl/logical.hpp> // not_
#include <boost/mpl/placeholders.hpp> // _1
#include <boost/mpl/vector.hpp> // vector
template<typename Sequence, typename Pred>
struct all_of
:
std::is_same< typename
boost::mpl::find_if<
Sequence,
boost::mpl::not_<Pred>
>::type, typename
boost::mpl::end<Sequence>::type
>
{};
template<typename Sequence, typename Pred>
struct none_of
:
all_of< Sequence, boost::mpl::not_< Pred > >
{};
template<typename Sequence, typename Pred>
struct any_of
:
boost::mpl::not_< none_of< Sequence, Pred > >
{};
struct B {};
struct D : B {};
struct X {};
using Universe = boost::mpl::vector<B, D, X>;
using Predicate = boost::mpl::lambda<std::is_base_of<B, boost::mpl::_1>>;
int main()
{
std::cout << std::boolalpha;
std::cout << all_of<Universe, Predicate>{} << "\n";
std::cout << any_of<Universe, Predicate>{} << "\n";
}
Live Example.
As you can see, because X does not have B as a base class, the universal quantification fails, but because D does have B as a base class, the existential quantification succeeds.
Well, if we're just talking about n-ary (variadic) functions and, or as in the answer by TemplateRex, here's my preferred way without Boost:
using _true = std::integral_constant <bool, true>;
using _false = std::integral_constant <bool, false>;
template <bool C, typename T, typename E>
using _if = typename std::conditional <C, T, E>::type;
template <typename...> struct _and;
template <typename...> struct _or;
template <typename A, typename... B>
struct _and <A, B...> : _if <A{}, _and <B...>, _false> { };
template <typename A, typename... B>
struct _or <A, B...> : _if <A{}, _true, _or <B...> > { };
template <> struct _and <> : _true { };
template <> struct _or <> : _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;