Search code examples
c++variant

C++ separate structs in to "groups" but allow `std::variant` to see all structs across the "groups"


Please see code below.

I have 3 structs (A, B and C) defined across 2 groups (in reality I have many more). The groups are to provide context within the real-world domain i'm modelling. These groups are implemented via two std::variants (Group1 and Group2).

I have a function which can return any of the underlying structs.

To provide a single return type for the function I create a third std::variant (AllGroups) from my initial two variants (Group1 and Group2).

However, my problem is AllGroups cannot see A, B or C, only Group1 and Group2.

Is there a way to keep the structs defined with some sort of group but also collapse them in to one std::variant for the function return type?

#include <variant>
#include <iostream>

struct A{}; struct B{};
using Group1 = std::variant<A, B>;

struct C{};
using Group2 = std::variant<C>;

using AllGroups = std::variant<Group1, Group2>;

AllGroups getObject()
{
    A a;     // Simple logic for the general question
    AllGroups ret(a);
    return ret;
}

int main()
{
    AllGroups ret = getObject();

    // Here i'd like to check for the individual structs, not Group1 and Group2
    // However I get a compiler error because the AllVariant can only see Group1 and Group2

    std::cout << std::holds_alternative<A>(ret) << std::endl;
}

Solution

  • Here is how you can do a trait which produces a type you want - a combination of variant's alternative types. Unlike the other answers, it works with more than two variants being joined ;)

    #include <variant>
    
    
    template<class Variant1, class Variant2, class... Variants> 
    struct variant_cat;
    
    template<class... Var1, class... Var2>
    struct variant_cat<std::variant<Var1...>, std::variant<Var2...>>
    {
        using type = std::variant<Var1..., Var2...>;
    };
    
    template<class... Var1, class... Var2, class... Variants>
    struct variant_cat<std::variant<Var1...>, std::variant<Var2...>, Variants...>
    {
        using type = typename variant_cat<std::variant<Var1..., Var2...>, Variants...>::type;
    };
    
    template<class... A>
    using variant_cat_t = variant_cat<A...>::type;
    
    // Usage:
    variant_cat_t<std::variant<int, char>, std::variant<double, float>, std::variant<std::nullptr_t>> Result;