Search code examples
c++c++17parameter-pack

Is there a less verbose way to initialize a tuple of heterogenous tuples (or similarly templated types)


I'm wanting to construct a tuple of hetergenous tuples but this seemingly requires an explicit constructor call for each member:

auto foo = std::tuple{
    std::tuple{1, "foo", 3},
    std::tuple{1, 2, 3},
    std::tuple{1.0, 2.0},
    // many more CTAD constructor calls
}

Basically I'm asking if a helper function, etc could make this look like the below example with homogenous inner types, where the inner constructor doesn't need to be called each time.

auto bar = std::vector<std::tuple<int, std::string_view, int>>{
    {1, "foo", 3},
    {4, "bar", 6},
    {1, "baz", 3},
    // ...
};

So that I can hopefully have something like this:

auto baz = make_tuple_of_tuples(
    {1, "foo", 3},
    {1, 2, 3},
    {1.0, 2.0},
    // ...
);

If this is impossible with std::tuples, what about with a custom type where we control the constructors and deduction guides?


Solution

  • C++ would require some way of dynamically specifying sets of variadic template arguments (something like template <typename <typename ... T> ... U> or whatever the syntax should look like), which is not supported, so out of luck so far.

    You can achieve something similar with a bit of pre-processor magic, though:

    #define TUPLE_OF_TUPLES(...) EVAL(CONCAT(TUPLE_OF_TUPLES_, COUNT(__VA_ARGS__))(__VA_ARGS__))
    
    #define EVAL(...) __VA_ARGS__
    #define COUNT(...) 2 // TODO: appropriate counting macro!
    #define CONCAT(X, Y) CONCAT_(X, Y)
    #define CONCAT_(X, Y) X ## Y 
    
    #define TUPLE_OF_TUPLES_2(X, Y) EVAL(std::tuple(std::tuple X, std::tuple Y));
    // TODO: analogously macros for 1, 3, 4, 5, ... arguments
    

    Counting macro arguments is discussed e.g. here (though in given case you only can use the pre-processor solution!).

    Demonstration on godbolt, though only tested for GCC, other compilers (especially MSVC) might or not need adjustments...

    Be aware, though, that pre-processor magic is more error prone than standard C++, so use with caution.