是否可以在 C++ 中将名称附加到运行时类

Is it possible to attach a name to a runtime class in C++?

本文关键字:运行时 C++ 是否      更新时间:2023-10-16

我有一个类的层次结构,基类具有打印类名的功能:

#include <iostream>
using namespace std;
class base
{
public:
  virtual void print_name() { cout << typeid(*this).name() << endl; };
};
class derived1 : public base { };
class derived2 : public base { };
int main ()
{
  base Base;
  Base.print_name();
  derived1 Derived1;
  Derived1.print_name();
  derived2 Derived2;
  Derived2.print_name();
}

上述输出为

class base
class derived1
class derived2

事实上,这取决于平台。

是否有一种或多或少的标准方法可以将一些唯一的名称"附加"到每个类,以便它可以用于printname()使所有平台的输出相同(并且独立于对真实类名所做的任何更改)?

当然:

class base {
  public:
    virtual char const *name() const { return "base"; }
};
class derived1 : public base {
  public:
    virtual char const *name() const { return "derived1"; }
};

但是,如果不重写类中的name,则其名称将是其超类的名称。这可能是错误或功能,具体取决于您的用例。如果这是一个错误,那么你可以添加一些运行时检查,以确保该方法被覆盖:

virtual char const *name() const {
    if (typeid(*this) != typeid(base))
        throw std::logic_error("name() not overridden");
    return "base";
}

但是,您必须在必须重写的每个name实现中重复此检查。

您可以有效地使用type_info来实现此目的。

type_info支持符合弱顺序的before方法,只要提供用户提供的谓词,它就允许在std::map(例如)中使用。

struct TypeInfoLess {
     bool operator()(std::type_info const* lhs, std::type_info const* rhs) const {
         return lhs->before(rhs);
     }
};
struct AdditionalTypeInfo {
    std::string name;
};
typedef std::map<std::type_info const*, AdditionalTypeInfo, TypeInfoLess> TypeInfoMap;

然后,您可以添加/搜索类型:

template <typename T>
void add(TypeInfoMap& map, T const& t, AdditionalTypeInfo const& ati) {
    map[&typeid(t)] = ati;
}
template <typename T>
AdditionalTypeInfo const* find(TypeInfoMap const& map, T const& t) {
    TypeInfoMap::const_iterator it = map.find(&typeid(t));
    if (it == map.end()) { return 0; }
    return &it->second;
}
int main() {
    TypeInfoMap timap;
    add(timap, timap, { "TypeInfoMap" });
    if (AdditionalTypeInfo const* const ati = find(timap, timap)) {
        std::cout << ati->name << "n";
    }
}

注意:然后,您有责任将您可能想要的每种类型添加到地图中。

您可以使用模板函数(或特征类)来获取名称:

template<typename T> const char * ClassName(T const * objPtr);
template<> const char * ClassName<derived1>(derived1 const *objPtr) { return "derived1"; }
template<> const char * ClassName<derived2>(derived2 const *objPtr) { return "derived2"; }

(此处函数参数仅用于模板参数匹配)。在调用代码时,您可以将指针投射到特定的类并使用类名

derived1 * pDerived = static_cast<derived1*>(pPointer);
cout << ClassName(pDerived);

但是,这都是编译时,您将需要一些机制(如 GUID)在运行时标识类(将其强制转换为右指针类型)。如果你的类没有,你不能修改它们,那么我就帮不了你;(但是,您可以使用特征技术来本地化依赖于平台的类型选择代码。