How to introspect the arity of a variadic template template argument? - c++

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>));
}

Related

Obtain original struct/class name during C++ template instantiation

template<typename T> struct S {};
template<typename T> struct R {};
int main() {
typedef S<double> s1;
typedef S<int> s2;
typedef R<int> s3;
static_assert(xxx<s1, s2>::value,
"No, assertion must not be raised");
static_assert(xxx<s2, s3>::value,
"Yes, assertion must be raised");
}
So, I want xxx<s1, s2>::value to return true while xxx<s2, s3>::value to return false during compile-time.
Is the existence of xxx impossible in C++?
Or, is the existence of xxx theoretically possible in C++ but possibly no one has done it yet?
Use two specialisations​ that use template template parameters to perform this "matching":
template<
typename T,
typename V>
struct xxx;
template<
template <class> class A,
template <class> class B,
typename X,
typename Y>
struct xxx<A<X>, B<Y>> {
static constexpr const int value = false;
};
template<
template <class> class U,
typename X,
typename Y>
struct xxx<U<X>, U<Y>> {
static constexpr const int value = true;
};
With your code on ideone
Note: For it to be a real type trait you should not set value manually, but derive from std::integral_constant (std::true_type or std::false_type). Above is just a quick mockup I did on my phone.
Something like same_base_template:
#include <type_traits>
template<class A, class B>
struct same_base_template : std::false_type{};
template<template<class...> class S, class... U, class... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};
Edit:
And a third specialization since you are using non-type template arguments (std::ratio):
template<class T, template<T...> class S, T... U, T... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};
Demo
This uses true_typeand false_type from type_traits so we don't need to write a constexpr bool value ourselves. I used a variadic template here because it was slightly more generic and took only a few more keystrokes to do. For your specific use case, you don't need them)

Expanding with pack of templates

