如何将反射添加到C++;应用 我想能够对一个C++类的名字、内容(即成员和它们的类型)进行内省。我这里说的是本地C++,而不是管理C++,它有反射。我意识到C++使用RTTI提供一些有限的信息。哪些额外的库(或其他技术)可以提供这些信息?
我想要一匹小马,但小马不是免费的:-p如何将反射添加到C++;应用 我想能够对一个C++类的名字、内容(即成员和它们的类型)进行内省。我这里说的是本地C++,而不是管理C++,它有反射。我意识到C++使用RTTI提供一些有限的信息。哪些额外的库(或其他技术)可以提供这些信息?,c++,reflection,templates,sfinae,C++,Reflection,Templates,Sfinae,我想要一匹小马,但小马不是免费的:-p 这就是你要得到的。像你所想的那样——在运行时可用的完全描述性元数据——默认情况下C++不存在。 < P>你想用反射做什么? 您可以将Boost和库用作编译时反射的有限形式。也就是说,您可以检查和修改传递给模板的类型的基本属性。 < P>我从C++天知道的两种反射式解决方案是: 1) 使用RTTI,它将为您提供一个引导,以构建类似反射的行为,如果您能够使所有类都从“对象”基类派生的话。该类可以提供一些方法,如GetMethod、GetBaseClass等。
这就是你要得到的。像你所想的那样——在运行时可用的完全描述性元数据——默认情况下C++不存在。 < P>你想用反射做什么?
您可以将Boost和库用作编译时反射的有限形式。也就是说,您可以检查和修改传递给模板的类型的基本属性。 < P>我从C++天知道的两种反射式解决方案是: 1) 使用RTTI,它将为您提供一个引导,以构建类似反射的行为,如果您能够使所有类都从“对象”基类派生的话。该类可以提供一些方法,如GetMethod、GetBaseClass等。至于这些方法的工作方式,您需要手动添加一些宏来修饰您的类型,这些宏在后台在类型中创建元数据以提供GetMethods等的答案 2) 如果您有权访问编译器对象,另一个选项是使用。如果我记得正确,这可以让你打开PDB,它应该包含你的C++类型的元数据。这可能足以满足你的需要。例如,演示如何获取类的所有基类型 不过,这两种解决方案都有点难看!没有什么比C++更能让你欣赏C语言的奢侈。 祝你好运。我建议使用
有一个开源许可证和一个商业许可证。您需要看看您正在尝试做什么,以及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;
}
通过一些涉及
boost
的模板魔术,在运行时(当读取配置文件时)将其转换为一系列方法调用,因此相当有效。除非你真的需要,否则我不建议你这么做,但是,当你这么做的时候,你可以做一些非常酷的事情。我想你可能会发现Dominic Filion的文章“在C++中使用模板进行反射”很有趣。第1.4节。不幸的是,我身上没有我的副本,但请寻找它,因为我认为它解释了您的要求。有两种反射
这是不可能的C++。李>
这样的事情是可能的,C++使用<代码>模板技巧。用于许多事情(例如检查类型是否为整数)。要检查成员函数是否存在,请使用。要检查某个嵌套类型是否存在,请使用plain李>
如果你在寻找实现1的方法,比如看看一个类有多少方法,或者像得到一个类ID的字符串表示,那么恐怕没有标准的C++方式来做这个。你必须使用其中一种
- 一个元编译器,比如Qt元对象编译器,它通过添加额外的元信息来翻译代码
- 由宏组成的框架,允许您添加所需的元信息。您需要告诉框架所有方法、类名、基类以及它需要的一切
C++的设计考虑到了速度。如果你想要像C#或Java那样的高级检查,那么我恐怕不得不告诉你,不费吹灰之力是不行的。我曾经做过类似于你所追求的事情,虽然可以得到某种程度的反映并访问更高级的功能,但维护方面的头痛可能不值得。我的系统通过类似于Objective-C的消息传递和转发概念的委托来保持UI类与业务逻辑完全分离。这样做的方法是创建一些基类,它能够将符号(我使用了一个字符串池,但是如果您更喜欢速度和编译时错误处理而不是总体灵活性,那么可以使用枚举)映射到函数指针(实际上不是纯粹的函数指针,而是类似于Boost与Boost.function的功能——我当时没有访问过它)。您可以对您的成员变量执行相同的操作,只要您有一些能够表示任何值的公共基类。整个系统是对键值编码和委托的公然模仿,有一些副作用,这些副作用可能值得花大量时间让使用该系统的每个类匹配所有为其方法和成员提供合法调用:1)任何类都可以调用任何其他类上的任何方法,而不必包含头或编写假基类,以便为编译器预定义接口;2)成员变量的getter和setter很容易使线程安全,因为更改或访问它们的值总是通过所有对象基类中的两个方法完成的 <>这也导致了一些可能发生的奇怪的事情,否则在C++中就不容易了。例如,我可以创建一个数组对象,其中包含任何类型的任意项,包括它本身,并通过向所有数组项传递消息并收集返回值(类似于Lisp中的map)动态创建新数组。另一个是t
FILTER-OUTPUT-OBJECT MyObject
FILTER-OUTPUT-FILENAME file.txt
FILTER-CLAUSE-1 person == 1773
FILTER-CLAUSE-2 time > 2000
class __declspec(export) MyClass
{
public:
void Foo(float x);
}
MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);
instance_ptr->Foo(1.331);
#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
(
(const char *) name,
(int) age
)
// 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)); \
} \
}; \
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;
};
};
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));
}
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};
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());
}
int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}
name=Tom
age=82
struct S1
{
ENUMERATE_MEMBERS(str,i);
std::string str;
int i;
};
struct S2
{
ENUMERATE_MEMBERS(s1,i2);
S1 s1;
int i2;
};
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)
#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);
}
int (*func)(int a, int b);
#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);
}
#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 );
}
<?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>
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;
class FuelTank {
public:
float capacity;
float currentLevel;
float tickMarks[2];
REFLECT(FuelTank, capacity, currentLevel, tickMarks)
};
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;
});
struct MyOtherObject { int myOtherInt; REFLECT(MyOtherObject, myOtherInt) };
struct MyObject
{
int myInt;
std::string myString;
MyOtherObject myOtherObject;
std::vector<int> myIntCollection;
REFLECT(MyObject, myInt, myString, 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 ]
}