【问题标题】:C++ Getting objects with IDs at runtimeC++ 在运行时获取具有 ID 的对象
【发布时间】:2017-07-21 12:52:19
【问题描述】:

假设我们有以下对象:

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 的一些vectors 的引用。应该有三种不同的方式来检索这些:

  1. vector 的类型,例如collection.get&lt;A&gt;();

  2. 按编译时的 ID,由 vector 持有的每个对象定义,例如collection.get&lt;4&gt;();

  3. 在运行时按 ID,例如collection.get(id);

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

我的问题是:

  • 有没有办法让案例 2 更优雅?
  • 我应该如何实现案例 3?

【问题讨论】:

  • 会一直只有A, B, C, D吗?
  • 可能没有,至少会有 30 个。
  • 你有一个运行时值。你不知道它是什么。您需要根据该值对不同的类型进行操作,这意味着完全不同的代码路径(即使它是同一函数模板的不同实例化)。这意味着一个运行时分支。所以,你要么有switch 要么有map&lt;int,function&gt; - 这些都是你的选择。
  • 但是我如何从switch 语句中返回vector?编译器抱怨无法推断返回类型。
  • 你不能 - 开关需要分派到特定类型的代码路径中。把它想象成一个访客。

标签: c++ templates runtime


【解决方案1】:

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

1 和 2 是困难的,但可以实现。首先是create a static map of all type-IDs,然后是use the static result to choose the type

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

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

首先,case一个基础抽象类,并让所有这些类派生自它,并使其包含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;
    }
};

现在,要实例化一个新类,您可以使用 Base 类。

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:代码不在我的脑海中,所以可能会有轻微的错别字/错误。

【讨论】:

  • 谢谢你的例子,我去看看。
猜你喜欢
  • 1970-01-01
  • 2015-03-08
  • 1970-01-01
  • 1970-01-01
  • 2012-11-01
  • 2012-06-20
  • 2012-11-16
  • 2019-07-30
  • 1970-01-01
相关资源
最近更新 更多