Define template_pack as in the following example. Given
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
then
template_pack<std::tuple<char, bool, double>, A, B, C>::type
is to be
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
i.e. always reading from left to right in the tuple, so as to get enough types to fit each template.
Here's my solution so far. But it only works for templates that take up to 3 types, and I don't see how I can generalize to templates of any number of types:
#include <type_traits>
#include <tuple>
template <template <typename...> class Template, typename... Ts> struct use_template;
template <template <typename> class Template, typename A, typename... Rest>
struct use_template<Template, A, Rest...> {
using type = Template<A>;
};
template <template <typename, typename> class Template, typename A, typename B, typename... Rest>
struct use_template<Template, A, B, Rest...> {
using type = Template<A,B>;
};
template <template <typename, typename, typename> class Template, typename A, typename B, typename C, typename... Rest>
struct use_template<Template, A, B, C, Rest...> {
using type = Template<A,B,C>;
};
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts, template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<typename use_template<Templates, Ts...>::type...>;
};
// Testing
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
int main() {
static_assert (std::is_same<
template_pack<std::tuple<char, bool, double>, A, B, C>::type,
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
>::value, "");
}
How to generalize the above? Is it even possible?
This is "find shortest prefix".
Utilities:
// pack that is easy to append to
template<class... Ts>
struct pack{
template<class... T1s>
using append = pack<Ts..., T1s...>;
};
template<class...> using void_t = void;
The meat:
// find the shortest proper prefix Ts... of [T, Rest...]
// such that A<Ts...> is well-formed, and return it.
template<template<class...> class A, // template we are going to look at
class AlwaysVoid, // for void_t
class Current, // pack containing the types we are testing next
class T, // next type to add to pack if test fails
class... Rest> // remaining types
struct find_viable
: find_viable<A, AlwaysVoid, typename Current::template append<T>, Rest...> {};
// picked if A<Ts...> is well-formed
template<template<class...> class A, class... Ts, class T, class...Rest>
struct find_viable<A, void_t<A<Ts...>>, pack<Ts...>, T, Rest...> {
using type = A<Ts...>;
};
// Finds the shortest prefix of Ts... such that A<prefix...> is well formed.
// the extra void at the end is slightly hackish - find_viable only checks
// proper prefixes, so we add an extra dummy type at the end.
template<template<class...> class A, class... Ts>
using find_viable_t = typename find_viable<A, void, pack<>, Ts..., void>::type;
Then use it:
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts,
template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<find_viable_t<Templates, Ts...>...>;
};
I won't write it all, but I'll do a walkthrough.
template<template<class...>class Z>
struct z_template{
template<class...Ts>using result=Z<Ts...>;
};
this lets you manipulate templates like types.
template<template<class...>class Z, class...Ts>
using can_apply = /* ... */;
this asks the question "is Z<Ts...> valid? If so, return true_type".
template<class...>struct types{using type=types;};
this is a light-weight template pack.
template<template<class...>class Z, class types>
using apply = /* ... */;
this takes a template Z, and a types<Ts...>, and returns Z<Ts...>.
template<class Z, class types>
using z_apply = apply<typename Z::template result, types>;
this does it with a z_template argument instead of a template one.
Working on types as often as possible makes many kinds of metaprogramming easier.
Next we write
template<class types>using pop_front = // ...
template<class types>using reverse = // ...
template<class types>using pop_back = reverse<pop_front<reverse<types>>>;
which remove elements from the front and back of types.
template<class Result>
struct z_constant {
template<class...Ts>using result=Result;
};
This finds the longest prefix of types that can be passed to Z::template result<???> validly. Some work to avoid passing too-short sequences is done, in case they fail "late":
template<class Z, class types>
struct z_find_longest_prefix :
std::conditional_t<
can_apply< z_apply, Z, types >{},
z_constant<types>,
z_template<longest_prefix>
>::template result<Z, pop_back<types>>
{};
template<class Z, class types>
using z_find_longest_prefix_t = typename z_find_longest_prefix<Z,types>::type;
struct empty_t {};
template<class Z>
struct z_find_longest_prefix<Z,types<>>:
std::conditional_t<
can_apply< Z::template result >,
types<>,
empty_t
>
{};
find_longest_prefix will fail to compile if there is no valid prefix.
template<class Z, class types>
using z_apply_longest_prefix_t =
z_apply< Z, z_find_longest_prefix_t<Z, types> >;
template<class src, template<class...>class... Zs>
struct use_template;
template<class src, template<class...>class... Zs>
using use_template_t = typename use_template<src, Zs...>::type;
template<template<class...>class Z0, class...Ts, template<class...>class... Zs>
struct use_template< Z0<Ts...>, Zs... > {
using type=Z0<
z_apply_longest_prefix_t<
z_template<Zs>, types<Ts...>
>...
>;
};
and up to typos and similar problems, done.
Can be cleaned up lots. Probably the entire z_ subsystem isn't needed. I just tend to run towards it whenever doing serious metaprogramming, because suddenly any metafunction I write that applies to types now can be used on templates.
reverse takes a bit of work to do efficiently.
can_apply is available in many of my posts.
pop_front is trivial.
apply should be easy.
This looked fun so I tried my hand at it; I'm not claiming this is in any way better than the existing answers, but maybe some people will find it clearer:
namespace detail {
template<typename...>
using void_t = void;
template<template<typename...> class, typename, typename, typename EnableT = void>
struct fill_template;
template<template<typename...> class TemplateT, typename TupleT, std::size_t... Is>
struct fill_template<
TemplateT, TupleT, std::index_sequence<Is...>,
void_t<TemplateT<std::tuple_element_t<Is, TupleT>...>>
>
{
using type = TemplateT<std::tuple_element_t<Is, TupleT>...>;
};
template<
template<typename...> class TemplateT, typename TupleT,
std::size_t I, std::size_t N
>
struct fill_template_dispatcher
{
template<typename T, typename = typename T::type>
static constexpr std::true_type test(int) { return {}; }
template<typename T>
static constexpr std::false_type test(long) { return {}; }
using candidate_t = fill_template<TemplateT, TupleT, std::make_index_sequence<I>>;
using type = typename std::conditional_t<
test<candidate_t>(0),
candidate_t,
fill_template_dispatcher<TemplateT, TupleT, I + 1, I != N ? N : 0>
>::type;
};
template<template<typename...> class TemplateT, typename TupleT, std::size_t I>
struct fill_template_dispatcher<TemplateT, TupleT, I, 0>
{
static_assert(I != I, "tuple contains insufficient types to satisfy all templates");
};
} // namespace detail
template<typename TupleT, template<typename...> class... TemplateTs>
struct template_pack
{
static_assert(std::tuple_size<TupleT>::value, "tuple must not be empty");
using type = std::tuple<typename detail::fill_template_dispatcher<
TemplateTs, TupleT, 0, std::tuple_size<TupleT>::value
>::type...>;
};
template<typename TupleT, template<typename...> class... TemplateTs>
using template_pack_t = typename template_pack<TupleT, TemplateTs...>::type;
Online Demo
Note that for variadic class templates this will pass as few types as possible (see D in the test); for an implementation that passes as many types as possible instead, see that variation of the code here (only two lines are changed in the implementation).

Define a struct which is derived from true_type whenever a given SFINAE-able constructor would be taken

