如何将反射添加到C ++应用程序?

c++ reflection templates sfinae

170666 观看

30回复

18419 作者的声誉

我希望能够内省一个C ++类的名称,内容(即成员及其类型)等。我在这里说的是原生C ++,而不是托管C ++,它有反射。我意识到C ++使用RTTI提供一些有限的信息。哪些额外的库(或其他技术)可以提供此信息?

作者: Nick 的来源 发布者: 2008 年 9 月 3 日

回应 30


55

47556 作者的声誉

我会喜欢小马,但小马不是免费的。:-P

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI就是你要得到的。您正在考虑的反思 - 在运行时可用的完全描述性元数据 - 默认情况下C ++不存在。

作者: Brad Wilson 发布者: 2008 年 9 月 3 日

13

80657 作者的声誉

你想用反射做什么?
您可以使用Boost 类型traitstypeof库作为有限形式的编译时反射。也就是说,您可以检查和修改传递给模板的类型的基本属性。

作者: Ferruccio 发布者: 2008 年 9 月 3 日

9

91 作者的声誉

我从C ++时代就知道的两个类似反射的解决方案是:

1)如果能够从“对象”基类派生所有类,则使用RTTI,它将为您提供构建类似反射的行为的引导程序。该类可以提供一些方法,如GetMethod,GetBaseClass等。至于这些方法如何工作,您需要手动添加一些宏来装饰您的类型,在幕后创建类型的元数据,以提供GetMethods等的答案。

2)如果您有权访问编译器对象,则另一个选项是使用DIA SDK。如果我没记错的话,这可以让你打开pdbs,它应该包含C ++类型的元数据。它可能足以满足您的需求。此页面显示了如何获取类的所有基类型。

这两种解决方案虽然有点难看!没有什么比C ++更能让你欣赏C#的奢侈品了。

祝好运。

作者: user4385 发布者: 2008 年 9 月 3 日

13

15800 作者的声誉

我建议使用Qt

有开源许可证和商业许可证。

作者: Jérôme 发布者: 2008 年 9 月 3 日

35

401432 作者的声誉

C ++不存在RTTI。

这是完全错误的。实际上,术语“RTTI”是由C ++标准创造的。另一方面,RTTI在实现反射方面并没有走得太远。

作者: Konrad Rudolph 发布者: 2008 年 9 月 3 日

14

14174 作者的声誉

您需要查看您要执行的操作,以及RTTI是否满足您的要求。我已经为一些非常特殊的目的实现了我自己的伪反射。例如,我曾经希望能够灵活地配置模拟输出的内容。它需要向要输出的类添加一些样板代码:

namespace {
  static bool b2 = Filter::Filterable<const MyObj>::Register("MyObject");
} 

bool MyObj::BuildMap()
{
  Filterable<const OutputDisease>::AddAccess("time", &MyObj::time);
  Filterable<const OutputDisease>::AddAccess("person", &MyObj::id);
  return true;
}

第一个调用将此对象添加到过滤系统,该系统调用该BuildMap()方法以确定可用的方法。

然后,在配置文件中,您可以执行以下操作:

FILTER-OUTPUT-OBJECT   MyObject
FILTER-OUTPUT-FILENAME file.txt
FILTER-CLAUSE-1        person == 1773
FILTER-CLAUSE-2        time > 2000

通过一些模板魔术涉及boost,它在运行时被转换为一系列方法调用(当读取配置文件时),因此它非常有效。除非你真的需要,我不建议这样做,但是,当你这样做时,你可以做一些非常酷的事情。

作者: KeithB 发布者: 2008 年 9 月 3 日

6

539 作者的声誉

我想你可能会发现Dominic Filion的文章“在C ++中使用模板反射”。它在Game Programming Gems 5的 1.4节中。不幸的是,我没有我的副本,但寻找它,因为我认为它解释了你的要求。

作者: Luis 发布者: 2008 年 11 月 24 日

96

409799 作者的声誉

reflection周围有两种游泳方式。

  1. 通过迭代类型的成员,枚举其方法等来进行检查。

    使用C ++是不可能的。
  2. 通过检查类类型(类,结构,联合)是否具有方法或嵌套类型来检查是从另一个特定类型派生的。

    使用C ++可以实现这种功能template-tricks。使用boost::type_traits了很多东西(如检查类型是否为整数)。要检查成员函数是否存在,请使用是否可以编写模板来检查函数是否存在?。要检查是否存在某种嵌套类型,请使用普通SFINAE

