Search code examples
c++variadic-templates

How to dynamically create aggregate instances with varying base classes?


I need to dynamically create instances of an Aggregate class in C++ which inherits from multiple base classes, where the combination of base classes can vary based on runtime conditions. I want to avoid pre-populating all possible combinations.

Here's a simplified version of what I'm trying to achieve

I have several base classes:

class CharBase
{
    CharBase() { std::cout << "CharBase\n"; }
};

class UnsignedCharBase
{
    UnsignedCharBase() { std::cout << "UnsignedCharBase\n"; }
};

class ShortBase
{
    ShortBase() { std::cout << "ShortBase\n"; }
};

class IntBase
{
    IntBase() { std::cout << "IntBase\n"; }
};

I use the following Aggregate class template to inherit from multiple base classes

template<typename... Bases>
class Aggregate : Bases…
{
    Aggregate() { std::cout << "Aggregate\n"; }
};

My goal is to create instances of the Aggregate classes on demand based on runtime conditions. The expected result is to create an Aggregate instance as if I hardcoded it, for example:

new Aggregate<CharBase, UnsignedCharBase, ShortBase>
new Aggregate<UnsignedCharBase, IntBase>

Solution

  • One way to turn runtime value to compile value is to use std::variant.

    For your case, I would use std::tuple to store the result for "optional" type.

    template <typename T>
    std::variant<std::tuple<>,
                 std::tuple<std::type_identity<T>>>
    AsOptionalTypeVar(bool b)
    {
        if (b) {
            return std::tuple<std::type_identity<T>>{};
        } else {
            return std::tuple<>{};
        }
    }
    

    An Helper trait to transform a tuple into Aggregate

    template <typename T> struct to_aggregate;
    template <typename... Ts> struct to_aggregate<std::tuple<std::type_identity<Ts>...>>
    {
        using type = Aggregate<Ts...>;
    };
    

    Then you might let std::visit does the Cartesian product

    std::visit(
        [](auto... bases){
            using all_base = decltype(std::tuple_cat(bases...));
            using aggregate = typename to_aggregate<all_base>::type;
    
            // Use aggregate type as you want
        },
        AsOptionalTypeVar<CharBase>(useCharBase),
        AsOptionalTypeVar<UnsignedCharBase>(useUnsignedCharBase),
        AsOptionalTypeVar<ShortBase>(useShortBase),
        AsOptionalTypeVar<IntBase>(useIntBase)
    );
    

    Demo