2

Recently I've come across a problem whose only solution I can think of is using macros, which although fine for some purposes, I think make this an ugly solution.

The problem is when declaring a class whose template parameters change all the time as more code is added and new functionality is required.

I realize that this might mean the code design isn't done well if the number of templates in a class keeps changing, but even so I'd like to know if there is any solution.

This is an example of the code in Test.hpp, the declaration of the class and prototype of all it's methods.

template<typename A,
         typename B,
         typename C>
struct Test 
{
    void do_something_1();
    void do_something_2();
    void do_something_3();
}

In Test.cpp, we'd implement all the methods as such

template<typename A,
         typename B,
         typename C>
Test<A,B,C>::do_something_1() { /* ... */ }

template<typename A,
         typename B,
         typename C>
Test<A,B,C>::do_something_2() { /* ... */ }

template<typename A,
         typename B,
         typename C>
Test<A,B,C>::do_something_3() { /* ... */ }

Which you can see that we're repeated the template part a lot, and if we were to add a typename D do the class we'd have to change it in all of the methods of Test.cpp, and so using macros we could define it all in a macro and use it, but then the person reading the code might not understand what exactly is happening there or how the macro looks expanded.

It's also possible to use typename... Args to get all of them and then somehow process them inside the class, but this is very clunky as getting the Nth type from Args isn't very easy and it doesn't provide the end user with how many types should be used nor in which order aside from inside the documentation and if we wanted to separate the template parameters into two categories, say one for what types to store and one for which types to transform to in a class that somehow processes types, with two typename... Args there is no easy way to say where the first one ends and where the second one starts.

The only approach I could think of is to add a packed-struct, that is a struct of types, and use it as such:

In Test.hpp

template<typename A_,
         typename B_,
         typename C_>
struct packed_struct
{
    using A = A_;
    using B = B_;
    using C = C_;
};

template<typename packed_struct_type>
struct Test
{
    using A = typename packed_struct_type::A;
    using B = typename packed_struct_type::B;
    using C = typename packed_struct_type::C;

    void do_something_1();
    void do_something_2();
    void do_something_3();
};

And in Test.cpp we'd just declare one typename, packed_struct_type, and we could also add a parameter easily at any time, this feels like the best solution because then we can enforce how many template parameters there should be and the name of each so the user can't mix them up.

With proposal N3728 and proposal N1603 we could declare a typename... Args, the user could instantiate it with Test< <int, char, double> > and we could retrieve each type more easily, but they weren't approved.

Of course the best way would probably be for first class language support for packed structs, that we could use instead of typename to enforce that pack, essentially what proposal N3728 does with the <int, char, double> syntax but using packed_struct Args instead of typename... Args to be able to easily retrieve each type with it's name instead of guessing which type is the first type and the 2nd one, and having the possibility of the user also mixing up the types in their declaration. But I cannot find any proposal for this specific funtionality, and so the best way I can think of achieving this is with the packed_struct solution I gave above.

Is there any better solution for this problem or any proposal that gives a solution that is scheduled for c++2a (or possibly later, but not rejected yet)?

Filipe Rodrigues
  • 1,843
  • 2
  • 12
  • 21
  • It should be `using A = typename packed_struct_type::A;`. – Evg Sep 06 '18 at 09:04
  • @Evg I'd forgotten, thanks, although I should also add the point that with a language support packed_struct since the type names are constant there should be no need for typename in that context. – Filipe Rodrigues Sep 06 '18 at 09:07
  • 1
    *"In Test.cpp, we'd define all the methods as such"*, you define template functions in cpp file ? read [why-can-templates-only-be-implemented-in-the-header-file](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file). – Jarod42 Sep 06 '18 at 09:13
  • @Jarod42 Sorry, I used the word define to mean implement accidently, what I meant is the second solution by the top answer used in the your link. – Filipe Rodrigues Sep 06 '18 at 09:15

0 Answers0