如果您正在寻找方法来完成1),比如查看一个类有多少方法,或者喜欢获取类id的字符串表示,那么我担心没有标准的C ++方法。你必须使用其中之一

  • 像Qt元对象编译器这样的元编译器,它可以翻译你的代码,添加额外的元信息。
  • 一个包含宏的框架,允许您添加所需的元信息。你需要告诉框架所有方法,类名,基类和它需要的一切。

C ++的设计考虑了速度。如果你想要高级检查,比如C#或Java,那么我恐怕不得不告诉你没有办法没有办法。

作者: Johannes Schaub - litb 发布者: 2008 年 11 月 24 日

10

1310 作者的声誉

我做过类似你曾经做过的事情,虽然可以获得一定程度的反思和访问更高级别的功能,但维护上的头痛可能不值得。我的系统用于通过委托将UI类与业务逻辑完全分离,类似于Objective-C的消息传递和转发概念。这样做的方法是创建一些能够映射符号的基类(我使用字符串池但你可以使用枚举,如果你喜欢速度和编译时错误处理而不是总灵活性)来实现函数指针(实际上不是纯函数指针,但类似于Boost与Boost.Function的相似之处 - 我当时没有访问权限。只要您有一些能够表示任何值的公共基类,您就可以对成员变量执行相同的操作。整个系统是一个毫不掩饰的密钥值编码和委派的冲突,有一些副作用可能值得花费足够的时间来使每个使用该系统的类匹配其所有方法和成员与合法调用:1)任何类都可以在任何其他类上调用任何方法,而不必包含头文件或编写伪基类,因此可以为编译器预定义接口; 2)成员变量的getter和setter很容易实现线程安全,因为更改或访问它们的值总是通过所有对象的基类中的2个方法完成。整个系统是一个毫不掩饰的密钥值编码和委派的冲突,有一些副作用可能值得花费足够的时间来使每个使用该系统的类匹配其所有方法和成员与合法调用:1)任何类都可以在任何其他类上调用任何方法,而不必包含头文件或编写伪基类,因此可以为编译器预定义接口; 2)成员变量的getter和setter很容易实现线程安全,因为更改或访问它们的值总是通过所有对象的基类中的2个方法完成。整个系统是一个毫不掩饰的密钥值编码和委派的冲突,有一些副作用可能值得花费足够的时间来使每个使用该系统的类匹配其所有方法和成员与合法调用:1)任何类都可以在任何其他类上调用任何方法,而不必包含头文件或编写伪基类,因此可以为编译器预定义接口; 2)成员变量的getter和setter很容易实现线程安全,因为更改或访问它们的值总是通过所有对象的基类中的2个方法完成。1)任何类都可以在任何其他类上调用任何方法,而不必包含头文件或编写伪基类,因此可以为编译器预定义接口; 2)成员变量的getter和setter很容易实现线程安全,因为更改或访问它们的值总是通过所有对象的基类中的2个方法完成。1)任何类都可以在任何其他类上调用任何方法,而不必包含头文件或编写伪基类,因此可以为编译器预定义接口; 2)成员变量的getter和setter很容易实现线程安全,因为更改或访问它们的值总是通过所有对象的基类中的2个方法完成。

它还导致了做一些非常奇怪的事情的可能性,否则这些事情在C ++中并不容易。例如,我可以创建一个包含任意类型的任意项(包括其自身)的Array对象,并通过将消息传递给所有数组项并收集返回值(类似于Lisp中的map)来动态创建新数组。另一个是键值观察的实现,我可以设置UI来立即响应后端类成员的变化,而不是不断地轮询数据或不必要地重新绘制显示。

您可能更感兴趣的是,您还可以转储为类定义的所有方法和成员,并且不会少于字符串形式。

系统的缺点可能会阻止您打扰:添加所有消息和键值非常繁琐; 它比没有任何反射慢; 你会成长讨厌看到boost::static_pointer_castboost::dynamic_pointer_cast与各地暴力激情你的代码; 强类型系统的局限性仍然存在,你真的只是隐藏它们,所以它不那么明显。你的琴弦中的错别字也不是一种有趣或容易发现的惊喜。

至于如何实现这样的事情:只使用共享和弱指针到一些共同的基础(我的想象力被称为“对象”),并派生出你想要使用的所有类型。我建议安装Boost.Function而不是像我那样做,这是一些自定义的垃圾和大量丑陋的宏来包装函数指针调用。由于所有内容都已映射,因此检查对象只需迭代所有键即可。由于我的类基本上只使用C ++就可以直接使用Cocoa,如果你想要这样的东西,那么我建议使用Cocoa文档作为蓝图。

