Answers:
您需要做的是让预处理器生成有关字段的反射数据。该数据可以存储为嵌套类。
首先,为了更轻松,更干净地在预处理器中编写它,我们将使用类型化表达式。带类型的表达式只是将类型放在括号中的表达式。因此,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_fields
在可反射Person
类中使用的示例:
int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}
哪个输出:
name=Tom
age=82
瞧,我们刚刚用不到100行代码在C ++中实现了反射。
#define DETAIL_TYPEOF_INT2(tuple) DETAIL_TYPEOF_HEAD tuple
, #define DETAIL_TYPEOF_INT(...) DETAIL_TYPEOF_INT2((__VA_ARGS__))
并将TYPEOF(x)的定义更改为:#define TYPEOF(x) DETAIL_TYPEOF_INT(DETAIL_TYPEOF_PROBE x,)
reflection
周围有两种游泳。
template-tricks
。使用boost::type_traits
了很多东西(如检查类型是否为整数)。要检查成员函数的存在,请使用是否可以编写模板来检查函数的存在?。要检查是否存在某种嵌套类型,请使用普通SFINAE。 如果您宁愿寻找完成1)的方法,例如查看一个类有多少种方法,或者喜欢获取一个类id的字符串表示形式,那么恐怕没有标准的C ++方法可以做到这一点。您必须使用
C ++的设计考虑了速度。如果您想要像C#或Java这样的高级检查,那么恐怕我不得不告诉您,没有任何努力就没有办法。
members<T>
返回T的所有成员的列表。如果我们想要运行时反射(即RTTI与反射混合),则编译器仍将知道所有反射的基本类型。很可能members<T>(T&)
永远不会为T = std :: string实例化它,因此不需要包括std :: string的RTTI或其派生类。
我会爱小马,但小马不是免费的。:-p
您将获得http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI。就像您正在思考的那样,反射-运行时可以使用完全描述性的元数据-默认情况下,C ++不存在。
信息确实存在-但不以您需要的格式提供,并且仅在您导出类时才提供。这在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安全等问题,因此这些方面都可以作为运行时检查来实现。
我确定语法可以改进,并且到目前为止仅适用于Win32和Win64。我们发现,对于类具有自动GUI界面,在C ++中创建属性,与XML进行流传输以及从XML进行流传输等确实非常有用,并且不需要从特定的基类派生。如果有足够的需求,也许我们可以将其成型以供发布。
__declspec(dllexport)
,如果您在构建期间启用了创建此类信息的权限,则可以从.map文件中检索信息。
开箱即用的C ++不支持反射。这很可悲,因为它使防御测试变得痛苦。
有几种方法可以进行反射:
第一个链接看起来是最有前途的(使用mod代替clang),第二个链接讨论了多种技术,第三个是使用gcc的不同方法:
现在有一个C ++反射工作组。查看C ++ 14 @ CERN的新闻:
编辑13/08/17:
自从最初的职位以来,在反思方面已有许多潜在的进步。以下内容提供了更多详细信息,并讨论了各种技术和状态:
但是,除非在社区中有更多的兴趣支持C ++中的反射,否则在不久的将来使用C ++中的标准化反射方法看起来并不乐观。
下面根据上次C ++标准会议的反馈详细介绍当前状态:
编辑13/12/2017
反思似乎正在朝着C ++ 20或更可能是TSR迈进。但是运动缓慢。
编辑15/09/2018
TS草案已发送给国家机构进行投票。
文本可以在这里找到:https : //github.com/cplusplus/reflection-ts
编辑11/07/2019
Reflection TS功能齐全,将在整个夏季(2019)进行评论和投票。
元模板编程方法将被更简单的编译时代码方法(未在TS中反映)取代。
编辑10/02/2020
这里有一个在Visual Studio中支持反射TS的请求:
关于作者David Sankel的TS的谈话:
编辑2020年3月17日
反思工作正在取得进展。可以在这里找到“ 2020-02布拉格ISO C ++委员会旅行报告”的报告:
有关C ++ 23考虑的内容的详细信息,可以在此处找到(包括关于Reflection的简短内容):
编辑2020年6月4日
Jeff Preshing发布了一个名为“ Plywood”的新框架,其中包含一个运行时反射机制。更多详情可在这找到:
到目前为止,工具和方法看起来是最完善和最容易使用的。
您需要查看您正在尝试做什么,以及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
,它可以在运行时(读取配置文件时)转换为一系列方法调用,因此相当高效。除非您确实需要,否则我不建议您这样做,但是当您这样做时,您可以做一些非常酷的事情。
我曾经做过类似您想做的事情,虽然有可能获得一定程度的反思和访问更高级别的功能,但是维护麻烦可能不值得。我的系统用于通过类似于Objective-C的消息传递和转发概念的委派来使UI类与业务逻辑完全分开。做到这一点的方法是创建一些能够将符号映射到函数指针的基类(我使用了一个字符串池,但是如果您更喜欢速度和编译时错误处理而不是总的灵活性,则可以使用枚举来实现)到函数指针(实际上不是纯函数指针,但类似于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_cast
并boost::dynamic_pointer_cast
与各地暴力激情你的代码; 强类型系统的局限性仍然存在,您实际上只是将它们隐藏了一点,因此并不那么明显。字符串中的错别字也不是有趣或容易发现的惊喜。
至于如何实现这样的事情:只需要使用共享和弱指针来指向一些通用的基数(我的想象力很强地称为“对象”),并为所有要使用的类型派生。我建议安装Boost.Function而不是像以前那样安装,它带有一些自定义废话和大量难看的宏来包装函数指针调用。由于所有内容均已映射,因此检查对象仅是遍历所有键的问题。由于我的课程从本质上讲仅使用C ++尽可能接近直接获取Cocoa,所以如果您想要类似的东西,那么我建议您使用Cocoa文档作为蓝图。
从我的C ++时代开始,我知道两种类似于反射的解决方案:
1)使用RTTI,如果您能够使所有类都从“对象”基类派生,则可以使用它来建立类似反射的行为。该类可以提供一些方法,例如GetMethod,GetBaseClass等。关于这些方法的工作方式,您将需要手动添加一些宏来装饰您的类型,这些宏在幕后在该类型中创建元数据以提供对GetMethods等的答案。
2)如果您有权访问编译器对象,则另一个选择是使用DIA SDK。如果我没记错的话,这可以让您打开pdbs,其中应包含C ++类型的元数据。做您需要的事情可能就足够了。例如,此页面显示如何获取类的所有基本类型。
这些解决方案都有点丑陋!没有什么比C ++能让您欣赏C#的奢侈了。
祝好运。
这个问题现在有点老了(不知道为什么今天我会继续提出老问题),但是我正在考虑引入编译时反射的BOOST_FUSION_ADAPT_STRUCT。
当然,这取决于您将其映射到运行时反射,这并不是一件容易的事,但是有可能朝这个方向发展,而并非相反:)
我真的认为封装一个宏BOOST_FUSION_ADAPT_STRUCT
可以生成必要的方法来获取运行时行为。
我认为您可能会发现Dominic Filion的文章“在C ++中使用反射模板”很有趣。它在《Game Programming Gems 5》的 1.4节中。不幸的是,我没有随身携带的复印件,但要寻找它,因为我认为它可以解释您的要求。
反思本质上是关于编译器决定在运行时代码可以查询的代码中留下的内容。C ++以不用付您不使用的东西而闻名。因为大多数人不使用/不希望反射,所以C ++编译器通过不记录任何内容来避免成本。
因此,C ++不能提供反射,而且要像其他答案所指出的那样,一般性地自己“模拟”它并不容易。
在“其他技术”下,如果您没有使用反射语言,请使用一种可以在编译时提取所需信息的工具。
我们的DMS软件再造工具包是通过明确的语言定义参数化的通用编译器技术。它具有针对C,C ++,Java,COBOL,PHP等的语言定义。
对于C,C ++,Java和COBOL版本,它提供对语法分析树和符号表信息的完整访问。该符号表信息包括“反射”可能需要的数据类型。如果您的目标是枚举一组字段或方法,并且做一些与他们,DMS可用于根据您在以任意方式的符号表中查找转换的代码。
您可以在此处找到另一个库:http : //www.garret.ru/cppreflection/docs/reflect.html 它支持2种方法:从调试信息中获取类型信息,并让程序员提供此信息。
我也对我的项目的反射感兴趣,并找到了这个库,我还没有尝试过,但是尝试了这个家伙的其他工具,我喜欢它们的工作方式:-)
查看Classdesc http://classdesc.sf.net。它以类“描述符”的形式提供反射,可与任何标准C ++编译器一起使用(是的,它可以与Visual Studio以及GCC一起使用),并且不需要源代码注释(尽管存在一些杂物来处理棘手的情况) )。它已经开发了十多年,并用于许多工业规模的项目。
即使在C ++中不立即支持反射,也很难实施。我遇到了这篇很棒的文章:http : //replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html
本文详细解释了如何实现一个非常简单且基本的反射系统。虽然它不是最有益的解决方案,但仍有一些粗糙的地方可以解决,但对于我的需求而言,这已经足够了。
最重要的是-如果正确完成,反射可以取得回报,并且在c ++中完全可行。
如果您正在寻找相对简单的C ++反射-我从各种来源收集了宏/定义,并对其进行了注释。您可以从此处下载头文件:
https://github.com/tapika/TestCppReflect/blob/master/MacroHelpers.h
一组定义以及最重要的功能:
https://github.com/tapika/TestCppReflect/blob/master/CppReflect.h https://github.com/tapika/TestCppReflect/blob/master/CppReflect.cpp https://github.com/tapika/TestCppReflect/ blob / master / TypeTraits.h
示例应用程序也位于git仓库中,位于以下位置:https : //github.com/tapika/TestCppReflect/
我将在此处部分复制并进行解释:
#include "CppReflect.h"
using namespace std;
class Person
{
public:
// Repack your code into REFLECTABLE macro, in (<C++ Type>) <Field name>
// form , like this:
REFLECTABLE( Person,
(CString) name,
(int) age,
...
)
};
void main(void)
{
Person p;
p.name = L"Roger";
p.age = 37;
...
// And here you can convert your class contents into xml form:
CStringW xml = ToXML( &p );
CStringW errors;
People ppl2;
// And here you convert from xml back to class:
FromXml( &ppl2, xml, errors );
CStringA xml2 = ToXML( &ppl2 );
printf( xml2 );
}
REFLECTABLE
define使用带有offsetof
-的类名+字段名来标识特定字段位于内存中的哪个位置。我尝试了尽可能多的.NET术语,但是C ++和C#是不同的,因此不是1对1。整个C ++反射模型都驻留在TypeInfo
and 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类和部分模板规范启用任何第3方类/结构支持-以类似于CString或int的方式定义自己的TypeTraitsT类-请参见中的示例代码
https://github.com/tapika/TestCppReflect/blob/master/TypeTraits.h#L195
该解决方案适用于Windows / Visual Studio。可以将其移植到其他OS /编译器,但还没有完成。(如果您真的喜欢解决方案,请问我,我可能会为您提供帮助)
此解决方案适用于具有多个子类的一类的一枪序列化。
但是,如果您正在寻找序列化类部分甚至控制反射调用产生的功能的机制,则可以查看以下解决方案:
https://github.com/tapika/cppscriptcore/tree/master/SolutionProjectModel
可以从youtube视频中找到更多详细信息:
C ++运行时类型反射 https://youtu.be/TN8tJijkeFE
我试图更深入地解释c ++反射将如何工作。
示例代码如下所示:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/testCppApp.cpp
c.General.IntDir = LR"(obj\$(ProjectName)_$(Configuration)_$(Platform)\)";
c.General.OutDir = LR"(bin\$(Configuration)_$(Platform)\)";
c.General.UseDebugLibraries = true;
c.General.LinkIncremental = true;
c.CCpp.Optimization = optimization_Disabled;
c.Linker.System.SubSystem = subsystem_Console;
c.Linker.Debugging.GenerateDebugInformation = debuginfo_true;
但是实际上,这里的每个步骤都会导致函数调用将C ++属性与一起使用__declspec(property(get =, put ... )
。
它以路径的形式接收有关C ++数据类型,C ++属性名称和类实例指针的完整信息,并基于该信息,您可以生成xml,json,甚至可以通过Internet序列化该信息。
此类虚拟回调函数的示例可在此处找到:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/VCConfiguration.cpp
请参见函数ReflectCopy
和虚函数::OnAfterSetProperty
。
但由于主题确实很先进-我建议您先检查视频。
如果您有任何改进的想法,请随时与我联系。
在C ++中,反射非常有用,在这种情况下,您需要为每个成员运行某种方法(例如:序列化,哈希,比较)。我提供了通用解决方案,其语法非常简单:
struct S1
{
ENUMERATE_MEMBERS(str,i);
std::string str;
int i;
};
struct S2
{
ENUMERATE_MEMBERS(s1,i2);
S1 s1;
int i2;
};
其中ENUMERATE_MEMBERS是宏,稍后将对其进行描述(UPDATE):
假设我们已经为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行代码不需要任何第三方库;)
您可以使用Boost :: Hana库中的BOOST_HANA_DEFINE_STRUCT为结构实现酷的静态反射功能。
Hana用途广泛,不仅适用于您想到的用例,而且适用于许多模板元编程。
在随机访问的反思库使得对相当容易和直观的反映-所有现场/类型信息被设计用来在阵列中采用或感觉像数组访问。它是为C ++ 17编写的,可与Visual Studios,g ++和Clang一起使用。该库仅是标头,这意味着您只需将“ Reflect.h”复制到项目中即可使用。
反映的结构或类需要REFLECT宏,您可以在其中提供要反映的类的名称和字段的名称。
class FuelTank {
public:
float capacity;
float currentLevel;
float tickMarks[2];
REFLECT(() FuelTank, () capacity, () currentLevel, () tickMarks)
};
这就是全部,无需其他代码即可设置反射。(可选)您可以提供超类(在第一个参数的括号中)和字段注释(在您要注释的字段之前的括号中),以便能够遍历超类或向字段中添加其他编译时信息(例如Json: :忽视)。
遍历字段可以很简单...
for ( size_t i=0; i<FuelTank::Class::TotalFields; i++ )
std::cout << FuelTank::Class::Fields[i].name << std::endl;
您可以遍历对象实例以访问字段值(可以读取或修改)和字段类型信息...
FuelTank::Class::ForEachField(fuelTank, [&](auto & field, auto & value) {
using Type = typename std::remove_reference<decltype(value)>::type;
std::cout << TypeToStr<Type>() << " " << field.name << ": " << value << std::endl;
});
甲JSON库建立在RandomAccessReflection的顶部,其自动识别用于读取或写入,并且可以递归遍历任何反射字段,以及阵列和STL容器适当JSON输出表示。
struct MyOtherObject { int myOtherInt; REFLECT(() MyOtherObject, () myOtherInt) };
struct MyObject
{
int myInt;
std::string myString;
MyOtherObject myOtherObject;
std::vector<int> myIntCollection;
REFLECT(() MyObject, () myInt, () myString, (Reflected) myOtherObject, () myIntCollection)
};
int main()
{
MyObject myObject = {};
std::cout << "Enter MyObject:" << std::endl;
std::cin >> Json::in(myObject);
std::cout << std::endl << std::endl << "You entered:" << std::endl;
std::cout << Json::pretty(myObject);
}
以上可以这样运行...
Enter MyObject:
{
"myInt": 1337, "myString": "stringy", "myIntCollection": [2,4,6],
"myOtherObject": {
"myOtherInt": 9001
}
}
You entered:
{
"myInt": 1337,
"myString": "stringy",
"myOtherObject": {
"myOtherInt": 9001
},
"myIntCollection": [ 2, 4, 6 ]
}
也可以看看...
如果您声明一个指向这样的函数的指针:
int (*func)(int a, int b);
您可以像这样向函数分配内存中的位置(要求libdl
和dlopen
)
#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
在调用二进制文件(argv[0]
)上使用。
此唯一的要求(除了其他dlopen()
,libdl
和dlfcn.h
被知道参数和函数的类型)。