为许多类型创建 "custom typeid" 并缓存它们的 sizeof()

Create "custom typeid" for many types AND cache their sizeof()

有很多类型的元素E0,E1,...,它们都来自Element

class Element{  public: int id=0; } //"Element" can be edited
class E0: public Element{ ... }
class E1: public Element{ ... }
...
class E999: public Element{ ... }

如何为 E0,E1,... 缓存 sizeof()
它必须 return 正确的值 [#2] 稍后 "custom typeid" [#1]。

class CCollection{
    template<class EX> EX* create(){ //(Edit)
        //all EX* is created by this function
    }
    /** user only pass EX*, never pass Element* */  (Edit)
    int toId(Element* element){//[#1] should replaced by template?
          //....... return id, e.g. 0 for E0, 1 for E2, 2 for E1, ...  
          // can be in any order, but must unique and packed ........ 
    }
    int sizeOf(int id){//[#2]
        //....... return size of EX
        // must consistent with toId() ,
        // e.g. sizeof(E0) for 0, sizeof(E2) for 1, sizeof(E1) for 2 ........ 
    }
    //....... other fields / functions, if necessary ............


}

如何实现?

要求:

  1. 不允许手动分配 E0,E1,E2,... 的 id 整数,因为可维护性低。
  2. 用户不必手动 "register" E0,E1,E2,...CCollection
    当新类型的元素时,例如E1000,不用修改。
    ..... 即 CCollection 对于 E1000 应该是 "just work"。
  3. 最高性能,例如:-
    • 应避免虚拟调用,除非完全无法避免。
    • 尽可能使用 constexpr
    • 函数内部的静态变量不好。
  4. 不要修改 E0,E1,E2,...
  5. 的代码

设计的灵活性

  1. 如果 CCollection 的函数从未被某个 E-X
    调用 E-X 的管理可以省略(由您决定)。
  2. 只有 1 个 CCollection 实例。

clue1: 模板似乎是最有希望的路径,但我不确定。

clue2:(编辑)我计划的这个片段可能会有所帮助:-

template<class EX> int toId(EX* element){//[#1] 
    // register something?
}

如你所料,真正的问题远不止于此,
但这是我整个拼图中唯一缺少的拼图。

虽然我想要代码,但非常感谢您的指导。

我认为以下不会违反您的任何规则:

struct CCollection
{
    template <typename T>
    std::size_t toId() {
        auto it = info.find(typeid(T));
        if (it == info.end()) {
            it = info.insert({typeid(T), {count++, sizeof(T)}}).first;
        }
        return it->second.id;   
    }

    std::size_t sizeOf(const Base& base) const {
        const auto& data = info.at(typeid(base));
        return data.size;   
    }

    std::size_t count = 0;
    std::unordered_map<std::type_index, Data> info;
};

Demo

如果您可以在 Element 和每个 EX 之间添加一个中间 class,这是一个可能的解决方案(有一个最小的工作示例):

#include<cstddef>
#include<memory>
#include<vector>
#include<cassert>

struct Element {
    static std::size_t counter;
};

std::size_t Element::counter = 0;

template<typename>
struct ElementType: Element {
    static const int type;
};

template<typename T>
const int ElementType<T>::type = Element::counter++;

struct E0: ElementType<E0> {};
struct E1: ElementType<E1> { int i; };
struct E2: ElementType<E2> {};

class CCollection {
    void ensure(std::size_t type) {
        if(!(type < sizes.size())) {
            sizes.resize(type+1, 0);
        }
    }

    template<typename EX>
    void ensure() {
        ensure(EX::type);
        sizes[EX::type] = sizeof(EX);
    }

public:
    template<class EX>
    std::shared_ptr<EX> create() {
        ensure<EX>();
        return std::make_shared<EX>();
    }

    template<typename EX>
    std::size_t toId() {
        ensure<EX>();
        return EX::type;
    }

    std::size_t sizeOf(std::size_t type) {
        ensure(type);
        return sizes[type];
    }

private:
    std::vector<std::size_t> sizes;
};

int main() {
    CCollection coll;
    assert(coll.toId<E0>() != coll.toId<E1>());
    assert(coll.toId<E0>() != coll.toId<E2>());
    assert(coll.toId<E1>() != coll.toId<E2>());
    assert(coll.sizeOf(0) == sizeof(E0));
    assert(coll.sizeOf(1) == sizeof(E1));
    assert(coll.sizeOf(2) == sizeof(E2));
    assert(coll.sizeOf(0) != coll.sizeOf(1));
    // ...
}

如果尝试在不使用 create 的情况下创建 EX 的实例,就会出现示例代码中的问题。
不管怎样:

  • 你说它永远不应该发生。
  • 您可以强制使用该功能。
  • CCollection class 被设计成 returns 从 sizeOf 0 如果你这样做的话。

也就是说,它至少可以作为更详细的生产代码的基础。