作者: Michel 发布者: 2008 年 11 月 27 日

37

746 作者的声誉

信息确实存在 - 但不是以您需要的格式存在,并且仅在您导出类时才存在。这适用于Windows,我不了解其他平台。使用存储类说明符,例如:

class __declspec(export) MyClass
{
public:
    void Foo(float x);
}

这使得编译器将类定义数据构建到DLL / Exe中。但它不是一种可以随意用于反射的格式。

在我的公司,我们构建了一个解释这个元数据的库,并允许您反映一个类,而无需在类本身中插入额外的宏等。它允许按如下方式调用函数:

MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);

这有效地做到了:

instance_ptr->Foo(1.331);

Invoke(this_pointer,...)函数具有可变参数。显然,通过以这种方式调用函数,你可以绕过诸如const-safety之类的东西,因此这些方面被实现为运行时检查。

我确信语法可以改进,它只适用于Win32和Win64到目前为止。我们发现它对于为类提供自动GUI接口,在C ++中创建属性,在XML之间进行流式传输等等非常有用,并且不需要从特定的基类派生。如果有足够的需求,也许我们可以将其打造成释放形状。

作者: Roderick 发布者: 2009 年 8 月 19 日

2

34484 作者的声誉

当我想用C ++进行反思时,我读了这篇文章,并根据我在那里看到的内容进行了改进。对不起,没法。我不拥有结果......但是你可以得到我拥有的东西并从那里开始。

我正在研究,当我觉得它时,使用inherit_linearly方法来更容易地定义可反射类型。我实际上已经相当远了,但我还有很长的路要走。C ++ 0x中的更改很可能在这方面提供了很多帮助。

作者: Crazy Eddie 发布者: 2010 年 6 月 14 日

12

28074 作者的声誉

编辑CAMP不再维护; 有两个叉子可供选择:

  • 一个也称为CAMP,并且基于相同的API。
  • 思考是部分重写,并且应该是首选,因为它不需要Boost; 它使用的是C ++ 11。

CAMP是MIT许可的库(以前称为LGPL),它为C ++语言添加了反射。它不需要编译中的特定预处理步骤,但必须手动进行绑定。

目前的Tegesoft库使用Boost,但也有一个使用C ++ 11 的fork不再需要Boost

作者: philant 发布者: 2010 年 6 月 20 日

6

205178 作者的声誉

这个问题现在有点旧了(不知道为什么我今天仍然会遇到旧问题)但我正在考虑引入编译时反射的BOOST_FUSION_ADAPT_STRUCT

你可以将它映射到运行时反射当然,它不会太容易,但它可以在这个方向,而它不会反过来:)

我真的认为封装一个宏BOOST_FUSION_ADAPT_STRUCT可以生成获取运行时行为的必要方法。

作者: Matthieu M. 发布者: 2010 年 11 月 12 日

4

80881 作者的声誉

反射主要是关于编译器决定留下什么作为运行时代码可以查询的代码中的脚印。C ++以不付你不使用的东西而闻名; 因为大多数人不使用/想要反射,所以C ++编译器通过不记录任何内容来避免成本。

因此,C ++不提供反射,并且像其他答案所指出的那样,自己“模拟”它并不容易。

在“其他技术”下,如果您没有带反射的语言,请获取一个可以在编译时提取所需信息的工具。

我们的DMS软件再造工具包是通过显式语言定义参数化的通用编译器技术。它有C,C ++,Java,COBOL,PHP的语言定义......

对于C,C ++,Java和COBOL版本,它提供对解析树和符号表信息的完全访问。该符号表信息包括您可能想要从“反射”获得的数据类型。如果您的目标是枚举某些字段或方法集并对其执行某些操作,则可以使用DMS根据您在符号表中以任意方式找到的内容来转换代码。

作者: Ira Baxter 发布者: 2010 年 12 月 30 日

3

468 作者的声誉

你可以在这里找到另一个图书馆:http//www.garret.ru/cppreflection/docs/reflect.html 它支持两种方式:从调试信息中获取类型信息,让程序员提供这些信息。

我也对我的项目的反思感兴趣,发现这个库,我还没有尝试过,但尝试过这个人的其他工具,我喜欢他们的工作方式:-)

作者: alariq 发布者: 2011 年 7 月 28 日

2

9183 作者的声誉

