4

I have a lot of C++ classes that use the same list of template parameters

template<typename T, typename Index, typename Bool, typename Data, Index n_x, Index n_u, Index n_c, Index n_w>
class A {
   ...
};

template<typename T, typename Index, typename Bool, typename Data, Index n_x, Index n_u, Index n_c, Index n_w>
class B {
   ...
};

template<typename T, typename Index, typename Bool, typename Data, Index n_x, Index n_u, Index n_c, Index n_w>
class C {
   ...
};

You get the idea. Then I instantiate them like

A<T, Index, Bool, Data, n_x, n_u, n_c, n_w> a;
B<T, Index, Bool, Data, n_x, n_u, n_c, n_w> b;
C<T, Index, Bool, Data, n_x, n_u, n_c, n_w> c;

Is there a way to somehow create an alias for this bundle of template parameters so that I don't have to keep re-typing the argument list?

I have something like this in mind...

using Params = T, Index, Bool, Data, n_x, n_u, n_c, n_w;
A<Params> a;
B<Params> b;
C<Params> c;

I realize that I could create a separate class which just defines types, and use that. But I am wondering if there is a way of doing this without defining a new class.

EDIT

I do not want to use macros.

I also do not want to use defaults because that would require ensuring that the defaults are uniform across a bunch of files. I realize that I could define a new header of defaults and just include that in all of the files, but that just seems like bad programming.

11
  • 1
    use default parameters Commented Mar 14, 2018 at 11:29
  • 1
    Create a macro... Commented Mar 14, 2018 at 11:30
  • I do not want to use defaults because then I would have to define the defaults in every file. Commented Mar 14, 2018 at 11:30
  • 5
    If you need something like that, then I would argue that the desing, or the implementation of that design, being flawed. Commented Mar 14, 2018 at 11:30
  • 2
    @bremen_matt What you want is impossible. Only way is to specialize it for a type list. Commented Mar 14, 2018 at 11:34

3 Answers 3

4

Not exactly what you asked but not so different...

But require a little work.

You can solve with a struct, foo, with a double layer of template management.

template <typename T, typename Index, typename Bool, typename Data,
          Index I1, Index I2, Index I3, Index I4>
struct foo
 { 
   template <template <typename, typename X, typename, typename, X, X, X, X>
                       class Cont>
   using type = Cont<T, Index, Bool, Data, I1, I2, I3, I4>;
 };

A first layer, the struct layer, with the types/values you want to fix (T, Index, Bool, Data, n_x, n_u, n_c, n_w, in your example).

A second layer, the using layer, with the variable template element (A, B and C, in your example.

You can also add a using alias foot_t to simplify the use

template <template <typename, typename X, typename, typename, X, X, X, X>
                    class Cont, typename C>
using foo_t = typename C::template type<Cont>;

Now you can fix types and values (the first layer) with a using

using f = foo<T, Index, Bool, Data, n_x, n_u, n_c, n_w>;

and declare variable activating second layer using foo_t

foo_t<A, f> a;
foo_t<B, f> b;
foo_t<C, f> c;

The following is a full working example

#include <vector>
#include <iostream>

template <typename T, typename Index, typename Bool, typename Data,
          Index n_x, Index n_u, Index n_c, Index n_w>
class A { };

template <typename T, typename Index, typename Bool, typename Data,
          Index n_x, Index n_u, Index n_c, Index n_w>
class B { };

template <typename T, typename Index, typename Bool, typename Data,
          Index n_x, Index n_u, Index n_c, Index n_w>
class C { };

template <typename T, typename Index, typename Bool, typename Data,
          Index I1, Index I2, Index I3, Index I4>
struct foo
 { 
   template <template <typename, typename X, typename, typename, X, X, X, X>
                       class Cont>
   using type = Cont<T, Index, Bool, Data, I1, I2, I3, I4>;
 };

template <template <typename, typename X, typename, typename, X, X, X, X>
                    class Cont, typename C>
using foo_t = typename C::template type<Cont>;


int main ()
 {
   using T     = float;
   using Index = std::size_t;
   using Bool  = bool;
   using Data  = std::vector<std::string>;

   constexpr std::size_t n_x { 0U };
   constexpr std::size_t n_u { 1U };
   constexpr std::size_t n_c { 2U };
   constexpr std::size_t n_w { 3U };

   using f = foo<T, Index, Bool, Data, n_x, n_u, n_c, n_w>;

   foo_t<A, f> a;
   foo_t<B, f> b;
   foo_t<C, f> c;

   static_assert( std::is_same<decltype(a),
                     A<T, Index, Bool, Data, n_x, n_u, n_c, n_w>>{}, "!" );
   static_assert( std::is_same<decltype(b),
                     B<T, Index, Bool, Data, n_x, n_u, n_c, n_w>>{}, "!" );
   static_assert( std::is_same<decltype(c),
                     C<T, Index, Bool, Data, n_x, n_u, n_c, n_w>>{}, "!" );
 }
Sign up to request clarification or add additional context in comments.

3 Comments

Looks good. Probably about the closest one can get to what I had in mind.
This code is overcomplicated. What you need is only a composer, which can accept a container and a type then produce a composed type. template<class T, class Index, Index n_x, template<class, class, Index>class Container> struct Composer { using type = Container<T, Index, n_x>; }; template<template<class, class, size_t>class Container> using float_sizet_0_composer = typename Composer<float, size_t, 0LU, Container>::type; static_assert( std::is_same_v<float_sizet_0_composer<A>, A<float, size_t, 0LU>> );
@liliscent - in this way you avoid the second layer... nice... but (IMHO) is significantly different from my solution; i think you should propose it as an alternative answer.
1

You could replace the alias by a define, not perfect, but easy solution and it works.

#define PARAMS T, Index, Bool, Data, n_x, n_u, n_c, n_w
A<PARAMS> a;
B<PARAMS> b;
C<PARAMS> c;

Note: no ; at the end of the define.

Comments

1
template <typename T, typename Index, typename Bool, typename Data, Index n_x, Index n_u, Index n_c, Index n_w>
class A
{
 ...
};


template <template<typename, typename Index, typename, typename, Index, Index, Index, Index> typename T>
using TypeInject = T<float, std::size_t, bool, std::string, 1, 2, 3, 4>;

use as follow:

TypeInject <A> a;
TypeInject <B> b;
TypeInject <C> c;

The problem now is how to reduce template <typename T, typename Index, typename Bool...>. Update me if someone gets a solution.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.