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
的引用。应该有三种不同的方法来检索这些:
按 vector
的类型,例如collection.get<A>();
通过编译时的 ID,它由 vector
持有的每个对象定义,例如collection.get<4>();
- 通过运行时的 ID,例如
collection.get(id);
案例 1 很简单,因为它可以通过 T::ID
转换为案例 2。案例 2 可以通过模板特化来实现(尽管如果我有很多对象,它看起来很难看)。
案例 3 制造了很多麻烦。如果没有一些巨大的 if
或 switch
语句,它几乎是不可能的,更不用说推断 return 类型了。
我的问题是:
- 有没有办法让case 2更优雅?
- 如何实现案例3?
有些事情你可以做,有些事情你永远做不到。你要的不是那么简单,还需要努力
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.
假设我们有以下对象:
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
的引用。应该有三种不同的方法来检索这些:
按
vector
的类型,例如collection.get<A>();
通过编译时的 ID,它由
vector
持有的每个对象定义,例如collection.get<4>();
- 通过运行时的 ID,例如
collection.get(id);
案例 1 很简单,因为它可以通过 T::ID
转换为案例 2。案例 2 可以通过模板特化来实现(尽管如果我有很多对象,它看起来很难看)。
案例 3 制造了很多麻烦。如果没有一些巨大的 if
或 switch
语句,它几乎是不可能的,更不用说推断 return 类型了。
我的问题是:
- 有没有办法让case 2更优雅?
- 如何实现案例3?
有些事情你可以做,有些事情你永远做不到。你要的不是那么简单,还需要努力
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.