看起来C ++仍然没有这个功能。和C ++ 11推迟反射太((

搜索一些宏或自己制作。Qt也可以帮助反射(如果可以使用)。

作者: Bohdan 发布者: 2011 年 11 月 24 日

2

2099 作者的声誉

试着看看这个项目http://www.garret.ru/cppreflection/docs/reflect.html 是对C ++的反思。它将元数据添加到您可以使用的类中。

作者: Damian 发布者: 2012 年 2 月 17 日

215

13360 作者的声誉

您需要做的是让预处理器生成有关字段的反射数据。此数据可以存储为嵌套类。

首先,为了使它在预处理器中更容易和更清晰,我们将使用类型化表达式。类型化表达式只是将类型放在括号中的表达式。所以不写作,int x你会写(int) x。这里有一些方便的宏来帮助输入类型:

#define REM(...) __VA_ARGS__
#define EAT(...)

// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

接下来,我们定义一个REFLECTABLE宏来生成关于每个字段的数据(加上字段本身)。这个宏将被调用如下:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

因此,使用Boost.PP,我们迭代每个参数并生成如下数据:

// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};


#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

这样做是生成一个常量fields_n,它是类中可反射字段的数量。然后它专门field_data针对每个领域。它也是reflector该类的朋友,即使它们是私有的,它也可以访问这些字段:

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

现在迭代字段我们使用访问者模式。我们创建一个从0到字段数的MPL范围,并访问该索引处的字段数据。然后它将字段数据传递给用户提供的访问者:

struct field_visitor
{
    template<class C, class Visitor, class I>
    void operator()(C& c, Visitor v, I)
    {
        v(reflector::get_field_data<I::value>(c));
    }
};


template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}

现在我们把它放在一起。以下是我们如何定义Person可反射的类:

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

这是一个print_fields使用反射数据迭代字段的通用函数:

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};

template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

使用print_fieldswith reflectable Person类的示例:

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

哪个输出:

name=Tom
age=82

瞧,我们刚刚用100行代码实现了C ++中的反射。

作者: Paul Fultz II 发布者: 2012 年 7 月 31 日

7

320 作者的声誉

开箱即用的C ++不支持反射。这很令人难过,因为它使防御性测试成为一种痛苦。

有几种做反射的方法:

  1. 使用调试信息(非便携式)。
  2. 使用宏/模板或其他一些源方法(看起来很难看)使用您的代码
  3. 修改诸如clang / gcc之类的编译器以生成数据库。
  4. 使用Qt moc方法
  5. 提升反思
  6. 精确平坦的反射

