什么是处理类的元方面的适当接口?

发布于 2024-12-28 11:45:19 字数 1966 浏览 2 评论 0原文

我正在寻找一些关于处理类(处理类)方面的适当接口的建议,但这些方面不是他们正在处理的实际类的一部分(元-方面)。这需要一些解释...

在我的具体示例中,我需要实现一个自定义 RTTI 系统,该系统比 C++ 提供的系统稍微复杂一点(我不会详细说明为什么需要它)。我的基础对象是 FooBase ,该基础的每个子类都与一个 FooTypeInfo 对象关联。

// Given a base pointer that holds a derived type,
// I need to be able to find the actual type of the
// derived object I'm holding.
FooBase* base = new FooDerived;

// The obvious approach is to use virtual functions...
const FooTypeInfo& info = base->typeinfo();

使用虚函数来处理对象的运行时类型对我来说并不合适。我倾向于认为对象的运行时类型超出了类的范围,因此它不应该是其显式接口的一部分。下面的接口让我感觉更舒服......

FooBase* base = new FooDerived;
const FooTypeInfo& info = foo::typeinfo(base);

但是,即使接口不是类的一部分,实现仍然必须使用虚函数,才能使其工作:

class FooBase
{
   protected:
     virtual const FooTypeInfo& typeinfo() const = 0;
     friend const FooTypeInfo& ::foo::typeinfo(const FooBase*);
};

namespace foo
{
    const FooTypeInfo& typeinfo(const FooBase* ptr) {
        return ptr->typeinfo();
    }
}

你认为我应该使用这个第二个接口(感觉更适合我)并处理稍微复杂的实现,或者我应该使用第一个接口?


@塞斯·卡内基

如果您甚至不希望派生类知道自己是 RTTI 的一部分,那么这就是一个难题...因为您实际上无法在依赖于的 FooBase 构造函数中执行任何操作正在实例化的类的运行时类型(出于同样的原因,您不能在 ctor 或 dtor 中调用虚拟方法)。

FooBase 是层次结构的公共基础。我还有一个单独的 CppFoo<> 类模板,它减少了样板文件的数量并使类型的定义更容易。还有另一个使用 Python 派生对象的 PythonFoo 类。

template<typename FooClass>
class CppFoo : public FooBase
{
  private:
    const FooTypeInfo& typeinfo() const {
        return ::foo::typeinfo<FooClass>();
    }
};

class SpecificFoo : public CppFoo<SpecificFoo>
{
    // The class can now be implemented agnostic of the
    // RTTI system that works behind the scenes.
};

有关系统如何工作的更多详细信息可以在此处找到:
https://stackoverflow.com/a/8979111/627005

I'm looking for some advice of what would be an appropriate interface for dealing with aspects about classes (that deal with classes), but which are not part of the actual class they are dealing with (meta-aspects). This needs some explanation...

