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


当前回答

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

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);
}

要使用间接方式加载局部符号,可以对调用二进制文件(argv[0])使用dlopen。

这样做的唯一要求(除了dlopen()、libdl和dlfcn.h)是知道函数的参数和类型。

其他回答

Ponder是一个c++反射库,用于回答这个问题。我考虑了这些选择,决定自己做一个,因为我找不到一个符合我所有要求的。

虽然这个问题有很好的答案,但我不想使用大量宏,也不想依赖Boost。Boost是一个很棒的库,但也有很多小型定制的c++ 0x项目,它们更简单,编译时间更快。能够从外部装饰一个类也有好处,比如包装一个不支持c++ 11的c++库。它是CAMP的分支,使用c++ 11,不再需要Boost。

I did something like what you're after once, and while it's possible to get some level of reflection and access to higher-level features, the maintenance headache might not be worth it. My system was used to keep the UI classes completely separated from the business logic through delegation akin to Objective-C's concept of message passing and forwarding. The way to do it is to create some base class that is capable of mapping symbols (I used a string pool but you could do it with enums if you prefer speed and compile-time error handling over total flexibility) to function pointers (actually not pure function pointers, but something similar to what Boost has with Boost.Function--which I didn't have access to at the time). You can do the same thing for your member variables as long as you have some common base class capable of representing any value. The entire system was an unabashed ripoff of Key-Value Coding and Delegation, with a few side effects that were perhaps worth the sheer amount of time necessary to get every class that used the system to match all of its methods and members up with legal calls: 1) Any class could call any method on any other class without having to include headers or write fake base classes so the interface could be predefined for the compiler; and 2) The getters and setters of the member variables were easy to make thread-safe because changing or accessing their values was always done through 2 methods in the base class of all objects.

It also led to the possibility of doing some really weird things that otherwise aren't easy in C++. For example I could create an Array object that contained arbitrary items of any type, including itself, and create new arrays dynamically by passing a message to all array items and collecting the return values (similar to map in Lisp). Another was the implementation of key-value observing, whereby I was able to set up the UI to respond immediately to changes in the members of backend classes instead of constantly polling the data or unnecessarily redrawing the display.

也许您更感兴趣的是,您还可以转储为类定义的所有方法和成员,而且是字符串形式。

该系统的缺点可能会让您望而却步:添加所有消息和键值非常繁琐;它比没有反射要慢;你会讨厌看到boost::static_pointer_cast和boost::dynamic_pointer_cast遍布你的代码库;强类型系统的局限性仍然存在,您实际上只是将它们隐藏了一些,所以它不那么明显。字符串中的错别字也不是一个有趣或容易发现的惊喜。

As to how to implement something like this: just use shared and weak pointers to some common base (mine was very imaginatively called "Object") and derive for all the types you want to use. I'd recommend installing Boost.Function instead of doing it the way I did, which was with some custom crap and a ton of ugly macros to wrap the function pointer calls. Since everything is mapped, inspecting objects is just a matter of iterating through all of the keys. Since my classes were essentially as close to a direct ripoff of Cocoa as possible using only C++, if you want something like that then I'd suggest using the Cocoa documentation as a blueprint.

当我想要在c++中进行反射时,我读了这篇文章并改进了我在那里看到的东西。对不起,没有罐头。结果不是我的…但你当然可以得到我所拥有的,然后从那里开始。

我目前正在研究,当我喜欢的时候,使用inherit_linear的方法使可反射类型的定义更容易。实际上我已经学了很多,但还有一段路要走。c++ 0x中的变化很可能在这方面有很大的帮助。

c++中还有另一个用于反射的新库,叫做RTTR(运行时类型反射,参见github)。

该接口类似于c#中的反射,并且不需要任何RTTI。

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

首先,为了在预处理器中更容易更清晰地编写它,我们将使用类型化表达式。类型化表达式只是将类型放在括号中的表达式。所以不是写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。它也与反射器类为友,这是为了它可以访问字段,即使它们是私有的:

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;
    };
};

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

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());
}

在可反射的Person类中使用print_fields的例子:

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

输出:

name=Tom
age=82

瞧,我们刚刚用c++实现了反射,用了不到100行代码。