第一个链接看起来最有希望(使用mod's clang),第二个链接讨论了许多技术,第三个是使用gcc的不同方法:

  1. http://www.donw.org/rfl/

  2. https://bitbucket.org/dwilliamson/clreflect

  3. https://root.cern.ch/how/how-use-reflex

现在有一个C ++反射工作组。查看C ++ 14 @ CERN的新闻:

编辑13/08/17:自原始帖子以来,反思有很多潜在的进步。以下提供了更多细节,并讨论了各种技术和状态:

  1. 坚果壳中的静态反射
  2. 静态反射
  3. 静态反射的设计

然而,在不久的将来,C ++中的标准化反射方法看起来并不乐观,除非社区对支持C ++中的反射有更多的兴趣。

以下根据上次C ++标准会议的反馈详细介绍了当前状态:

编辑13/12/2017

反思看起来正朝着C ++ 20或更高的方向发展,可能是TSR。然而运动很慢。

编辑15/09/2018

已将TS草案发送给国家机构进行投票。

可在此处找到该文本:https//github.com/cplusplus/reflection-ts

作者: Damian Dixon 发布者: 2012 年 11 月 4 日

2

31 作者的声誉

即使在c ++中不支持开箱即用的反射,也不难实现。我遇到过这篇很棒的文章:http//replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

文章详细解释了如何实现一个非常简单和基本的反射系统。授予它不是最健康的解决方案,并且还有粗糙的边缘需要整理,但是对于我的需求来说已经足够了。

底线 - 如果正确完成,反射可以得到回报,并且在c ++中完全可行。

作者: Naore Azenkut 发布者: 2013 年 2 月 2 日

3

31 作者的声誉

查看Classdesc http://classdesc.sf.net。它以类“描述符”的形式提供反射,适用于任何标准C ++编译器(是的,它已知可以与Visual Studio以及GCC一起使用),并且不需要源代码注释(尽管存在一些编译指示来处理棘手的情况)。它已经开发了十多年,并用于许多工业规模项目。

作者: Russell Standish 发布者: 2013 年 4 月 6 日

7

5162 作者的声誉

编辑:更新了截至2017年2月7日的断开链接。

我想没人提到这个:

在欧洲核子研究中心,他们使用C ++的全反射系统:

CERN Reflex。它似乎工作得很好。

作者: Germán Diago 发布者: 2013 年 10 月 29 日

2

21 作者的声誉

我想宣传自动内省/反射工具包“IDK”的存在。它使用像Qt这样的元编译器,并将元信息直接添加到目标文件中。它声称易于使用。没有外部依赖。它甚至允许您自动反映std :: string,然后在脚本中使用它。请看IDK

作者: Jen 发布者: 2014 年 3 月 12 日

8

91 作者的声誉

在C ++中还有另一个用于反射的新库,称为RTTR(运行时类型反射,另请参见github)。

该接口类似于C#中的反射,它可以在没有任何RTTI的情况下工作。

作者: Zack 发布者: 2014 年 7 月 23 日

0

1827 作者的声誉

更新于24.2.2017

以前我已经分析了对使用#define的支持,并且喜欢它在一些网络文章中推荐 - 我在Visual C ++中的定义与gcc中使用的定义相比没有相同的工作(例如,在互联网上,这通常被称为“MSVC walkaround”)。除了不能轻易理解定义/宏扩展机制背后发生的事情之外 - 调试每个宏扩展相当困难。

有几种方法可以解决定义扩展的复杂性,一种方法是打开“/ P”编译器标志(仅预处理到文件) - 之后您可以比较定义的开放方式。(以前我也使用过主动stringfy运算符(#))

我从多个论坛收集了所有有用的定义,使用它们,并注释掉机器背后发生的事情,你现在可以在这里找到整个头文件:

https://sourceforge.net/p/testcppreflect/code/HEAD/tree/MacroHelpers.h

我认为使用这些宏来启用C ++反射是相当简单的,但它需要更多的魔法来进行反射。

我已经回忆起了一个工作示例代码,并把它作为sourceforge项目,可以在这里下载:

https://sourceforge.net/p/testcppreflect/code/HEAD/tree/

演示代码如下所示:

#include "CppReflect.h"
using namespace std;


class Person
{
public:
    REFLECTABLE( Person,
        (CString)   name,
        (int)       age
    )

};

class People
{
public:
    REFLECTABLE( People,
        (CString) groupName,
        (vector<Person>)  people
    )
};


void main(void)
{
    People ppl;
    ppl.groupName = "Group1";

    Person p;
    p.name = L"Roger";
    p.age = 37;
    ppl.people.push_back(p);
    p.name = L"Alice";
    p.age = 27;
    ppl.people.push_back( p );
    p.name = L"Cindy";
    p.age = 17;
    ppl.people.push_back( p );

    CStringA xml = ToXML( &ppl );
    CStringW errors;

    People ppl2;

    FromXml( &ppl2, xml, errors );
    CStringA xml2 = ToXML( &ppl2 );
    printf( xml2 );

}

REFLECTABLE定义使用类名+字段名称和offsetof - 来标识特定字段所在的内存中的哪个位置。我试图尽可能地选择.NET术语,但是C ++和C#是不同的,所以它不是1比1.整个C ++反射模型驻留在TypeInfo和FieldInfo类中以便于计时,它也可以扩展对方法的支持,但我现在决定保持简单。

我使用pugi xml解析器将演示代码提取到xml中并从xml恢复。

因此,演示代码生成的输出如下所示:

<?xml version="1.0" encoding="utf-8"?>
<People groupName="Group1">
    <people>
        <Person name="Roger" age="37" />
        <Person name="Alice" age="27" />
        <Person name="Cindy" age="17" />
    </people>
</People>

也可以通过TypeTraits类和部分模板规范启用任何第三方类/结构支持 - 以TypeTraitsT类似的方式定义您自己的类,CString或者int- 参见示例代码

https://sourceforge.net/p/testcppreflect/code/HEAD/tree/TypeTraits.h#l65

template <>
class TypeTraitsT<CString> : public TypeTraits
{
public:
    virtual CStringW ToString( void* pField )
    { 
        CString* s = (CString*)pField;
        return *s;
    }

    virtual void FromString( void* pField, const wchar_t* value )
    { 
        CString* s = (CString*)pField;
        *s = value;
    }
};

template <>
class TypeTraitsT<int> : public TypeTraits
{
public:
    virtual CStringW ToString( void* pField )
    {
        int* p = (int*) pField;
        return std::to_string(*p).c_str();
    }

    virtual void FromString( void* pField, const wchar_t* value )
    {
        int* p = (int*)pField;
        *p = _wtoi(value);
    }
};

我想我自己实现的唯一缺点就是使用__if_exists- 这可能是Microsoft编译器特定的扩展。如果有人知道如何走动它,请告诉我。

作者: TarmoPikaro 发布者: 2015 年 2 月 8 日

4

18419 作者的声誉

决定

Ponder是一个C ++反射库,用于回答这个问题。我考虑了这些选项,并决定自己做,因为我找不到一个勾选了我所有的盒子。

虽然这个问题有很好的答案,但我不想使用吨的宏,或者依赖Boost。Boost是一个很棒的库,但有许多小的定制C ++ 0x项目更简单,编译时间更短。能够在外部装饰类也有一些优点,比如包装一个没有(但是?)支持C ++ 11的C ++库。它是CAMP的分支,使用C ++ 11,不再需要Boost

作者: Nick 发布者: 2015 年 12 月 31 日

0

509 作者的声誉

Root Reflex项目对此有所支持。

请参阅https://root.cern.ch/how/how-use-reflex

作者: Buğra Gedik 发布者: 2016 年 6 月 17 日

1

311 作者的声誉

C ++中的反射非常有用,如果你需要为每个成员运行一些方法(例如:序列化,散列,比较)。我带有通用解决方案,语法非常简单:

struct S1
{
    ENUMERATE_MEMBERS(str,i);
    std::string str;
    int i;
};
struct S2
{
    ENUMERATE_MEMBERS(s1,i2);
    S1 s1;
    int i2;
};

其中ENUMERATE_MEMBERS是一个宏,稍后将对此进行描述(更新):

假设我们为int和std :: string定义了序列化函数,如下所示:

void EnumerateWith(BinaryWriter & writer, int val)
{
    //store integer
    writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
    //store string
    writer.WriteBuffer(val.c_str(), val.size());
}

我们在“秘密宏”附近有通用功能;)

template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
    val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}

现在你可以写了

S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");

EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

因此,在结构定义中使用ENUMERATE_MEMBERS宏,您可以构建序列化,比较,散列和其他内容而无需触及原始类型,唯一的要求是为每个类型实现“EnumerateWith”方法,这是不可枚举的,每个枚举器(如BinaryWriter) 。通常,您必须实现10-20“简单”类型以支持项目中的任何类型。

这个宏应该在运行时对结构创建/销毁进行零开销,并且应该按需生成T.EnumerateWith()的代码,这可以通过使其成为模板内联函数来实现,因此这是唯一的开销。所有的故事都是为每个结构添加ENUMERATE_MEMBERS(m1,m2,m3 ...),而在任何解决方案中都必须实现每个成员类型的特定方法,所以我不认为它是开销。

更新:ENUMERATE_MEMBERS宏的实现非常简单(但是可以稍微扩展一下以支持从可枚举结构继承)

#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }

// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) 
{ 
    int x[] = { (EnumerateWith(enumerator, v), 1)... }; 
}

// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
    val.EnumerateWith(enumerator);
}

对于这15行代码,您不需要任何第三方库;)

作者: Evgeny Mamontov 发布者: 2016 年 11 月 7 日

0

364 作者的声誉

如果你声明一个指向这样的函数的指针:

int (*func)(int a, int b);

您可以在内存中为此功能分配一个位置(需要libdldlopen

#include <dlfcn.h>

int main(void)
{
    void *handle;
    char *func_name = "bla_bla_bla";
    handle = dlopen("foo.so", RTLD_LAZY);
    *(void **)(&func) = dlsym(handle, func_name);
    return func(1,2);
}

要使用间接加载本地符号,可以dlopen在调用binary(argv[0])上使用。

对此的唯一要求(除了dlopen()libdl和之外dlfcn.h)是知道函数的参数和类型。

作者: JL2210 发布者: 2019 年 3 月 3 日

0

15489 作者的声誉

使用C ++ 20,您可以获得扩展语句,它允许您迭代聚合类型:

struct my_type {
    double data;
    std::string another_data;
    int last_data;
};

for...(auto& member : object) {
    using member_type = std::remove_cvref_t<decltype(member)>;
    member = get_data<member_type>();
}
作者: Guillaume Racicot 发布者: 2019 年 3 月 22 日
32x32