如何动态创建具有不同基类的聚合实例?

问题描述 投票:0回答:1

我需要在 C++ 中动态创建

Aggregate
类的实例,该类继承自多个基类,其中基类的组合可以根据运行时条件而变化。我想避免预先填充所有可能的组合。

这是我想要实现的目标的简化版本

我有几个基类:

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"; }
};

我使用以下

Aggregate
类模板从多个基类继承

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

我的目标是根据运行时条件按需创建

Aggregate
类的实例。预期结果是创建一个
Aggregate
实例,就像我对其进行硬编码一样,例如:

new Aggregate<CharBase, UnsignedCharBase, ShortBase>
new Aggregate<UnsignedCharBase, IntBase>
c++ variadic-templates
1个回答
0
投票

这是我设法想出的解决方案。这里唯一的限制是

highestSetBit
。我试图摆脱它,但我做不到,如果有人能解决这个问题,我将不胜感激。

#include <array>
#include <iostream>
#include <memory>
#include <tuple>

class IAggregate
{
public:
    virtual ~IAggregate() = default;
};

template<typename... Bases>
class Aggregate : public IAggregate, public Bases...
{
public:
    Aggregate() { std::cout << "Aggregate\n"; }
    virtual ~Aggregate() override = default;
};

class CharBase
{
public:
    static constexpr int Mask = 1 << 0;
    CharBase() { std::cout << "CharBase display\n"; }
};

class UnsignedCharBase
{
public:
    static constexpr int Mask = 1 << 1;
    UnsignedCharBase() { std::cout << "UnsignedCharBase display\n"; }
};

class ShortBase
{
public:
    static constexpr int Mask = 1 << 2;
    ShortBase() { std::cout << "ShortBase display\n"; }
};

class IntBase
{
public:
    static constexpr int Mask = 1 << 3;
    IntBase() { std::cout << "IntBase display\n"; }
};

// List of all base classes
using AllBases = std::tuple<CharBase, UnsignedCharBase, ShortBase, IntBase>;

constexpr int highestSetBit(const int mask) // This is the only limitation in this design
{
    for (int i = 31; i >= 0; --i)
        if (mask & (1 << i))
            return i;
    return -1; // should never reach here for valid masks
}

template<int Mask, typename Tuple, typename Enable = void, typename... Types>
struct TypeExtractor;

template<int Mask, typename Tuple, typename... Types>
struct TypeExtractor<Mask, Tuple, std::enable_if_t<Mask == 0>, Types...>
{
    using type = Aggregate<Types...>;
};

template<int Mask, typename Tuple, typename... Types>
struct TypeExtractor<Mask, Tuple, std::enable_if_t<Mask != 0>, Types...>
{
    static constexpr int highestBit = highestSetBit(Mask);
    using BaseType                  = std::tuple_element_t<highestBit, Tuple>;
    using type                      = typename TypeExtractor<Mask & ~(1 << highestBit), Tuple, void, Types..., BaseType>::type;
};

template<int Mask>
using ExtractTypes = typename TypeExtractor<Mask, AllBases>::type;

using AggregatePtr = std::unique_ptr<IAggregate>;

template<int Mask>
AggregatePtr createAggregateInstance()
{
    return std::make_unique<ExtractTypes<Mask>>();
}

// Helper to create a map of factory functions
template<int... Masks>
constexpr auto createFactoryMap(std::integer_sequence<int, Masks...>)
{
    using FuncType = AggregatePtr (*)();
    return std::array<FuncType, sizeof...(Masks)> {{&createAggregateInstance<Masks>...}};
}

// The factory map consist of AllBases tuple
constexpr auto factoryMap = createFactoryMap(std::make_integer_sequence<int, 1 << std::tuple_size_v<AllBases>> {});

AggregatePtr createAggregate(int mask)
{
    if (mask < factoryMap.size())
        return factoryMap[mask]();
    return nullptr;
}

int main()
{
    for (int i = 1; i <= 3; ++i)
    {
        auto instance = createAggregate(i);
        if (!instance)
            std::cout << "Invalid combination for input: " << i << "\n";
        std::cout << "\n";
    }

    return 0;
}

实例

© www.soinside.com 2019 - 2024. All rights reserved.