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

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

本文关键字:缓存 sizeof typeid 多类型 创建 custom      更新时间:2023-10-16

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() ?
它必须稍后通过"custom typeid"[#1]返回正确的值[#2]。

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. 不允许手动分配id-integer的E0,E1,E2,...,因为低维护性。
  2. 用户无需手动"注册"E0,E1,E2,...CCollection
    对于新类型的元素,例如E1000,不需要做任何修改。
    …也就是说,CCollection应该在E1000上"刚好工作"。
  3. 最高性能,例如:-
    • 虚拟通话应该避免,除非完全不可避免。
  4. 使用constexpr
  5. 函数内部的静态变量是不好的
  6. 不要修改E0,E1,E2,...的代码

设计的灵活性

  • 如果CCollection的函数从未被某个E-X调用,
    E-X的管理可以省略(由您决定)。
  • 只有一个CCollection实例
  • clue1: Template似乎是最有前途的路径,但我不确定。

    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;
    };
    
    演示

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

    #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类被设计成如果你这样做,它会从sizeOf返回0。

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