Related
Is it possible to capture a template from a template argument i.e. have a nested template specifier with a template argument that holds the template type?
template< typename T, typename Label = std::string>
class foo {
// ...
};
template <
template < typename C, typename T > typename C<T>,
// ...
typename Label = std::string
>
class bar {
// ...
C< foo< T, Label > > A;
};
For instance, I'd like to pass a generic STL container (std::vector< int >) as template argument, but declare a member of the same meta-type (std::vector) but with different value type (foo< int >) i.e. std::vector< foo< int > >. It may seem convoluted, but it would be helpful to not hardcode the type of STL container.
For context, I'm aiming at a generic container adapter/wrapper (in the line of std::stack or std::queue) that provides some higher-level functionality.
Yes, you can just use template specialization:
#include <string>
template<typename T, typename Label = std::string>
class foo {};
template <class T, typename Label = std::string>
class bar;
template <template<class...> class C, typename T, typename Label>
class bar<C<T>, Label> {
C<foo<T, Label>> A;
};
Demo.
The other answer's approach can be generalized as a reusable template rebinder:
template<typename T>
struct rebinder;
template<template<typename...> typename T, typename... Args>
struct rebinder<T<Args...>> {
template<typename... Us>
using rebind = T<Us...>;
};
template<typename T, typename... Us>
using rebound = rebinder<T>::template rebind<Us...>;
// example:
#include <vector>
template<typename T>
struct MyStruct {
rebound<T, float> vec;
};
int main() {
MyStruct<std::vector<int>> x;
static_assert(std::is_same_v<std::vector<float>, decltype(x.vec)>);
}
see on godbolt
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).
Is it possible to write a type trait whose value is true for all common STL structures (e.g., vector, set, map, ...)?
To get started, I'd like to write a type trait that is true for a vector and false otherwise. I tried this, but it doesn't compile:
template<class T, typename Enable = void>
struct is_vector {
static bool const value = false;
};
template<class T, class U>
struct is_vector<T, typename boost::enable_if<boost::is_same<T, std::vector<U> > >::type> {
static bool const value = true;
};
The error message is template parameters not used in partial specialization: U.
Look, another SFINAE-based solution for detecting STL-like containers:
template<typename T, typename _ = void>
struct is_container : std::false_type {};
template<typename... Ts>
struct is_container_helper {};
template<typename T>
struct is_container<
T,
std::conditional_t<
false,
is_container_helper<
typename T::value_type,
typename T::size_type,
typename T::allocator_type,
typename T::iterator,
typename T::const_iterator,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end()),
decltype(std::declval<T>().cbegin()),
decltype(std::declval<T>().cend())
>,
void
>
> : public std::true_type {};
Of course, you might change methods and types to be checked.
If you want to detect only STL containers (it means std::vector, std::list, etc) you should do something like this.
UPDATE. As #Deduplicator noted, container might not meet AllocatorAwareContainer requirements (e.g.: std::array<T, N>). That is why check on T::allocator_type is not neccessary. But you may check any/all Container requirements in a similar way.
You would say that it should be simpler than that...
template <typename T, typename _ = void>
struct is_vector {
static const bool value = false;
};
template <typename T>
struct is_vector< T,
typename enable_if<
is_same<T,
std::vector< typename T::value_type,
typename T::allocator_type >
>::value
>::type
>
{
static const bool value = true;
};
... But I am not really sure of whether that is simpler or not.
In C++11 you can use type aliases (I think, untested):
template <typename T>
using is_vector = is_same<T, std::vector< typename T::value_type,
typename T::allocator_type > >;
The problem with your approach is that the type U is non-deducible in the context where it is used.
Actually, after some trial and error I found it's quite simple:
template<class T>
struct is_vector<std::vector<T> > {
static bool const value = true;
};
I'd still like to know how to write a more general is_container. Do I have to list all types by hand?
While the other answers here that try to guess whether a class is a container or not might work for you, I would like to present you with the alternative of naming the type you want to return true for. You can use this to build arbitrary is_(something) traits types.
template<class T> struct is_container : public std::false_type {};
template<class T, class Alloc>
struct is_container<std::vector<T, Alloc>> : public std::true_type {};
template<class K, class T, class Comp, class Alloc>
struct is_container<std::map<K, T, Comp, Alloc>> : public std::true_type {};
And so on.
You will need to include <type_traits> and whatever classes you add to your rules.
Why not do something like this for is_container?
template <typename Container>
struct is_container : std::false_type { };
template <typename... Ts> struct is_container<std::list<Ts...> > : std::true_type { };
template <typename... Ts> struct is_container<std::vector<Ts...> > : std::true_type { };
// ...
That way users can add their own containers by partially-specializing. As for is_vector et-al, just use partial specialization as I did above, but limit it to only one container type, not many.
The way I like to detect whether something is a container is to look for data() and size() member functions. Like this:
template <typename T, typename = void>
struct is_container : std::false_type {};
template <typename T>
struct is_container<T
, std::void_t<decltype(std::declval<T>().data())
, decltype(std::declval<T>().size())>> : std::true_type {};
template <typename T>
struct is_container {
template <
typename U,
typename I = typename U::const_iterator
>
static int8_t test(U* u);
template <typename U>
static int16_t test(...);
enum { value = sizeof test <typename std::remove_cv<T>::type> (0) == 1 };
};
template<typename T, size_t N>
struct is_container <std::array<T,N>> : std::true_type { };
Fast forward to 2018 and C++17, I was so daring to improve on #Frank answer
// clang++ prog.cc -Wall -Wextra -std=c++17
#include <iostream>
#include <vector>
namespace dbj {
template<class T>
struct is_vector {
using type = T ;
constexpr static bool value = false;
};
template<class T>
struct is_vector<std::vector<T>> {
using type = std::vector<T> ;
constexpr static bool value = true;
};
// and the two "olbigatory" aliases
template< typename T>
inline constexpr bool is_vector_v = is_vector<T>::value ;
template< typename T>
using is_vector_t = typename is_vector<T>::type ;
} // dbj
int main()
{
using namespace dbj;
std::cout << std::boolalpha;
std::cout << is_vector_v<std::vector<int>> << std::endl ;
std::cout << is_vector_v<int> << std::endl ;
} /* Created 2018 by dbj#dbj.org */
The "proof the pudding". There are better ways to do this, but this works for std::vector.
We can also use concepts. I compiled this with GCC 10.1 flag -std=c++20.
#include<concepts>
template<typename T>
concept is_container = requires (T a)
{
a.begin();
// Uncomment both lines for vectors only
// a.data(); // arrays and vectors
// a.reserve(1); // narrowed down to vectors
};
In our project we still didn't manage to migrate to compiler supporting C++11, so for type_traits of container objects I had to wrote a simple boost style helper:
template<typename Cont> struct is_std_container: boost::false_type {};
template<typename T, typename A>
struct is_std_container<std::vector<T,A> >: boost::true_type {};
template<typename T, typename A>
struct is_std_container<std::list<T,A> >: boost::true_type {};
template<typename T, typename A>
struct is_std_container<std::deque<T,A> >: boost::true_type {};
template<typename K, typename C, typename A>
struct is_std_container<std::set<K,C,A> >: boost::true_type {};
template<typename K, typename T, typename C, typename A>
struct is_std_container<std::map<K,T,C,A> >: boost::true_type {};
template<typename Cont> struct is_std_sequence: boost::false_type {};
template<typename T, typename A>
struct is_std_sequence<std::vector<T,A> >: boost::true_type {};
template<typename T, typename A>
struct is_std_sequence<std::list<T,A> >: boost::true_type {};
template<typename T, typename A>
struct is_std_sequence<std::deque<T,A> >: boost::true_type {};
If you also want to make it work for const std::vector, you can use the following:
namespace local {
template<typename T, typename _ = void>
struct isVector: std::false_type {
};
template<typename T>
struct isVector<T,
typename std::enable_if<
std::is_same<typename std::decay<T>::type, std::vector<typename std::decay<T>::type::value_type, typename std::decay<T>::type::allocator_type> >::value>::type> : std::true_type {
};
}
TEST(TypeTraitTest, testIsVector) {
ASSERT_TRUE(local::isVector<std::vector<int>>::value);
ASSERT_TRUE(local::isVector<const std::vector<int>>::value);
ASSERT_FALSE(local::isVector<std::list<int>>::value);
ASSERT_FALSE(local::isVector<int>::value);
std::vector<uint8_t> output;
std::vector<uint8_t> &output2 = output;
EXPECT_TRUE(core::isVector<decltype(output)>::value);
EXPECT_TRUE(core::isVector<decltype(output2)>::value);
}
Without the std::remove_cv call the second ASSERT_TRUE would fail. But of course this depends on your needs. The thing here is that according to the specs, std::is_same checks for const and volatile to also match.
I'm searching for a way to remove (let's say for now all occurences of) a type from a template parameter pack. The end result would be a struct that looked like this :
template<typename T, typename...Ts>
struct RemoveT
{
using type = /* a new type out of Ts that does not contain T */
}
Let's say that the marginal case RemoveT<int, int> would be handled by returning void (not handled in the code that follows). My initial design looks like this:
// --------------------------------------------------------------
// 1. A "way" of typedefing variadic number of types ------------
template<typename...Ts>
struct pack {
using type = Ts;
};
// --------------------------------------------------------------
// --------------------------------------------------------------
template<typename T, typename...Ts> struct RemoveT;
template<typename T, typename T1, typename...Ts>
struct RemoveT {
using type = typename pack<T1, typename RemoveT<T, Ts...>::type>::type;
};
template<typename T, typename T1>
struct RemoveT<T, T1> {
using type = T1;
};
template<typename T, typename...Ts>
struct RemoveT<T, T, Ts...> {
using type = typename RemoveT<Ts...>::type;
};
// --------------------------------------------------------------
Now I can't even begin to test this code because the pack structure is not valid C++
Reiteration
Just in case this is helpfull for an answer, some other thoughs on solving it
One could argue that pack is not even useful at all. We could instead move around the RemoveT structure, creating a new RemoveT that only contains the types needed. The problem then transforms in extracting the types out of the struct
We could create type pairs that mimic the behaviour of typelists and take a more recursive approach on this.
Bottom Line
For variadic types Ts and a type T: Can I create Us out of Ts ommiting T ?
The following provides a non-recursive and direct way to remove T from Ts... and, like Jarod42's solutions, yields a std::tuple<Us...> but without the need to use typename ...::type:
#include <tuple>
#include <type_traits>
template<typename...Ts>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<Ts>()...));
template<typename T, typename...Ts>
using remove_t = tuple_cat_t<
typename std::conditional<
std::is_same<T, Ts>::value,
std::tuple<>,
std::tuple<Ts>
>::type...
>;
int main()
{
static_assert(std::is_same<
remove_t<int, int, char, int, float, int>,
std::tuple<char, float>
>::value, "Oops");
}
Live example
Following may help:
namespace detail
{
template <typename T, typename Tuple, typename Res = std::tuple<>>
struct removeT_helper;
template<typename T, typename Res>
struct removeT_helper<T, std::tuple<>, Res>
{
using type = Res;
};
template<typename T, typename... Ts, typename... TRes>
struct removeT_helper<T, std::tuple<T, Ts...>, std::tuple<TRes...>> :
removeT_helper<T, std::tuple<Ts...>, std::tuple<TRes...>>
{};
template<typename T, typename T1, typename ...Ts, typename... TRes>
struct removeT_helper<T, std::tuple<T1, Ts...>, std::tuple<TRes...>> :
removeT_helper<T, std::tuple<Ts...>, std::tuple<TRes..., T1>>
{};
}
template <typename T, typename...Ts> struct RemoveT
{
using type = typename detail::removeT_helper<T, std::tuple<Ts...>>::type;
};
static_assert(std::is_same<std::tuple<char, float>,
typename RemoveT<int, int, char, int, float, int>::type>::value, "");
First, move all the specific template names out into a list. There might be a way of specifying a template name, and a list of parameters, giving that template with the parameters, but I haven't been able to figure it out:
template <typename...TArgs> struct TypeList
{
typedef std::tuple<TArgs...> tuple_type;
// whatever other types you need
};
Next, define addition:
template<typename T, typename TList> struct AddT;
template<typename T, typename ... TArgs>
struct AddT< T, TypeList<TArgs...> >
{
typedef TypeList<T, TArgs... > type;
};
Then, define removal:
template<typename R, typename ... TArgs> struct RemoveT;
template<typename R>
struct RemoveT<R>
{
typedef TypeList<> type;
};
template<typename R, typename T, typename ...TArgs>
struct RemoveT<R, T, TArgs...>
{
typedef typename std::conditional
< std::is_same<R, T>::value
, typename RemoveT<R, TArgs...>::type
, typename AddT<T, typename RemoveT<R, TArgs...>::type>::type
>::type type;
};
Finally, test:
int result = 0;
result = std::is_same
< std::tuple<long,double>
, RemoveT<int, int, long, int, double, int>::type::tuple_type
>::value;
assert ( result );
I want to flatten a tree type to flat type. Example:
typedef std::tuple<int,std::tuple<int,long>,int> tup;
Flat<tup>::type=>std::tuple<int,int,long,int>
I use:
template<typename T>
struct Flat
{
using type=T;
};
template <template <typename ...> class C,typename...ARGS>
struct Flat<C<ARGS...> >
{
using type=C<ARGS...>;
};
template <template <typename ...> class C,typename ...ARGS0,typename...ARGS1,typename ...ARGS2>
struct Flat<C<ARGS0...,C<ARGS1...>,ARGS2...> >
:Flat<C<ARGS0...,ARGS1...,ARGS2...> >
{
};
void test(){
typedef std::tuple<int,std::tuple<int,long>,int> tup;
static_assert(std::is_same<typename Flat<tup>::type,std::tuple<int,int,long,int> >::value,"");
}
but I get std::tuple<int,std::tuple<int,long>,int> still... I use gcc 4.8.1
First the explanation of how this works:
This is a forward declaration so that we can use flatten with a single type.
template<class T>
struct flatten;
Then we specialize flatten to accept any template type.
C is a template template parameter so we can get the type of the surrounding template.
E.g. if you would use flatten with the parameter std::tuple<int, double> then C would be std::tuple.
FArgs is used to retrieve the parameter list of the template passed. In the case of the example I just referred to, it would be int, double
template< template< typename ... > class C, typename ...FArgs>
struct flatten<C<FArgs...>>
The rest of the flatten implementation now has always access to C the wrapper type and FArgs the list of arguments.
Forward declaration for append which appends the parameter B to the list of items in parameter Target
template< typename Target, typename B >
struct append;
The specialization of append which retrieves the list of types from Target (see above).
Note that C here still is std::tuple.
So we're adding one more item to the list and creating a new type by applying the previous list of arguments Args1 and then adding T
template< typename ...Args1, typename T >
struct append<C<Args1...>, T> {
using type = C<Args1..., T>;
};
Forward declaration of inner and inner2
inner is used to iterate through the list of arguments provided and applies inner2 on each of them, which in turn again applies inner on the type, if the type is another template with parameters. This template must be the matched template C
template< typename Target, typename ...Args >
struct inner;
template< typename Target, typename T >
struct inner2;
This inner2 handles all C template types and recursively iterates through its parameter list with the help of inner
template< typename Target, typename ...Args>
struct inner2<Target, C<Args...>> {
using type = typename inner<Target, Args...>::type;
};
If this inner2 specialization is used, the type will be appended to the end result.
template< typename Target, typename T >
struct inner2 {
using type = typename append<Target, T>::type;
};
Recurses through the template parameters by inheriting and applying on each parameter inner2
template< typename Target, typename T, typename ...Args>
struct inner<Target, T, Args...>
: inner<typename inner2<Target, T>::type, Args...>
{};
End condition for the recursion.
template< typename Target, typename T >
struct inner<Target, T>
{
using type = typename inner2<Target, T>::type;
};
Here the whole thing from above gets triggered, C<> specifies the Target and FArgs are the arguments extracted by the flatten specialization.
using type = typename inner<C<>, FArgs...>::type;
};
And here the whole code:
#include <tuple>
#include <string>
template<class T>
struct flatten;
template< template< typename ... > class C, typename ...FArgs>
struct flatten<C<FArgs...>>
{
template< typename Target, typename ...B >
struct append;
template< typename ...Args1, typename T >
struct append<C<Args1...>, T> {
using type = C<Args1..., T>;
};
template< typename Target, typename ...Args >
struct inner;
template< typename Target, typename T >
struct inner2;
template< typename Target, typename ...Args>
struct inner2<Target, C<Args...>> {
using type = typename inner<Target, Args...>::type;
};
template< typename Target, typename T >
struct inner2 {
using type = typename append<Target, T>::type;
};
template< typename Target, typename T, typename ...Args>
struct inner<Target, T, Args...>
: inner<typename inner2<Target, T>::type, Args...>
{};
template< typename Target, typename T >
struct inner<Target, T>
{
using type = typename inner2<Target, T>::type;
};
using type = typename inner<C<>, FArgs...>::type;
};
int main() {
typedef flatten<std::tuple<int, float, double>>::type first;
static_assert(std::is_same<first, std::tuple<int, float, double>>::value, "First not the same");
typedef flatten<std::tuple<int, std::tuple<float, double>>>::type second;
static_assert(std::is_same<second, std::tuple<int, float, double>>::value, "Second not the same");
typedef flatten<std::tuple<int, std::tuple<char const *>, std::tuple<std::tuple<float, int>, double>>>::type third;
static_assert(std::is_same<third, std::tuple<int, char const *, float, int, double>>::value, "Third not the same");
typedef flatten<std::tuple<int, std::tuple<std::tuple<std::tuple<std::tuple<char const *>>>>, std::tuple<std::tuple<float, int>, double>>>::type fourth;
static_assert(std::is_same<fourth, std::tuple<int, char const *, float, int, double>>::value, "Fourth not the same");
typedef flatten<std::tuple<int, std::tuple<std::tuple<std::tuple<std::tuple<std::string>>>>, std::tuple<std::tuple<float, int>, double>>>::type fifth;
static_assert(std::is_same<fifth, std::tuple<int, std::string, float, int, double>>::value, "Fifth not the same");
}
Edit: I have rewritten the implementation to make it more readable and shorter (inspired by #DyP)
Edit2: Explained the code
Here is my stab at this. I tried to document what is going on to make it clear:
We start with Flatten. It takes a type. We will specialize it below:
template<typename T>
struct Flatten;
Here is our workhorse. Takes Src, and flattens its contents and appends it to Dest:
template<typename Dest, typename Src>
struct Flatten_append;
An empty right hand side pack means return left hand side:
template<template<typename...>class Pack, typename... LHS>
struct Flatten_append< Pack<LHS...>, Pack<> > {
typedef Pack<LHS...> type;
};
A right hand side whose first argument is a Pack<...> should be flattened before processing:
template<template<typename...>class Pack, typename... LHS, typename... RHS0, typename... RHSrest>
struct Flatten_append< Pack<LHS...>, Pack<Pack<RHS0...>, RHSrest... > >:
Flatten_append< Pack<LHS...>, Pack< RHS0..., RHSrest... > >
{};
Otherwise, a non-empty right hand side pack should have its first element moved over to the left hand side: (this will match weaker than the above, as it is less specialized)
template<template<typename...>class Pack, typename... LHS, typename RHS0, typename... RHSrest>
struct Flatten_append< Pack<LHS...>, Pack<RHS0, RHSrest... > >:
Flatten_append< Pack<LHS..., RHS0>, Pack< RHSrest... > >
{};
Implement Flatten in terms of Flatten_append to an empty Pack:
template<template<typename...>class Pack, typename... Ts>
struct Flatten< Pack<Ts...> >:Flatten_append< Pack<>, Pack<Ts...> > {};
The goal was to make it as clear what is going on as possible.
Now, you'll note that a downside to this design is that it will flatten any template that only contains types. We probably want to pass in the pack that we want to flatten.
template<template<typename...>class Pack, typename T>
struct Flatten;
template<template<typename...>class Pack, typename Dest, typename Src>
struct Flatten_append;
template<template<typename...>class Pack, typename... Ts>
struct Flatten< Pack<Ts...> > : Flatten_append< Pack, Pack<>, Pack<Ts...> > {};
and then change each specialization of Flatten_append< blah, blah, blah > to Flatten_append< Pack, blah, blah, blah >.
This means you pass in the template you want to flatten explicitly, and the code only flattens that template.
In practice, this may not be needed, as the Pack type gets deduced from the left hand side type passed in.