In my specific example I need to implement a custom RTTI system that is a bit more complex than the one offered by C++ (I won't go into why I need that). My base object is FooBase and each child class of this base is associated a FooTypeInfo object.

// Given a base pointer that holds a derived type,
// I need to be able to find the actual type of the
// derived object I'm holding.
FooBase* base = new FooDerived;

// The obvious approach is to use virtual functions...
const FooTypeInfo& info = base->typeinfo();

Using virtual functions to deal with the run-time type of the object doesn't feel right to me. I tend to think of the run-time type of an object as something that goes beyond the scope of the class, and as such it should not be part of its explicit interface. The following interface makes me feel a lot more comfortable...

FooBase* base = new FooDerived;
const FooTypeInfo& info = foo::typeinfo(base);

However, even though the interface is not part of the class, the implementation would still have to use virtual functions, in order for this to work:

class FooBase
{
   protected:
     virtual const FooTypeInfo& typeinfo() const = 0;
     friend const FooTypeInfo& ::foo::typeinfo(const FooBase*);
};

namespace foo
{
    const FooTypeInfo& typeinfo(const FooBase* ptr) {
        return ptr->typeinfo();
    }
}

Do you think I should use this second interface (that feels more appropriate to me) and deal with the slightly more complex implementation, or shoud I just go with the first interface?


@Seth Carnegie

This is a difficult problem if you don't even want derived classes to know about being part of the RTTI ... because you can't really do anything in the FooBase constructor that depends on the runtime type of the class being instantiated (for the same reason you can't call virtual methods in a ctor or dtor).

FooBase is the common base of the hierarchy. I also have a separate CppFoo<> class template that reduces the amount of boilerplate and makes the definition of types easier. There's another PythonFoo class that work with Python derived objects.

template<typename FooClass>
class CppFoo : public FooBase
{
  private:
    const FooTypeInfo& typeinfo() const {
        return ::foo::typeinfo<FooClass>();
    }
};

class SpecificFoo : public CppFoo<SpecificFoo>
{
    // The class can now be implemented agnostic of the
    // RTTI system that works behind the scenes.
};

A few more details about how the system works can be found here:
https://stackoverflow.com/a/8979111/627005

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

提笔书几行 2025-01-04 11:45:19

您可以通过 typeid 关键字将动态类型与静态类型联系起来,并使用返回的 std::type_info 对象作为标识方式。此外,如果您将 typeid 应用于专门为此目的而创建的单独类,那么对于您感兴趣的类来说,这将是完全非侵入性的,尽管它们的名称仍然必须提前知道。重要的是,typeid 应用于支持动态多态性的类型 - 它必须具有一些virtual 函数。

这是一个例子:

#include <typeinfo>
#include <cstdio>

class Base;
class Derived;

template <typename T> class sensor { virtual ~sensor(); };

extern const std::type_info& base = typeid(sensor<Base>);
extern const std::type_info& derived = typeid(sensor<Derived>);

template <const std::type_info* Type> struct type
{
  static const char* name;

  static void stuff();
};

template <const std::type_info* Type> const char* type<Type>::name = Type->name();

template<> void type<&base>::stuff()
{
  std::puts("I know about Base");
}

template<> void type<&derived>::stuff()
{
  std::puts("I know about Derived");
}

int main()
{
  std::puts(type<&base>::name);
  type<&base>::stuff();

  std::puts(type<&derived>::name);
  type<&derived>::stuff();
}

不用说,由于 std::type_info 是正确的对象,并且它们是唯一且有序的,因此您可以在集合中管理它们,从而删除从接口查询的类型:

template <typename T> struct sensor {virtual ~sensor() {}};

struct type
{
  const std::type_info& info;

  template <typename T>
  explicit type(sensor<T> t) : info(typeid(t))
  {};
};

bool operator<(const type& lh, const type& rh)
{
  return lh.info.before(rh.info);
}

int main()
{
  std::set<type> t;
  t.insert(type(sensor<Base>()));
  t.insert(type(sensor<Derived>()));

  for (std::set<type>::iterator i = t.begin(); i != t.end(); ++i)
    std::puts(i->info.name());
}

当然可以按照您认为合适的方式混合搭配两者。

有两个限制:

  • 这里没有实际的内省。您可以通过巧妙的元编程将其添加到模板结构传感器中,这是一个非常广泛的主题(有时令人费解)。
  • 必须提前知道您想要支持的所有类型的名称。

一种可能的变化是添加 RTTI“框架钩子”,例如static constsensor。 rtti_MyClass; 到类名已知的实现文件,并让构造函数完成工作。此时它们还必须是完整的类型,才能在传感器中进行内省。

You can tie dynamic type with static type via typeid keyword and use returned std::type_info objects as means of identification. Furthermore, if you apply typeid on a separate class created specially for the purpose, it will be totally non-intrusive for the classes you are interesed in, althought their names still have to be known in advance. It is important that typeid is applied on a type which supports dynamic polymorphism - it has to have some virtual function.

Here is example:

#include <typeinfo>
#include <cstdio>

class Base;
class Derived;

template <typename T> class sensor { virtual ~sensor(); };

extern const std::type_info& base = typeid(sensor<Base>);
extern const std::type_info& derived = typeid(sensor<Derived>);

template <const std::type_info* Type> struct type
{
  static const char* name;

  static void stuff();
};

template <const std::type_info* Type> const char* type<Type>::name = Type->name();

template<> void type<&base>::stuff()
{
  std::puts("I know about Base");
}

template<> void type<&derived>::stuff()
{
  std::puts("I know about Derived");
}

int main()
{
  std::puts(type<&base>::name);
  type<&base>::stuff();

  std::puts(type<&derived>::name);
  type<&derived>::stuff();
}

Needless to say, since std::type_info are proper objects and they are unique and ordered, you can manage them in a collection and thus erase type queried from the interface:

template <typename T> struct sensor {virtual ~sensor() {}};

struct type
{
  const std::type_info& info;

  template <typename T>
  explicit type(sensor<T> t) : info(typeid(t))
  {};
};

bool operator<(const type& lh, const type& rh)
{
  return lh.info.before(rh.info);
}

int main()
{
  std::set<type> t;
  t.insert(type(sensor<Base>()));
  t.insert(type(sensor<Derived>()));

  for (std::set<type>::iterator i = t.begin(); i != t.end(); ++i)
    std::puts(i->info.name());
}

Of course you can mix and match both, as you see fit.

Two limitations:

  • there is no actual introspection here . You can add it to template struct sensor via clever metaprogramming, it's very wide subject (and mind bending, sometimes).
  • names of all types you want to support have to be known in advance.

One possible variation is adding RTTI "framework hook" such as static const sensor<Myclass> rtti_MyClass; to implementation files where class names are already known and let the constructor do the work. They would also have to be complete types at this point to enable introspection in sensor.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文