I'd like to implement something like same_type() function in C++11 like below (this is what I have tried so far but it can not cope with the use cases mentioned below). The function is to check if T is of the same type of the n-th argument of Args, currently n=0 should be enough for my requirement, although n=other meaningful value would be something better to have (not very important if not straightforward).
template<typename T, typename... Args>
struct MyClass
{
// check if T is of the same type of the n-th argument of Args
bool same_type() {
// currently, I only check the first argument
// but if possible, it would be more useful to extend
// this function to check the n-th argument
return std::is_same<T,typename std::tuple_element<0, std::tuple<Args...> >::type>;
}
};
I have already had a look at this answer, but it does not consider the following use cases.
Use cases:
1.use with references and const qualifier:
MyClass<int,const int&> c;
// expect to see 1, type of int should match const int&, i.e. reference or const should NOT affect the result
std::cout<<c.same_type();
2.use with no argument supplied:
MyClass<int> c;
// expect to see 0 as there is no variadic argument provided to compare with int
std::cout<<c.same_type();
I propose to develop a type traits isSameNth as follows
template <std::size_t, typename...>
struct isSameNth;
template <std::size_t N, typename T, typename A0, typename ... As>
struct isSameNth<N, T, A0, As...> : public isSameNth<N-1U, T, As...>
{ };
template <std::size_t N, typename T>
struct isSameNth<N, T> : public std::false_type
{ };
template <typename T, typename A0, typename ... As>
struct isSameNth<0U, T, A0, As...> : public std::is_same<
typename std::remove_reference<T>::type const,
typename std::remove_reference<A0>::type const>
{ };
to transform same_type() in a template static method (where the template value is N)
template <typename T, typename... Args>
struct MyClass
{
template <std::size_t N>
static constexpr bool same_type()
{ return isSameNth<N, T, Args...>::value; }
};
The following is a full example (C++11 compliant)
#include <type_traits>
template <std::size_t, typename...>
struct isSameNth;
template <std::size_t N, typename T, typename A0, typename ... As>
struct isSameNth<N, T, A0, As...> : public isSameNth<N-1U, T, As...>
{ };
template <std::size_t N, typename T>
struct isSameNth<N, T> : public std::false_type
{ };
template <typename T, typename A0, typename ... As>
struct isSameNth<0U, T, A0, As...> : public std::is_same<
typename std::remove_reference<T>::type const,
typename std::remove_reference<A0>::type const>
{ };
template <typename T, typename... Args>
struct MyClass
{
template <std::size_t N>
static constexpr bool same_type()
{ return isSameNth<N, T, Args...>::value; }
};
int main ()
{
static_assert(
false == MyClass<int, long, int, short>::template same_type<0U>(), "!");
static_assert(
true == MyClass<int, long, int, short>::template same_type<1U>(), "!");
static_assert(
false == MyClass<int, long, int, short>::template same_type<2U>(), "!");
static_assert(
true == MyClass<int const, int &>::template same_type<0U>(), "!");
static_assert(
false == MyClass<int const &>::template same_type<0U>(), "!");
}
I think you're looking to check compatibility more than 'sameness'. here's one way:
#include <tuple>
#include <type_traits>
#include <iostream>
#include <string>
template<class...Ts>
struct nth_is_compatible
{
using tuple = std::tuple<Ts...>;
template<class T, std::size_t N> static constexpr bool check()
{
return std::is_convertible<decltype(std::get<N>(std::declval<tuple>())), T>::value;
}
};
struct Monkey
{
Monkey(std::string) {} // conversion constructor
};
int main()
{
using checklist = nth_is_compatible<const int&, float, std::string>;
constexpr auto list = checklist();
std::cout << list.check<int, 0>() << std::endl;
std::cout << list.check<int, 1>() << std::endl;
std::cout << list.check<int, 2>() << std::endl;
// prove it's a constexpr and that it works for conversions
constexpr auto monkeyable = checklist::check<Monkey, 2>();
std::cout << monkeyable << std::endl;
}
This seems to be a very simple question: How does one remove the first (the n-th) type in a std::tuple?
Example:
typedef std::tuple<int, short, double> tuple1;
typedef std::tuple<short, double> tuple2;
The operation described above would transform tuple1 into tuple2. Is it possible?
You can use a simple type function based on partial specialization of a class template:
#include <type_traits>
#include <tuple>
using namespace std;
template<typename T>
struct remove_first_type
{
};
template<typename T, typename... Ts>
struct remove_first_type<tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};
int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_first_type<my_tuple>::type my_tuple_wo_first_type;
static_assert(
is_same<my_tuple_wo_first_type, tuple<bool, double>>::value,
"Error!"
);
}
Also, this solution can be easily generalized to remove the i-th type of a tuple:
#include <type_traits>
#include <tuple>
using namespace std;
template<size_t I, typename T>
struct remove_ith_type
{
};
template<typename T, typename... Ts>
struct remove_ith_type<0, tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};
template<size_t I, typename T, typename... Ts>
struct remove_ith_type<I, tuple<T, Ts...>>
{
typedef decltype(
tuple_cat(
declval<tuple<T>>(),
declval<typename remove_ith_type<I - 1, tuple<Ts...>>::type>()
)
) type;
};
int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_ith_type<1, my_tuple>::type my_tuple_wo_2nd_type;
static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!"
);
}
I wrote a proposal which was accepted into the C++14 standard making it quite easy to do for any "tuple-like" type, i.e. one that supports the tuple_size and tuple_element API:
template<typename T, typename Seq>
struct tuple_cdr_impl;
template<typename T, std::size_t I0, std::size_t... I>
struct tuple_cdr_impl<T, std::index_sequence<I0, I...>>
{
using type = std::tuple<typename std::tuple_element<I, T>::type...>;
};
template<typename T>
struct tuple_cdr
: tuple_cdr_impl<T, std::make_index_sequence<std::tuple_size<T>::value>>
{ };
And you can transform a tuple object into the new type with only a couple of functions:
template<typename T, std::size_t I0, std::size_t... I>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr_impl(T&& t, std::index_sequence<I0, I...>)
{
return std::make_tuple(std::get<I>(t)...);
}
template<typename T>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr(T&& t)
{
return cdr_impl(std::forward<T>(t),
std::make_index_sequence<std::tuple_size<T>::value>{});
}
This creates an integer sequence [0,1,2,...,N) where N is tuple_size<T>::value, then creates a new tuple with make_tuple(get<I>(t)...) for I in [1,2,...,N)
Testing it:
using tuple1 = std::tuple<int, short, double>;
using tuple2 = std::tuple<short, double>;
using transformed = decltype(cdr(std::declval<tuple1>()));
static_assert(std::is_same<transformed, tuple2>::value, "");
static_assert(std::is_same<tuple_cdr<tuple1>::type, tuple2>::value, "");
#include <iostream>
int main()
{
auto t = cdr(std::make_tuple(nullptr, "hello", "world"));
std::cout << std::get<0>(t) << ", " << std::get<1>(t) << '\n';
}
My reference implementation for the proposal is at https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h
I came up with a solution very similar to that proposed by #Andy, but that tries to be a bit more generic by working directly on the parameter pack (using a dummy wrapper) rather than on std::tuple. This way, the operation can be applied to other variadic templates as well, not only to tuples:
#include <type_traits>
#include <tuple>
template <typename... Args> struct pack {};
template <template <typename...> class T, typename Pack>
struct unpack;
template <template <typename...> class T, typename... Args>
struct unpack<T, pack<Args...>>
{
typedef T<Args...> type;
};
template <typename T, typename Pack>
struct prepend;
template <typename T, typename... Args>
struct prepend<T, pack<Args...>>
{
typedef pack<T, Args...> type;
};
template <std::size_t N, typename... Args>
struct remove_nth_type;
template <std::size_t N, typename T, typename... Ts>
struct remove_nth_type<N, T, Ts...>
: prepend<T, typename remove_nth_type<N-1, Ts...>::type>
{};
template <typename T, typename... Ts>
struct remove_nth_type<0, T, Ts...>
{
typedef pack<Ts...> type;
};
template <typename T, int N>
struct remove_nth;
template <template <typename...> class T, int N, typename... Args>
struct remove_nth<T<Args...>, N>
{
typedef typename
unpack<
T, typename
remove_nth_type<N, Args...>::type
>::type type;
};
template <typename... Args>
struct my_variadic_template
{
};
int main()
{
typedef std::tuple<int, bool, double> my_tuple;
typedef remove_nth<my_tuple, 1>::type my_tuple_wo_2nd_type;
static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!"
);
typedef my_variadic_template<int, double> vt;
typedef remove_nth<vt, 0>::type vt_wo_1st_type;
static_assert(
is_same<vt_wo_1st_type, my_variadic_template<double>>::value,
"Error!"
);
}
pack is an helper structure whose sole purpose is to store a template parameter pack. unpack can then be used to unpack the parameters into an arbitrary class template (thanks to #BenVoigt for this trick). prepend simply prepends a type to a pack.
remove_nth_type uses partial template specialization to remove the nth type from a parameter pack, storing the result into a pack. Finally, remove_nth takes a specialization of an arbitrary class template, remove the nth type from its template parameters, and return the new specialization.
Beside that crazy TMP stuff, there is a very easy way using the C++17 STL function std::apply:
#include <string>
#include <tuple>
template <class T, class... Args>
auto tail(const std::tuple<T, Args...>& t)
{
return std::apply(
[](const T&, const Args&... args)
{
return std::make_tuple(args...);
}, t);
}
template <class T>
using tail_t = decltype(tail(T{}));
int main()
{
std::tuple<int, double, std::string> t{1, 2., "3"};
auto _2_3 = tail(t);
using tuple_t = tail_t<std::tuple<int, double, std::string>>;
static_assert(std::is_same_v<std::tuple<double, std::string>, tuple_t>);
}
DEMO.
This is an over engineered bit of template metaprogramming for this task. It includes the ability to do arbitrary reorders/duplications/removals on the types of a tuple via a filter template:
#include <utility>
#include <type_traits>
template<typename... Ts> struct pack {};
template<std::size_t index, typename Pack, typename=void> struct nth_type;
template<typename T0, typename... Ts>
struct nth_type<0, pack<T0, Ts...>, void> { typedef T0 type; };
template<std::size_t index, typename T0, typename... Ts>
struct nth_type<index, pack<T0, Ts...>, typename std::enable_if<(index>0)>::type>:
nth_type<index-1, pack<Ts...>>
{};
template<std::size_t... s> struct seq {};
template<std::size_t n, std::size_t... s>
struct make_seq:make_seq<n-1, n-1, s...> {};
template<std::size_t... s>
struct make_seq<0,s...> {
typedef seq<s...> type;
};
template<typename T, typename Pack> struct conc_pack { typedef pack<T> type; };
template<typename T, typename... Ts> struct conc_pack<T, pack<Ts...>> { typedef pack<T, Ts...> type; };
template<std::size_t n, typename Seq> struct append;
template<std::size_t n, std::size_t... s>
struct append<n, seq<s...>> {
typedef seq<n, s...> type;
};
template<typename S0, typename S1> struct conc;
template<std::size_t... s0, std::size_t... s1>
struct conc<seq<s0...>, seq<s1...>>
{
typedef seq<s0..., s1...> type;
};
template<typename T, typename=void> struct value_exists:std::false_type {};
template<typename T> struct value_exists<T,
typename std::enable_if< std::is_same<decltype(T::value),decltype(T::value)>::value >::type
>:std::true_type {};
template<typename T, typename=void> struct result_exists:std::false_type {};
template<typename T> struct result_exists<T,
typename std::enable_if< std::is_same<typename T::result,typename T::result>::value >::type
>:std::true_type {};
template<template<std::size_t>class filter, typename Seq, typename=void>
struct filter_seq { typedef seq<> type; };
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<value_exists<filter<s0>>::value>::type>
: append< filter<s0>::value, typename filter_seq<filter, seq<s...>>::type >
{};
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && result_exists<filter<s0>>::value>::type>
: conc< typename filter<s0>::result, typename filter_seq<filter, seq<s...>>::type >
{};
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && !result_exists<filter<s0>>::value>::type>
: filter_seq<filter, seq<s...>>
{};
template<typename Seq, typename Pack>
struct remap_pack {
typedef pack<> type;
};
template<std::size_t s0, std::size_t... s, typename Pack>
struct remap_pack< seq<s0, s...>, Pack >
{
typedef typename conc_pack< typename nth_type<s0, Pack>::type, typename remap_pack< seq<s...>, Pack >::type >::type type;
};
template<typename Pack>
struct get_indexes { typedef seq<> type; };
template<typename... Ts>
struct get_indexes<pack<Ts...>> {
typedef typename make_seq< sizeof...(Ts) >::type type;
};
template<std::size_t n>
struct filter_zero_out { enum{ value = n }; };
template<>
struct filter_zero_out<0> {};
template<std::size_t n>
struct filter_zero_out_b { typedef seq<n> result; };
template<>
struct filter_zero_out_b<0> { typedef seq<> result; };
#include <iostream>
int main() {
typedef pack< int, double, char > pack1;
typedef pack< double, char > pack2;
typedef filter_seq< filter_zero_out, typename get_indexes<pack1>::type >::type reindex;
typedef filter_seq< filter_zero_out_b, typename get_indexes<pack1>::type >::type reindex_b;
typedef typename remap_pack< reindex, pack1 >::type pack2_clone;
typedef typename remap_pack< reindex_b, pack1 >::type pack2_clone_b;
std::cout << std::is_same< pack2, pack2_clone >::value << "\n";
std::cout << std::is_same< pack2, pack2_clone_b >::value << "\n";
}
Here we have a type pack that holds an arbitrary list of types. See #LucTouraille 's neat answer for how to move between tuple and pack.
seq holds a sequence of indexes. remap_pack takes a seq and a pack, and builds a resulting pack by grabbing the nth element of the original pack.
filter_seq takes a template<size_t> functor and a seq, and uses the functor to filter the elements of the seq. The functor can return either a ::value of type size_t or a ::result of type seq<...> or neither, allowing one-to-one or one-to-many functors.
A few other helper functions, like conc, append, conc_pack, get_indexes, make_seq, nth_type round things out.
I tested it with filter_zero_out which is a ::value based filter that removes 0, and filter_zero_out_b which is a ::result based filter that also removes 0.
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;
Currently, I have:
template <unsigned I,
unsigned N,
typename Tuple,
typename UnaryFunction>
struct for_;
template <unsigned N, typename Tuple, typename UnaryFunction>
struct for_<N, N, Tuple, UnaryFunction> {
static
void call(const Tuple&, UnaryFunction) {}
};
template <unsigned I,
unsigned N,
typename Tuple,
typename UnaryFunction>
struct for_ {
static
void call(Tuple&& x, UnaryFunction f) {
f(get<I>(x));
for_<I + 1, N, Tuple, UnaryFunction>::call(std::forward<Tuple>(x), f);
}
};
template <typename Tuple, typename UnaryFunction>
inline
void for_each(Tuple&& x, UnaryFunction f) {
for_<0,
tuple_size<
typename std::remove_const<
typename std::remove_reference<Tuple>::type
>::type
>::value,
Tuple,
UnaryFunction>::call(std::forward<Tuple>(x), f);
}
Is it possible to generalize this, probably by variadic templates, to take any number of tuple arguments?
EDIT:
Here is how I would use what I am unable to define:
if (i != e) {
std::array<Tuple, 2> x;
std::get<0>(x) = *i;
std::get<1>(x) = *i;
++i;
std::for_each (i, e, [&x](const Tuple& y) {
for_each(std::get<0>(x), y, assign_if(std::less));
for_each(std::get<1>(x), y, assign_if(std::greater));
});
}
EDIT: changed to use rvalue references and std::forward
I'm not sure is it what you expected, but I'll post it - maybe someone will find it helpful.
namespace std {
template<int I, class Tuple, typename F> struct for_each_impl {
static void for_each(const Tuple& t, F f) {
for_each_impl<I - 1, Tuple, F>::for_each(t, f);
f(get<I>(t));
}
};
template<class Tuple, typename F> struct for_each_impl<0, Tuple, F> {
static void for_each(const Tuple& t, F f) {
f(get<0>(t));
}
};
template<class Tuple, typename F>
F for_each(const Tuple& t, F f) {
for_each_impl<tuple_size<Tuple>::value - 1, Tuple, F>::for_each(t, f);
return f;
}
}
Functor:
struct call_tuple_item {
template<typename T>
void operator()(T a) {
std::cout << "call_tuple_item: " << a << std::endl;
}
};
Main function:
std::tuple<float, const char*> t1(3.14, "helloworld");
std::for_each(t1, call_tuple_item());
You can check my answer here for a hint on expanding tuples
How do I expand a tuple into variadic template function's arguments?
See below for the map(UnaryFunction, Tuple&&...) implementation I will be using, as well as the code I had been messing with in an attempt to get it working completely as I wanted (for_aux, last, etc.).
#include <array>
#include <iostream>
#include <tuple>
namespace detail {
struct static_ {
private:
static_() = delete;
static_(const static_&) = delete;
static_& operator=(const static_&) = delete;
};
template <unsigned... Args>
struct max;
template <unsigned Head, unsigned... Tail>
struct max<Head, Tail...>: private static_ {
static const unsigned value = Head > max<Tail...>::value
? Head
: max<Tail...>::value;
};
template <>
struct max<>: private static_ {
static const unsigned value = 0;
};
template <unsigned... Args>
struct min;
template <unsigned Head, unsigned... Tail>
struct min<Head, Tail...>: private static_ {
static const unsigned value = Head < min<Tail...>::value
? Head
: min<Tail...>::value;
};
template <>
struct min<>: private static_ {
static const unsigned value = 0;
};
template <typename... Args>
struct for_aux;
template <typename A, typename B>
struct for_aux<A, B>: private static_ {
static
void call(A&& a, B b) {
b(std::forward(a));
}
};
template <typename A, typename B, typename C>
struct for_aux<A, B, C>: private static_ {
static
void call(A&& a, B&& b, C c) {
c(std::forward(a), std::forward(b));
}
};
template <typename A, typename B, typename C, typename D>
struct for_aux<A, B, C, D>: private static_ {
static
void call(A&& a, B&& b, C&& c, D d) {
d(std::forward(a), std::forward(b), std::forward(c));
}
};
// template <typename Head, typename... Tail>
// struct for_aux: private static_ {
// static
// void call(Tail&&... x, Head f) {
// f(std::forward(x)...);
// }
// };
template <typename... Args>
struct last;
template <typename X>
struct last<X>: private static_ {
typedef X type;
};
template <typename Head, typename... Tail>
struct last<Head, Tail...>: private static_ {
typedef typename last<Tail...>::type type;
};
template <unsigned I,
unsigned N,
typename UnaryFunction,
typename... Tuples>
struct map;
template <unsigned N, typename UnaryFunction, typename... Tuples>
struct map<N, N, UnaryFunction, Tuples...>: private static_ {
static
void call(UnaryFunction, const Tuples&...) {}
};
template <unsigned I,
unsigned N,
typename UnaryFunction,
typename... Tuples>
struct map: private static_ {
static
void call(UnaryFunction f, Tuples&&... x) {
f(std::get<I>(std::forward<Tuples>(x))...);
map<I + 1,
N,
UnaryFunction,
Tuples...>::call(f, std::forward<Tuples>(x)...);
}
};
template <typename Tuple>
struct tuple_size: private static_ {
enum {
value = std::tuple_size<
typename std::remove_const<
typename std::remove_reference<Tuple>::type
>::type
>::value
};
};
}
template <typename UnaryFunction, typename... Tuples>
inline
void map(UnaryFunction f, Tuples&&... x) {
detail::map<0,
detail::max<
detail::tuple_size<Tuples>::value...
>::value,
UnaryFunction,
Tuples...
>::call(f, std::forward<Tuples>(x)...);
}
using namespace std;
struct f {
template <typename T, typename U>
void operator()(const T& i, const U& j) {
cout << i << " " << j << endl;
}
};
int main() {
const array<int, 2> x = {{2}};
const tuple<double, char> y(1.1, 'a');
map(f(), x, y);
}