Please consider the following code snippet:
template<typename T, class Tuple>
class vector
{
using size_type = typename Tuple::size_type;
template<typename... Elements,
typename = decltype(std::declval<Tuple>().reserve(size_type()))>
typename = decltype(std::declval<Tuple>().push_back(T())),
vector(Elements&&... elements)
{ /* ... */ }
};
I want to define a nested struct supports_reserve_push_back which is derived from std::true_type whenever the constructor above would be enabled (and which is derived from std::false_type in the other case).
How can I do this?
I've modified the code to make it build. And implemented the trait you requested, to the best of my understanding.
#include <iostream>
#include <type_traits>
#include <vector>
#include <map>
namespace example {
template<typename...>
using void_t = void;
template<typename T, class Tuple>
struct vector {
using size_type = typename Tuple::size_type;
using tuple_type = Tuple;
using elem_type = T;
template<typename... Elements>
vector(Elements&&... elements)
{ /* ... */ }
};
template <class T, typename = void>
struct supports_reserve_push_back : std::false_type {};
template <class Vec>
struct supports_reserve_push_back<Vec, void_t<
decltype(std::declval<typename Vec::tuple_type>().reserve(typename Vec::size_type())),
decltype(std::declval<typename Vec::tuple_type>().push_back(typename Vec::elem_type())) >
>
: std::true_type {};
}
int main() {
std::cout
<< example::supports_reserve_push_back<example::vector<int, std::vector<int>>>::value
<< '\n'
<< example::supports_reserve_push_back<example::vector<int, std::map<int, int>>>::value;
return 0;
}
A few thing to note:
The way you wrote the c'tor originally caused a hard error when instantiating the class in the negative case. That's why I removed the chcck from the c'tor.
I'd suggest you define the type traits first, and use them to enable your c'tors.
namespace details{
template<template<class...>class Z,class,class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z,class...Ts>
struct can_apply<Z,std::void_t<Z<Ts...>,Ts...>:
std::true_type{};
}
template<template<class...>class Z,class... Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
Wrap up your decltypes into template usings and do some && and done.
There is also a std experimental similar to above.
template<class T, class U>
using push_back_r = decltype(std::declval<T>().push_back(std::declval<U>()));
template<class T>
using reserve_r = decltype(std::declval<T>().reserve(1));
template<class T, class U>
constexpr can_apply<push_back_r,T,U> has_push_back={};
template<class T>
constexpr can_apply<reserve_r,T> has_reserve={};
template<bool b>using bool_t=std::integral_constant<bool,b>;
template<class T,class U>
constexpr bool_t<has_push_back<T,U>&&has_reserve<T>>
efficiently_fillable_with = {};
Then efficiently_fillable_with<T,U> is true type iff you can reserve space with T and then push Us into it. The r/l value category of T and U is preserved: if you want to know about filling an non-cinstant lvalue of T with rvalue Us:
efficiently_fillable_with<T&,U>
If you want to fill with U const& instead of rvalues, pass U const&.
#include <type_traits>
#include <utility>
template <typename...>
using void_t = void;
template <typename AlwaysVoid, template <typename...> class Operation, typename... Args>
struct detect_impl : std::false_type {};
template <template <typename...> class Operation, typename... Args>
struct detect_impl<void_t<Operation<Args...>>, Operation, Args...> : std::true_type {};
template <template <typename...> class Operation, typename... Args>
using detect = detect_impl<void_t<>, Operation, Args...>;
template <typename T, typename Sz>
using has_reserve = decltype(std::declval<T>().reserve(std::declval<Sz>()));
template <typename T, typename U>
using has_push_back = decltype(std::declval<T>().push_back(std::declval<U>()));
template <typename Tuple, typename T, typename size_type>
constexpr bool supports_reserve_push_back = detect<has_reserve, Tuple, size_type>{} && detect<has_push_back, Tuple, T>{};
Test:
template <typename T, class Tuple>
class vector
{
public:
using size_type = typename Tuple::size_type;
template <typename... Elements, typename U = Tuple,
std::enable_if_t<supports_reserve_push_back<U&, T, size_type>, int> = 0>
vector(Elements&&... elements)
{
}
template <typename... Elements, typename U = Tuple,
std::enable_if_t<!supports_reserve_push_back<U&, T, size_type>, int> = 0>
vector(Elements&&... elements)
{
}
};
DEMO
I would try the following approach:
Have your vector template class inherit from a superclass, like this:
template<typename T, class Tuple> class vector
: public supports_reserve_push_back_impl<
vector_has_default_constructor<T, Tuple>::value() > {
// ...
}
Now, define a vector_has_default_constructor template class that takes the same template parameters:
template<typename T, class Tuple> class vector_has_default_constructor {
public:
// ...
};
In vector_has_default_constructor:
Define a constexpr bool value() method with the same exact signature as the vector's constructor. This constexpr method returns true.
Define an overload constexpr bool value() with a ... signature, which should have lower priority in the overload resolution. This constexpr returns false.
Now, this situation is reduced to defining two trivial specializations, supports_reserve_push_back_impl<true> and supports_reserve_push_back_impl<false>.
supports_reserve_push_back_impl<true> contains your desired supports_reserve_push_back value, and is inherited by your vector.
supports_reserve_push_back_impl<false> is empty.

How to test if type is specialization of template with non-type parameters?

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" );
}

Ignoring duplicate explicit instantiations of template classes in C++

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);