C ++反射如何实现

时间:2012-08-31 11:08:53

标签: c++ reflection template-meta-programming

我知道C ++不支持反射,但我浏览了论文Reflection support by means of template meta-programming,但不明白这是如何实现的。有没有人可以使用模板元编程在C ++中获得更多细节或示例?

2 个答案:

答案 0 :(得分:5)

下面是一个结构的示例,如果类型为Obj的类型具有名为“foo”的Type类型的公共数据成员,则在编译时进行测试。它使用C ++ 11功能。虽然可以使用C ++ 03功能完成,但我认为这种方法更优越。

首先,我们检查Obj是否是使用std :: is_class的类。如果它不是类,则它不能具有数据成员,因此测试返回false。这是通过下面的部分模板专业化实现的。

我们将使用SFINAE来检测对象是否包含数据成员。我们声明了struct helper,它具有类型为“指向类Obj的类型Type的数据成员的指针”的模板参数。然后我们声明静态函数测试的两个重载版本:第一个,它rsturns指示失败测试的类型通过省略号接受任何参数。请注意,省略号在重载决策中具有最低优先级。第二个返回指示成功的类型,接受带有模板的辅助结构的指针 参数& U :: foo。现在我们检查一下使用U绑定到Obj的调用调用返回如果使用nullptr调用并且将typedef调用到testresult。由于最后尝试省略号,编译器首先尝试第二个测试版本。如果帮助<& Obj :: foo>是一种合法类型,只有在Obj具有Type类型的公共数据成员时才会生效,然后选择此重载 testresult将是std :: true_type。如果这不是合法类型,则从可能的候选列表(SFINAE)中排除重载,因此将选择接受任何参数类型的测试的剩余版本,并且testresult将是std :: false_type。最后,testresult的静态成员值被赋值给我们的静态成员值,该值表明我们的测试是否成功。

该技术的一个缺点是您需要知道您正在测试的数据成员的名称(在我的示例中为“foo”),因此要为不同的名称执行此操作,您必须编写宏。

你可以编写类似的测试来测试一个类型是否具有一个具有特定名称和类型的静态数据成员,如果它具有内部类型或具有特定名称的typedef,如果它具有一个具有某个特定名称的成员函数用给定的参数类型调用等等,但这超出了我现在的时间范围。

template <typename Obj, typename Type, bool b = std::is_class<Obj>::value>
struct has_public_member_foo
{
  template <typename Type Obj::*> 
  struct helper;

  template <typename U>
  static std::false_type test(...);

  template <typename U>
  static std::true_type test(helper<&U::foo> *);

  typedef decltype(test<Obj>(nullptr)) testresult;

  static const bool value = testresult::value;
};

template <typename Obj, typename Type>
struct has_public_member_foo<Obj, Type, false> : std::false_type { };

struct Foo
{
  double foo;
};

struct Bar
{
  int bar;
};


void stackoverflow() 
{
  static_assert(has_public_member_foo<Foo, double>::value == true, "oops");
  static_assert(has_public_member_foo<Foo, int>::value == false, "oops");
  static_assert(has_public_member_foo<Bar, int>::value == false, "oops");
  static_assert(has_public_member_foo<double, int>::value == false, "oops");
}

答案 1 :(得分:0)

可以在编译时查询类型的某些特征。 最简单的情况可能是内置的sizeof运算符。作为疯狂科学家 发布后,您还可以针对特定成员进行调查。

在使用通用编程或模板元编程的框架内 通常有关于类概要的合同(形式化为 概念)。

例如,STL对函数对象使用成员typedef result_typeboost:result_of(后来成为std::result_of)扩展了此合同 允许嵌套类模板以计算a的结果类型 函数对象,其参数是通用的(换句话说 - 有一个 重载或模板operator())。然后boost:result_of将执行编译 时间反射,允许客户端代码确定函数的结果类型 指针,STL函数对象或“模板化函数对象”统一允许 编写更通用的代码,在任何一种情况下都“正常工作”。旁注:C ++ 11 在这种特殊情况下可以做得更好 - 我用它作为例子,因为它是 两者都非常重要,并且基于广泛的组件。

此外,可以使用将发出数据结构的客户端代码 包含在编译时推导出的元信息(甚至是客户端传入的元信息) 代码)注册某种类型时。框架代码可以是例如使用 typeid运算符获取类型的运行时表示并生成 为特定构造函数,析构函数和一组成员调用存根 函数(有些可能是可选的)并将此信息存储在std::map中 由std::type_index键入(或std::type_info周围的手写包装 对于旧版本的语言)。在此计划的稍后阶段 可以找到某个对象类型的(运行时表示)信息 为了运行一个算法,例如,创建更多相同类型的实例 有些人有临时生活,做一些操作并整理。

结合这两种技术非常强大,因为要运行的代码 可以使用积极内联在编译时生成高复杂度 对于可能在运行中模板生成的许多变化,接口 在程序运行时通过类似方法使用时间要求较低的部分。