If I have a class:
template <typename T>
class MyClass
{
// ...
};
and I explicitly instantiate it:
template class MyClass<int>;
template class MyClass<int>; // second time
I get an error on some compilers (Clang for example, but not on VC++ 2010). Why would I want to do this? Well, in some cases T may be a typedef to another type.
template class MyClass<my_type_1>;
template class MyClass<my_type_2>;
With certain build options, my_type_1 is the same as my_type_2 and in other cases it is different. How would I make sure that the above compiles in all scenarios? Is there a way to ignore the duplicate instantiation?
You could find another way to explicitly instantiate the template in a way that you can do metaprogramming on it.
Then instead of doing one instantiation per line, do them all in a pack. Run an n^2 algorithm on them (at compile time) to eliminate duplicates (or, honestly, you could probably skip that: depending on how you instantiate the template, it might not care).
Something like this, assuming Instantiate< Template, types< blah, foo, bar > > actually instantiates the list on the template passed in as the first argument:
#include <utility>
#include <type_traits>
template<typename T>
struct Test {};
template<typename... Ts>
struct types {};
template<template<typename>class Template, typename Types>
struct Instantiate {};
template<template<typename>class Template, typename T0, typename... Ts>
struct Instantiate<Template, types<T0, Ts...>>:
Instantiate<Template, types<Ts...>>
{
Template<T0>& unused();
};
template<typename U, typename Types>
struct prepend;
template<typename U, template<typename...>class pack, typename... Ts>
struct prepend< U, pack<Ts...> > {
typedef pack<U, Ts...> types;
};
template<typename U, typename Types>
using Prepend = typename prepend<U, Types>::types;
template<typename U, typename Types, typename=void>
struct remove_type_from_types;
template<typename U, template<typename...>class pack>
struct remove_type_from_types<U, pack<>, void>
{
typedef pack<> types;
};
template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
typename std::enable_if< std::is_same<U, T0>::value >::type
>: remove_type_from_types< U, pack<Ts...> >
{};
template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
typename std::enable_if< !std::is_same<U, T0>::value >::type
>
{
typedef Prepend< T0, typename remove_type_from_types< U, pack<Ts...> >::types > types;
};
template<typename Types>
struct remove_duplicates {
typedef Types types;
};
template<template<typename...>class pack, typename T0, typename... Ts>
struct remove_duplicates<pack<T0, Ts...>> {
private:
typedef typename remove_type_from_types< T0, pack<Ts...> >::types filtered_tail;
typedef typename remove_duplicates< filtered_tail >::types unique_tail;
public:
typedef Prepend< T0, unique_tail > types;
};
template<typename Types>
using RemoveDuplicates = typename remove_duplicates<Types>::types;
static Instantiate<Test, RemoveDuplicates<types<int, double>> > unused;
int main() {
}
As noted, you can probably do away with the entire eliminate-duplicates bit, because of how I'm instantiating the use of the template. I am also not sure if the above use of each template is sufficient to instantiate it (ie, that it won't be optimized away somehow, and that the symbol will be exported).
(Recursion depth is n in the number of types, and total work done is n^2 in the number of types: that is shallow enough and fast enough for any reasonable number of types, I suspect. Fancier unique type removal is difficult, due to lack of weak ordering on naked types...)
Don't specialize for the typedefs, instead specialize for the relevant underlying types (such as int). That way you can typedef as many/few times as you like and you still always get the specializations you want.
You can define a preprocessor flag for you configuration and then put template inside an #ifdef block.
Yakk saved my days. Actually I have lots of non-type template functions with parameters calculated at compile time depend on some "#define"s. I won't know in advance their values and I want to instantiate them explicitly. But when they are conflicting, the compiler stops with the error "duplicate ...". So I followed the idea of Yakk and I made a very simple version of his. I post it here for someones possibly interested.
File 1: instantiate.h
#ifndef INC_INSTANTIATE_H
#define INC_INSTANTIATE_H
#include <iostream>
#include <utility>
#include <type_traits>
using namespace std;
template<unsigned short base> void func();
template<unsigned short base>
struct test {
test() {
cout << "Class " << base << " instantiated.\n";
func<base>();
}
void instantiate_me(){};
};
template<typename... Ts>
struct instances {};
template<typename Types>
struct Instantiate {};
template<typename T0, typename... Ts>
struct Instantiate<instances<T0, Ts...>> : Instantiate<instances<Ts...>>
{
T0 unused;
};
#endif
File 2: instantiate.cpp
#include <iostream>
#include "instantiate.h"
using namespace std;
static Instantiate<instances<test<1>, test<2>, test<3>, test<4>>> unused;
template<unsigned short base>
void func() {
cout << "Function " << base << " instantiated.\n";
}
static int initialize() {
unused.unused.instantiate_me();
return 0;
}
static int dummy = initialize();
File 3: main.cpp
#include <iostream>
#include "instantiate.h"
using namespace std;
int main() {
cout << "Good day commander!\n";
}
It works perfectly with g++ 8.0.1 ubuntu 18.04 hyper-v windows 10. Here I do not
need to care about template duplication because I explicitly use implicit instantiation, in one file there won't never be duplication. With all functions' definitions in one file, I can instantiate it in the constructor
of the class "test" in the file instantiate.h. With plural files, I use one Instantiate, one initialize and one dummy for each, with possibly plural implementations of the class "test".
using the extern template syntax can solve the problem, example:
extern template int max (int, int);
Related
I am attempting to add some extern template into my project, to speed up build times and reduce footprint on disk during build.
I've done so by listing specialisations that I use frequently, e.g.
extern template class std::vector<std::string>;
extern template class std::vector<unsigned int>;
extern template class std::vector<uint32_t>;
extern template class std::vector<size_t>;
The problem is that unsigned int is size_t, maybe, and uint32_t is unsigned int, maybe. So, depending on the build target, compilation fails for the "non-extern" variety of the list that actually instantiates the specialisations:
template class std::vector<std::string>;
template class std::vector<unsigned int>;
template class std::vector<uint32_t>;
template class std::vector<size_t>;
The error is like this (line and column number inaccurate):
templates-impl.h:15:36: error: duplicate explicit instantiation of ‘class std::vector<long unsigned int>’ [-fpermissive]
template class std::vector<size_t>;
My goal is to just be able to list the types I use, as I use them, with a minimum of fuss and with no need to hardcode variants of the list for different targets.
I guess in C++14 I could solve this with an if constexpr and some std::is_same, at least if that were permitted at namespace scope.
How can I do it in C++11?
Alternatively, you may have type-lists of to-be-instantiated types, of which only repeating types are replaced with dummy types (hopefully, to be optimized-out at link time).
In C++11, something like:
template<typename... V>
struct explicit_instlist
{
template<int I>
struct get
{
using VI = typename std::tuple_element<I,std::tuple<V...>>::type;
using type = typename std::conditional< is_first_at<I,VI,V...>::value,
VI, dummy_inst<I,VI> >::type;
};
};
template<unsigned I>
using my_list = typename explicit_instlist<
std::string,
unsigned int,
uint32_t,
size_t
>::template get<I>::type;
/*extern*/ template class std::vector< my_list<0> >;
/*extern*/ template class std::vector< my_list<1> >;
/*extern*/ template class std::vector< my_list<2> >;
/*extern*/ template class std::vector< my_list<3> >;
where dummy_inst<I,T> generates a unique dummy type to be used in place of T when already used, and is_first_at works like:
template<int I,typename T>
struct dummy_inst {};
template<int I,typename T,typename... V>
struct is_first_at {};
template<int I,typename T,typename V,typename... Vs>
struct is_first_at<I,T,V,Vs...>: std::conditional< std::is_same<T,V>::value,
std::integral_constant<bool,I==0>,
is_first_at<I-1,T,Vs...> >::type {};
clearly, dummy_inst will need to be specialized if the to be explictly instantiated template does not support the empty default. Something like boost preprocessor may be used to avoid explictly 'iterating' the explicit instantations and write a macro accepting the type list directly ...
How about drop the unsigned int and size_t specializations and just make them all size-based?
extern template class std::vector<uint8_t>;
extern template class std::vector<uint16_t>;
extern template class std::vector<uint32_t>;
extern template class std::vector<uint64_t>;
Here's a way of doing it:
#include <utility>
#include <type_traits>
template<typename T>
struct Test {};
template<typename... Ts>
struct types {};
template<template<typename>class Template, typename Types>
struct Instantiate {};
template<template<typename>class Template, typename T0, typename... Ts>
struct Instantiate<Template, types<T0, Ts...>>:
Instantiate<Template, types<Ts...>>
{
Template<T0>& unused();
};
template<typename U, typename Types>
struct prepend;
template<typename U, template<typename...>class pack, typename... Ts>
struct prepend< U, pack<Ts...> > {
typedef pack<U, Ts...> types;
};
template<typename U, typename Types>
using Prepend = typename prepend<U, Types>::types;
template<typename U, typename Types, typename=void>
struct remove_type_from_types;
template<typename U, template<typename...>class pack>
struct remove_type_from_types<U, pack<>, void>
{
typedef pack<> types;
};
template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
typename std::enable_if< std::is_same<U, T0>::value >::type
>: remove_type_from_types< U, pack<Ts...> >
{};
template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
typename std::enable_if< !std::is_same<U, T0>::value >::type
>
{
typedef Prepend< T0, typename remove_type_from_types< U, pack<Ts...> >::types > types;
};
template<typename Types>
struct remove_duplicates {
typedef Types types;
};
template<template<typename...>class pack, typename T0, typename... Ts>
struct remove_duplicates<pack<T0, Ts...>> {
private:
typedef typename remove_type_from_types< T0, pack<Ts...> >::types filtered_tail;
typedef typename remove_duplicates< filtered_tail >::types unique_tail;
public:
typedef Prepend< T0, unique_tail > types;
};
template<typename Types>
using RemoveDuplicates = typename remove_duplicates<Types>::types;
static Instantiate<Test, RemoveDuplicates<types<int, double>> > unused;
Possible improvements/alternatives:
Do a daisy chain call. Then call the bottom one in an exported function that is not called by anyone. The templates will be instantiated in order to export that function, and later at linking the exported function will be eliminated
Do a sum-of-sizeof
I am not sure how extern works but if it is a question of explicitly instantiating the template, maybe you can use a function invocation like this.
Type duplication shouldn't generate an error.
template<class T>
void instance(T const&){std::vector<T> v{}; (void)v;}
void instantiate(){
instance(std::string{}):
instance(unsigned int{});
instance(uint32_t{});
instance(size_t{});
}
(I am not saying it is the best method.)
Consider a hypothetical metafunction arity, which takes any metafunction as argument and returns its actual arity.
The following obvious approach is not possible, since by language standards named inner template template parameters are only defined locally.
template<template<typename... args> class f>
struct arity
{
static constexpr std::size_t value = sizeof...(args); //ERROR: undefined 'args'
};
Not even exhaustive specializations are an alternative, since a template type taking another template type may not be partially specialized with respect to the number of arguments of the inner template.
This brings me to the question, whose answer I fear to be no.
Is there any reasonable way to introspect the actual arity of a template type?
I don't expect an actual implementation of arity to take the form of a template type, such as in the obvious approach, that is, anything which may be computed during compile time is acceptable as a "reasonable" solution, as long as it doesn't depend on the actual arguments.
Note: for simplicity assume only non-variadic metafunctions are allowed as arguments for arity.
template<class...> struct foo;
template<class X> struct foo<X>:std::true_type {};
template<class X, class Y, class Z> struct foo<X,Y,Z>:std::false_type {};
under any naive pattern matching, foo has an infinite airity.
In practice, it has an airity of either 1 or 3.
In general, the question "what is the airty of this template" is the wrong question. Rather, "can these types be passed to this template", or "how many of these types can be passed to this template" is a more useful one.
Looking for the airity of a template is like wanting to extract the signature from a callable object. If you know how you are going to call an object, asking "can I call it this way? How about that?" is reasonable; asking "tell me how to call you" is almost always misguided.
template<class...>struct types{using type=types;};
template<class types>struct types_length;
template<class...Ts>struct types_length<types<Ts...>>:
std::integral_constant<size_t, sizeof...(Ts)>
{};
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
namespace details {
template<template<class...>class Z, class types, class=void>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,types<Ts...>,void_t<Z<Ts...>>>: std::true_type {};
};
template<template<class...>class Z, class...Ts>
struct can_apply : details::can_apply<Z,types<Ts...>> {};
the above answers the question "can I apply some types to a template".
Now, the longest prefix of a set of types you can apply to a template:
template<class T>struct tag{using type=T;};
namespace details {
template<class types, class=types<>>
struct pop_back {};
template<class T0, class...rhs>
struct pop_back<types<T0>, types<rhs...>>:types<rhs...> {};
template<class T0, class...Ts, class...rhs>
struct pop_back<types<T0, Ts...>, types<rhs...>>:
pop_back<types<T0,Ts...>,types<rhs...,T0>>
{};
template<class types>
using pop_back_t = typename pop_back<types>::type;
}
template<class types>
using pop_back = details::pop_back_t<types>;
namespace details {
template<template<class...>class Z, class types, class=void>
struct longest_prefix {};
template<template<class...>class Z, class...Ts>
struct longest_prefix<
Z,types<Ts...>,
std::enable_if_t<can_apply<Z,Ts...>>
>:
types<Ts...>
{};
template<template<class...>class Z,class T0, class...Ts>
struct longest_prefix<
Z,types<T0, Ts...>,
std::enable_if_t<!can_apply<Z, T0, Ts...>>
>:
longest_prefix<Z,pop_back_t<types<T0,Ts...>>>
{};
}
template<template<class...>class Z, class...Ts>
using longest_prefix =
typename details::longest_prefix<Z, types<Ts...>>::type;
namespace details {
template<class types>
struct pop_front;
template<>
struct pop_front<types<>> {};
template<class T0, class...Ts>
struct pop_front<types<T0,Ts...>>:types<Ts...>{};
template<class types>
using pop_front_t=typename pop_front<types>::type;
}
similar code that takes a bundle of types and a template, and repeatedly slices off the longest prefix of the bundle of types that can be passed to the template can be written.
(The above code certainly contains typos).
template<class types>
using pop_front = details::pop_front_t<types>;
template<size_t n, template<class...>class Z, class T>
struct repeat : repeat< n-1, Z, Z<T> > {};
template<template<class...>class Z, class T>
struct repeat<0,Z,T> : tag<T> {};
template<size_t n, template<class...>class Z, class T>
using repeat_t = typename repeat<n,Z,T>::type;
template<template<class...>class Z, class types>
using longest_prefix_tail =
repeat_t<
types_length<longest_prefix<Z,Ts...>>{},
pop_front,
types<Ts...>
>;
now we can take a template and a bunch of types, and build a bundle of types resulting from applying the template to the longest prefix of the bunch of types in turn.
If we where insane, we could even do backtracking, so that if our template takes 2 or 3 elements, and we feed it 4, it wouldn't try to feed it 3, then fail on having 1 element left -- instead, it could find the longest prefix of each application that allows the tail to be similarly bundled.
While it is true that template types taking template template parameters may not be partially specialized with respect to the arity of its template template parameters, functions may be overload in this manner.
template<template<typename> class f>
constexpr std::size_t _arity(){return 1;}
template<template<typename, typename> class f>
constexpr std::size_t _arity(){return 2;}
template<template<typename, typename, typename> class f>
constexpr std::size_t _arity(){return 3;}
//...
template<template<typename...> class f>
constexpr std::size_t _arity(){return 0;}
template<template<typename... args> class f>
struct arity
{
static constexpr std::size_t value = _arity<f>();
};
While not ideal, this approach works within reasonable limits and is the closest to a "reasonable" solution that I could think of. However I'm still looking for a pure variadic solution which does not require exhaustive enumeration of functions/types.
It is my approach to this problem. It computes template's arity
by substituting fake types.
is_subs_success checks whether it is possible to substitute types to variadic template:
#include <boost/mpl/assert.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/integral_c.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/void.hpp>
#include <boost/mpl/eval_if.hpp>
using namespace boost;
/*
is_subs_success<F, T...>::value == false
==> F<T...> causes a compile error
*/
template
<
template<typename... FuncArgs> class Func,
typename... SubsArgs
>
class is_subs_success {
typedef int success[1];
typedef int failure[2];
// if it's not possible to substitute overload
template<typename...>
static failure& test(...);
// if it's possible to substitute overload
template<typename... U>
static success& test(typename mpl::identity<Func<U...> >::type*);
public:
typedef is_subs_success<Func, SubsArgs...> type;
static bool const value =
sizeof(test<SubsArgs...>(0)) == sizeof(success);
};
arity computes template's arity. It substitutes fake arguments in template. If substitution causes compile error, it continues with one more argument:
template
<
template<typename... FuncArgs> class Func
>
class arity {
// Checks whether `U` is full set of `Func`'s arguments
template<typename... U>
struct is_enough_args;
// Adds one more argument to `U` and continues iterations
template<size_t n, typename... U>
struct add_arg;
template<size_t n, typename... SubsArgs>
struct go : mpl::eval_if
<
is_enough_args<SubsArgs...>,
mpl::integral_c<size_t, n>,
add_arg<n, SubsArgs...>
> {};
template<typename... U>
struct is_enough_args : is_subs_success<Func, U...> {};
template<size_t n, typename... U>
struct add_arg {
typedef typename
go<n + 1, mpl::void_, U...>::type type;
};
public:
typedef typename go<0>::type type;
};
This solution works fine only with templates. arity never returns 0.
Simple check:
template<typename A>
struct t1 {};
template<typename A, typename B>
struct t2 {};
template<typename A, typename B, typename C>
struct t3 {};
int main() {
BOOST_MPL_ASSERT((mpl::bool_<arity<t1>::type::value == 1>));
BOOST_MPL_ASSERT((mpl::bool_<arity<t2>::type::value == 2>));
BOOST_MPL_ASSERT((mpl::bool_<arity<t3>::type::value == 3>));
}
I was wondering if there was any solution to find if a type was a specialization of a template that takes non-type parameters without specifying every type ?
For instance, if have a class like this:
template<typename T, std::size_t R>
struct F {}
For now, I'm using a very specialized traits:
template<template<typename, std::size_t> class TT, typename T>
struct is_2 : std::false_type { };
template<template<typename, std::size_t> class TT, typename V1, std::size_t R>
struct is_2<TT, TT<V1, R>> : std::true_type { };
and used like is_2<F, T>::value. However, this is not practical since, if you add another template parameter, you have to edit your traits. Moreover, if you have several templates of this kind, you need to write a traits for each of them.
Is there any way to make something more practical ? I can use C++14. And I don't mean using a macro to reduce the code amount.
Non-type template parameters are a bit of a red headed stepchild.
There is no "any template parameter is matched, type or not".
If you can modify F, you make it more uniform by wrapping your constants in thin types. So:
template<typename T, class R>
struct F;
template<typename T, std::size_t R>
struct F<T, std::integral_constant<std::size_t, R>> {};
now meta-programs like is can be written uniformly:
template<template<class...>class Template, class T>
struct is_instantiation : std::false_type {};
template<template<class...>class Template, class... Ts>
struct is_instantiation<Template, Template<Ts...>> : std::true_type {};
matching everything.
If you have less control over F, you can either use your approach, or write metaprogram that hoists both a template and an instance of that template into something with type wrappers.
struct meta_F {
template<class T, std::size_t R>using raw_apply=F<T,R>;
template<class T, class R>using apply=raw_apply<T,R::value_type>;
};
template<class meta_Template, class... Args>
struct type_lifted_template {};
template<class T, std::size_t R>
struct type_lifted_template< meta_F, T, std::integral_constant<std::size_t, R> > {
using result = meta_F::template raw_apply<T, R>;
};
template<class T, std::size_t R>
auto type_lift_instance( F<T,R> )
-> type_lifted_template< meta_F, T, std::integral_constant<std::size_t, R> >;
Now, type_lift_instance can be specialized for multiple types, and some decltype magic could be used to extract the type_lifted_template specialization for different types.
All of this is pretty rough. You'd be best, if you are doing lots of meta programming on templates, to just have your templates take uniform type parameters, instead of messing around with this stuff.
template<class meta_F, class C>
struct meta_template_is_lifted : std::false_type {};
template<class meta_F, class...Ts>
struct meta_template_is_lifted<meta_F, type_lifted_template< meta_F, Ts... >> : std::true_type {};
template<class meta_F, class C>
struct meta_template_is : meta_template_is_lifted< meta_F, decltype(type_lift_instance( std::declval<C>() ) ) > {};
this isn't much less typing, but the metafication goes on far away from the is code (or other similar code).
I'm probably using "lift" incorrectly.
If you can modify F and there are no other restrictions you haven't mentioned, the easiest solution would be to add a unique base class:
#include <cstddef>
#include <type_traits>
struct unique_F_base {};
template<typename T, std::size_t R>
struct F : unique_F_base
{
};
template<typename T>
using is_F = std::is_base_of<unique_F_base,T>;
int main()
{
static_assert( !is_F< int >::value, "Oops" );
static_assert( is_F< F<int,42> >::value, "Oops" );
}
I'm writing a template for expressions parametrised by an arbitrary number of char labels.
Given an argument list, a factory function returns an expression of different types depending on whether there are two arguments of the same types or whether they are unique.
A concrete example: suppose that A is a "labelable" object with its operator() overloaded to produce an ?Expression<...>. Let a, b, ... be declared as labels LabelName<'a'>, LabelName<'b'>, .... Then A(a,b,c,d) would produce a UniqueExpression<'a','b','c','d'>, whereas A(a,c,b,c) would produce a RepeatedExpression<'a','c','b','c'> instead.
To achieve this, I had to define the ?Expression's factory function with auto and decltype. Moreover, the decltype has to cascade to another decltype until the metaprogram finishes recursing through the arguments and the return type is finally decided. As an illustration, I have isolated a fairly minimal code for the factory method.
template <typename... T> struct TypeList { };
template <char C> struct LabelName { };
template <typename... T> class UniqueExpression
{
// Contains implementation details in actual code
};
template <typename... T> class RepeatedExpression
{
// Contains implementation details in actual code
};
class ExpressionFactory {
private:
template <char _C, typename... T, typename... _T>
static UniqueExpression<T...>
_do_build(TypeList<T...>,
TypeList<LabelName<_C>>,
TypeList<>,
TypeList<_T...>)
{
return UniqueExpression<T...> ();
}
template <char _C, typename... T, typename... _T1, typename... _T2, typename... _T3>
static RepeatedExpression<T...>
_do_build(TypeList<T...>,
TypeList<LabelName<_C>, _T1...>,
TypeList<LabelName<_C>, _T2...>,
TypeList<_T3...>)
{
return RepeatedExpression<T...> ();
}
template <char _C1, char _C2, typename... T, typename... _T1, typename... _T2, typename... _T3>
static auto
_do_build(TypeList<T...>,
TypeList<LabelName<_C1>, _T1...>,
TypeList<LabelName<_C2>, _T2...>,
TypeList<_T3...>)
-> decltype(_do_build(TypeList<T...>(),
TypeList<LabelName<_C1>, _T1...>(),
TypeList<_T2...>(),
TypeList<_T3..., LabelName<_C2>>()))
{
return _do_build(TypeList<T...>(),
TypeList<LabelName<_C1>, _T1...>(),
TypeList<_T2...>(),
TypeList<_T3..., LabelName<_C2>>());
}
template <char _C1, char _C2, typename... T, typename... _T1, typename... _T2>
static auto
_do_build(TypeList<T...>,
TypeList<LabelName<_C1>, LabelName<_C2>, _T1...>,
TypeList<>,
TypeList<LabelName<_C2>, _T2...>)
-> decltype(_do_build(TypeList<T...>(),
TypeList<LabelName<_C2>, _T1...>(),
TypeList<_T2...>(),
TypeList<>()))
{
return _do_build(TypeList<T...>(),
TypeList<LabelName<_C2>, _T1...>(),
TypeList<_T2...>(),
TypeList<>());
}
public:
template <char C, typename... T>
static auto
build_expression(LabelName<C>, T...)
-> decltype(_do_build(TypeList<LabelName<C>, T...>(),
TypeList<LabelName<C>, T...>(),
TypeList<T...>(),
TypeList<>()))
{
return _do_build(TypeList<LabelName<C>, T...>(),
TypeList<LabelName<C>, T...>(),
TypeList<T...>(),
TypeList<>());
}
};
The factory could be called in the program like so: (in the actual program there is another class with an overloaded operator() which calls the factory)
int main()
{
LabelName<'a'> a;
LabelName<'b'> b;
...
LabelName<'j'> j;
auto expr = ExpressionFactory::build_expression(a,b,c,d,e,f,g,h,i,j);
// Perhaps do some cool stuff with expr
return 0;
}
The above code works as intended, and is correctly compiled by both GCC and the Intel compiler. Now, I understand that the compiler would take more time to perform recursive template deduction as I crank up the number of labels I use.
On my computer, if build_expression is called with one argument, then GCC 4.7.1 takes around 0.26 second to compile on average. The compile time scales up to around 0.29 second for five arguments, and to 0.62 second for ten arguments. This is all perfectly reasonable.
The story is quite different with the Intel compiler. ICPC 13.0.1 compiles the one-argument code in 0.35 second, and the compile time stays pretty much constant for up to four arguments. At five arguments the compile time goes up to 12 seconds, and at six arguments it shoots up above 9600 seconds (that is, over 2 hours and 40 minutes). Needless to say, I haven't waited long enough to find out how long it takes to compile the seven-argument version.
Two questions immediately come to mind:
Is the Intel compiler particularly known to be slow to compile recursive decltype?
Is there any way to rewrite this code to achieve the same effect in a way that is perhaps friendlier to the compiler?
Here is a stab at it. Instead of doing pairwise comparisons of each of the elements, I sort the list of types, then use a brain-dead unique algorithm to see if there are any duplicates.
I implemented merge sort on types, because it was fun. Probably a naive bubble sort would work better on reasonable number of arguments. Note that a bit of work would allow us to do a merge sort on long lists, and specialize for bubble sorts (or even insertion sorts) on short lists. I'm not up for writing a template quicksort.
This gives me a compile time boolean that says if there are duplicates in the list. I can then use enable_if to pick which overload I'm going to use.
Note that your solution involved n^2 layers of template recursion, at each stage of which the return type requires evaluating the type of a 1 step simpler class, and then the type returned also requires the same! If the Intel compiler memoization fails, you are talking exponential amounts of work.
I augmented a few of your classes with some helpers. I made your LabelNames inherit from std::integral_constant, so I have easy compile time access to their value. This makes the sorting code a tad easier. I also exposed an enum from the repeated and unique return values so I can do simple printf debugging on the result.
Most of this work is writing the merge sort -- is there a standard compile time type sort we could use?
#include <type_traits>
#include <iostream>
template <typename... T> struct TypeList { };
// NOTE THIS CHANGE:
template <char C> struct LabelName:std::integral_constant<char, C> {};
template <typename... T> class UniqueExpression
{
// Contains implementation details in actual code
public:
enum { is_unique = true };
};
template <typename... T> class RepeatedExpression
{
// Contains implementation details in actual code
public:
enum { is_unique = false };
};
// A compile time merge sort for types
// Split takes a TypeList<>, and sticks the even
// index types into Left and odd into Right
template<typename T>
struct Split;
template<>
struct Split<TypeList<>>
{
typedef TypeList<> Left;
typedef TypeList<> Right;
};
template<typename T>
struct Split<TypeList<T>>
{
typedef TypeList<T> Left;
typedef TypeList<> Right;
};
// Prepends First into the TypeList List.
template<typename First, typename List>
struct Prepend;
template<typename First, typename... ListContents>
struct Prepend<First,TypeList<ListContents...>>
{
typedef TypeList<First, ListContents...> type;
};
template<typename First, typename Second, typename... Tail>
struct Split<TypeList<First, Second, Tail...>>
{
typedef typename Prepend< First, typename Split<TypeList<Tail...>>::Left>::type Left;
typedef typename Prepend< Second, typename Split<TypeList<Tail...>>::Right>::type Right;
};
// Merges the sorted TypeList<>s Left and Right to the end of TypeList<> MergeList
template< typename Left, typename Right, typename MergedList=TypeList<> >
struct Merge;
template<typename MergedList>
struct Merge< TypeList<>, TypeList<>, MergedList >
{
typedef MergedList type;
};
template<typename L1, typename... Left, typename... Merged>
struct Merge< TypeList<L1, Left...>, TypeList<>, TypeList<Merged... >>
{
typedef TypeList<Merged..., L1, Left...> type;
};
template<typename R1, typename... Right, typename... Merged>
struct Merge< TypeList<>, TypeList<R1, Right...>, TypeList<Merged...> >
{
typedef TypeList<Merged..., R1, Right...> type;
};
template<typename L1, typename... Left, typename R1, typename... Right, typename... Merged>
struct Merge< TypeList<L1, Left...>, TypeList<R1, Right...>, TypeList<Merged...>>
{
template<bool LeftIsSmaller, typename LeftList, typename RightList, typename MergedList>
struct MergeHelper;
template<typename FirstLeft, typename... LeftTail, typename FirstRight, typename... RightTail, typename... MergedElements>
struct MergeHelper< true, TypeList<FirstLeft, LeftTail...>, TypeList<FirstRight, RightTail...>, TypeList<MergedElements...> >
{
typedef typename Merge< TypeList<LeftTail...>, TypeList< FirstRight, RightTail... >, TypeList< MergedElements..., FirstLeft > >::type type;
};
template<typename FirstLeft, typename... LeftTail, typename FirstRight, typename... RightTail, typename... MergedElements>
struct MergeHelper< false, TypeList<FirstLeft, LeftTail...>, TypeList<FirstRight, RightTail...>, TypeList<MergedElements...> >
{
typedef typename Merge< TypeList<FirstLeft, LeftTail...>, TypeList<RightTail... >, TypeList< MergedElements..., FirstRight > >::type type;
};
typedef typename MergeHelper< (L1::value < R1::value), TypeList<L1, Left...>, TypeList<R1, Right...>, TypeList<Merged...> >::type type;
};
// Takes a TypeList<T...> and sorts it via a merge sort:
template<typename List>
struct MergeSort;
template<>
struct MergeSort<TypeList<>>
{
typedef TypeList<> type;
};
template<typename T>
struct MergeSort<TypeList<T>>
{
typedef TypeList<T> type;
};
template<typename First, typename Second, typename... T>
struct MergeSort<TypeList<First, Second, T...>>
{
typedef Split<TypeList<First, Second, T...>> InitialSplit;
typedef typename MergeSort< typename InitialSplit::Left >::type Left;
typedef typename MergeSort< typename InitialSplit::Right >::type Right;
typedef typename Merge< Left, Right >::type type;
};
// Sorts a TypeList<T..>:
template<typename List>
struct Sort: MergeSort<List> {};
// Checks sorted TypeList<T...> SortedList for adjacent duplicate types
// return value is in value
template<typename SortedList>
struct Unique;
template<> struct Unique< TypeList<> >:std::true_type {};
template<typename T> struct Unique< TypeList<T> >:std::true_type {};
template<typename First, typename Second, typename... Tail>
struct Unique< TypeList< First, Second, Tail... > >
{
enum
{
value = (!std::is_same<First, Second>::value) &&
Unique< TypeList<Second, Tail...> >::value
};
};
// value is true iff there is a repeated type in Types...
template<typename... Types>
struct RepeatedType
{
typedef TypeList<Types...> MyListOfTypes;
typedef typename Sort< MyListOfTypes >::type MyListOfTypesSorted;
enum
{
value = !Unique< MyListOfTypesSorted >::value
};
};
// A struct that creates an rvalue trivial constructed type
// of any type requested.
struct ProduceRequestedType
{
template<typename Result>
operator Result() { return Result(); };
};
struct ExpressionFactory {
template<typename... T>
typename std::enable_if<
!RepeatedType<T...>::value,
UniqueExpression<T...>
>::type
build_expression(T...) const
{
return ProduceRequestedType();
};
template<typename... T>
typename std::enable_if<
RepeatedType<T...>::value,
RepeatedExpression<T...>
>::type
build_expression(T...) const
{
return ProduceRequestedType();
};
};
// Simple testing code for above:
int main()
{
auto foo1 = ExpressionFactory().build_expression( LabelName<'a'>(), LabelName<'b'>(), LabelName<'a'>() );
typedef decltype(foo1) foo1Type;
if (foo1Type::is_unique)
std::cout << "foo1 is unique\n";
else
std::cout << "foo1 is repeated\n";
auto foo2 = ExpressionFactory().build_expression( LabelName<'q'>(), LabelName<'a'>(), LabelName<'b'>(), LabelName<'d'>(), LabelName<'t'>(), LabelName<'z'>() );
typedef decltype(foo2) foo2Type;
if (foo2Type::is_unique)
std::cout << "foo2 is unique\n";
else
std::cout << "foo2 is repeated\n";
}
In addition I'd like to add a critique of your code: Template programming is programming -- your typenames are the equivalent of using "i1" through "i9" for integer variables in a function. Give your typenames meaningful names when doing something non-trivial.
How does this compile on Intel?
I would like to allow use of the class I'm writing to specify as a template parameters a list of types along with a list of allocators of those types in a manner that types are at odd positions and allocators are at even ones:
template<typename... T>
class MyClass {
// Stuff inside
}
int main() {
MyClass<SomeType1, AllocatorOfSomeType1> c1;
MyClass<SomeType1, AllocatorOfSomeType1,
SomeType2, AllocatorOfSomeType2> c2;
MyClass<SomeType1, AllocatorOfSomeType1,
SomeType2, AllocatorOfSomeType2,
SomeType3, AllocatorOfSomeType3> c3;
// And so on....
}
Internally it would make sense to have a tuple of vectors of types for storage:
std::tuple<std::vector<EveryOddTypeInParameterPack>...> m_storage_;
and a tuple of allocators for usage:
std::tuple<std::vector<EveryEvenTypeInParameterPack>...> m_storage_;
How can I actually declare those tuples in code? In theory I need to somehow select every odd/even type in parameter pack - is that possible?
Though the code got a little lengthy, I suppose the mechanism doesn't have
unnecessary peculiarities.
If I understand the question correctly,
probably the following code will meet the purpose:
// push front for tuple
template< class, class > struct PFT;
template< class A, class... T > struct PFT< A, tuple< T... > > {
typedef tuple< A, T... > type;
};
// for even
template< class... > struct even_tuple;
template< class A, class B > struct even_tuple< A, B > {
typedef tuple< A > type;
};
template< class A, class B, class... T > struct even_tuple< A, B, T... > {
typedef typename PFT< A, typename even_tuple< T... >::type >::type type;
};
// As for odd elements, in the same way as even(please see the test on ideone)
// objective type
template< class > struct storage_type;
template< class... T > struct storage_type< tuple< T... > > {
typedef tuple< vector< T >... > type;
};
template< class... T >
struct MyClass {
typename storage_type< typename even_tuple< T... >::type >::type
m_storage_even_;
typename storage_type< typename odd_tuple< T... >::type >::type
m_storage_odd_;
};
Here is a test on ideone.
Perhaps something like this:
#include <tuple>
// Example receptacle
template <typename ...Args> struct MyContainer;
// Tuple concatenator
template<typename PackR, typename PackL> struct cat;
template<typename ...R, typename ...L>
struct cat<std::tuple<R...>, std::tuple<L...>>
{
typedef std::tuple<R..., L...> type;
};
// Even/Odd extractors
template <typename ...Args> struct GetEven;
template <typename ...Args> struct GetOdd;
template <typename E1, typename O1, typename ...Args>
struct GetEven<E1, O1, Args...>
{
typedef typename cat<std::tuple<E1>, typename GetEven<Args...>::value>::type value;
};
template <typename E1, typename O1>
struct GetEven<E1, O1>
{
typedef std::tuple<E1> value;
};
template <typename E1, typename O1, typename ...Args>
struct GetOdd<E1, O1, Args...>
{
typedef typename cat<std::tuple<O1>, typename GetEven<Args...>::value>::type value;
};
template <typename E1, typename O1>
struct GetOdd<E1, O1>
{
typedef std::tuple<O1> value;
};
// Tuple-to-Receptacle mover
template <typename Pack, template <typename ...T> class Receiver> struct Unpack;
template <typename ...Args, template <typename ...T> class Receiver>
struct Unpack<std::tuple<Args...>, Receiver>
{
typedef Receiver<Args...> type;
};
// Example consumer
template <typename ...Args>
struct Foo
{
typedef typename Unpack<typename GetEven<Args...>::value, MyContainer>::type EvenVector;
typedef typename Unpack<typename GetOdd<Args...>::value, MyContainer>::type OddVector;
EvenVector x;
OddVector y;
};
You still have to define your MyContainer class to do something useful with the variadic parameters, e.g. implement your tuple of vectors... (why not a vector of tuples, though?)
Credits to brunocodutra for the tuple trick.
this is just a try
template<typename... T> class Myclass;
template<typename T1, typename allocT1>
class MyClass <T1, allocT1> {
std::pair<T1, allocT1> myFirstArglist;
//and you have to do a check that allocT1::value_type is same as T1 or not
//or may be alloT1 is an allocator type or not(i'm thinking concepts, may be)
//this idea is inspired from Chris's comment
};
template<typename T1, typename allocT1, typename... T>
class Myclass<T1, allocT1, T...> {
std::pair<T1, allocT1> myFirstArglist;
Myclass<T>; //something like this
};
template<>
class Myclass<> {
//probably you would like some error message here
//when there are no types and containers
};
may be i'm not clear enough, you'd probably like to read
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2080.pdf
Also there is a good post related to design of allocator types... you would like to have a look at:
C++ Design Pattern for allocator type arguments
I know your question was originally tagged "c++11", but I figure it's worth pointing out for posterity that in C++14 you have access to make_index_sequence, and that makes the whole thing pretty simple. For filtering a tuple, I'd start with this outline: https://quuxplusone.github.io/blog/2018/07/23/metafilter/
And then we end up with something like this (Godbolt):
template<bool> struct zero_or_one {
template<class E> using type = std::tuple<E>;
};
template<> struct zero_or_one<false> {
template<class E> using type = std::tuple<>;
};
template<class Tuple, class = std::make_index_sequence<std::tuple_size<Tuple>::value>>
struct just_evens;
template<class... Es, size_t... Is>
struct just_evens<std::tuple<Es...>, std::index_sequence<Is...>> {
using type = decltype(std::tuple_cat(
std::declval<typename zero_or_one<Is % 2 == 0>::template type<Es>>()...
));
};
To get just_odds, you'd switch the condition from Is % 2 == 0 to Is % 2 != 0.
Example usage:
static_assert(std::is_same<
just_evens<std::tuple<char, short, int, long, double>>::type,
std::tuple<char, int, double>
>::value, "");