C++ 在运行时获取带有 ID 的对象

C++ Getting objects with IDs at runtime

假设我们有以下对象:

struct A{
    static const int ID = 1;
};

struct B{
    static const int ID = 2;
};

struct C{
    static const int ID = 3;
};

struct D{
    static const int ID = 4;
};

struct Collection{
    std::vector<A> o1;
    std::vector<B> o2;
    std::vector<C> o3;
    std::vector<D> o4;
};

Collection collection;

我想要做的是获取对 Collection 的某些 vector 的引用。应该有三种不同的方法来检索这些:

  1. vector 的类型,例如collection.get<A>();

  2. 通过编译时的 ID,它由 vector 持有的每个对象定义,例如collection.get<4>();

  3. 通过运行时的 ID,例如collection.get(id);

案例 1 很简单,因为它可以通过 T::ID 转换为案例 2。案例 2 可以通过模板特化来实现(尽管如果我有很多对象,它看起来很难看)。 案例 3 制造了很多麻烦。如果没有一些巨大的 ifswitch 语句,它几乎是不可能的,更不用说推断 return 类型了。

我的问题是:

有些事情你可以做,有些事情你永远做不到。你要的不是那么简单,还需要努力

1 和 2 很难,但可以实现。首先,create a static map of all type-IDs, then use the static result to choose the type.

现在 3...好吧...很抱歉告诉你这是不可能的!提供一个反例来证明我在说什么:您永远不能创建一个在运行时选择 return 类型的函数。但是...

有些事情你可以做,这基本上是 LLVM 人为摆脱 dynamic_cast 而做的事情,但它的代码太多了。

首先,case一个基本抽象class,并使所有这些classes派生自它,并使其包含ID:

struct Base{
    virtual int getClassID() = 0;
    static Base* CreateObject(int ID) //consider using shared_ptr here
        {
        switch(ID) {
        case A::ID:
            return new A;
        }            
        case B::ID:
            return new B;
        default:
            return nullptr;
        }
        //...
    }
}

struct A : public Base {
    static const int ID = 1;
    int getClassID() override {
        return ID;
    }
};

struct B : public Base {
    static const int ID = 2;
    int getClassID() override {
        return ID;
    }
};

struct C : public Base {
    static const int ID = 3;
    int getClassID() override {
        return ID;
    }
};

struct D : public Base {
    static const int ID = 4;
    int getClassID() override {
        return ID;
    }
};

现在,要实例化一个新的 class,您需要使用 Base class。

Base* c = Base::CreateObject(3); //object of C

现在,您终于创建了自己的魔法转换运算符:

template <typename T>
T* MyMagicCast(Base* obj)
{
    if(obj->getClassID() ISVALID) //Then use the ID to make sure that T matches the ID
    {
        return static_cast<T*>(obj);
    }
    else
    {
        return nullptr;
    }
}

这将为您完成运行时的工作。使用这种方法,您的优势在于所有类型的操作都可以通过调用 base 来完成 + 您不使用任何 RTTI。现在只有当您需要特定于您拥有的类型之一(A、B、C、D)的东西时,您才可以使用 MyMagicCast() 转换为它们的类型。

PS: 代码不在我的脑海中,所以可能会有轻微的 typos/errors.