登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

秒大刀 博客

好好学习 天天向上

 
 
 

日志

 
 
 
 

[转] C++ Reflection  

2007-10-27 03:15:13|  分类: C/C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

        反射(Reflection)是.net平台下一个很牛的特性。最近看到有位牛人在做C++中的reflection,可谓大开眼界。

虽然这篇文章在互联网中随处可以看到,但我还是要再次的转发,因为实在是太精彩了。

Part 1

       Setting Goals

So you want to build a reflection system in C++? Well, I assume so since you're reading this. =) Ok, enough jokes...

There are a few ways to go about building a reflection system in C++ and there are different levels that we can accomplish. For instance, to create a reflection based system that works without any modification to standard C++ types and classes would be very challenging, to say the least. In order to help us with this problem, we'll start off with a set of goals so that we know what we want to aim for.

  1. Maintainable - It would be nice if (and when) we need to make modifications or bug fixes the code base is clean and logical.
  2. Robust - I don't like when things only work when sprinkling some magic pixie dust, so let's make this thing as robust as possible. This means we'll be using templates rather than MACROs to do our magic.
  3. Effecient - The idea isn't to create a reflection system takes five minutes to perform something, we want a small and fast system.
  4. Flexible - Let's shoot for making an extensible system so that we can add to it later without too many problems.

I think that those goals are good enough to get started.

Where to Start

In order to being programming this thing we need to understand what type of information we want. For now the only things that we will concern ourselves with are unique ID for the class and the name of the class; we'll add to it later to actually make it do some cool stuff.

To start off we'll need the following classes to provide us with some basic run-time information: RuntimeInfo and RuntimeClass.

1: struct RuntimeInfo

2: {

3: RuntimeInfo( unsigned int classId, const char* name );

4:

5: static const unsigned int CLASS_NAME_LENGTH = 32;

6:

7: unsigned int ClassId;

8: char ClassName[ CLASS_NAME_LENGTH ];

9: };

This class will define all of the cool details for our class; currently we only need to know the ID for the and the name for the class. The ID could be used to create a new type of class or to compare is two classes are the same.

1: template< class T, class BaseClass, unsigned int ClassId >

2: class RuntimeClass : public BaseClass

3: {

4: public:

5: static inline RuntimeInfo* GetRuntimeInfo() { return &_info; }

6:

7: protected:

8: static RuntimeInfo _info;

9: };

10:

11: template< class T, class BaseClass, unsigned int ClassId >

12: RuntimeInfo

13: RuntimeClass< T, BaseClass, ClassId >::_info( ClassId, typeid( T ).name() );

I hope you're up to speed with your templates =). This class is a tricky way of injecting information that is shared by each type of class we'll be using in our reflection system. All this class currently has is the runtime information that is created based on the templated information.

The next step is to create the base class we'll be using for all of the objects in our system; it's appropriately named Object.

1: class NullObject{};

2: class Object : public RuntimeClass< Object, NullObject, 0x0A >

3: {

4: public:

5: Object();

6: virtual ~Object();

7: };

One of the first things to notice is that it implements our RuntimeClass and passes in the type that it is, the type it derives from, and the ID to use for the class. As you can see, there is another class NullObject here; this is used so that are template parameters will be satisfied.

That's it! I've given a very brief breakdown of what it takes to get started building a reflection system. Next time we'll learn how set it up so we can create new instances and set some properties on our classes. I'll leave you with a little sample app that shows it working.

1: class MyClass : public RuntimeClass< MyClass, Object, 0xF0 >

2: {

3: public:

4: MyClass();

5: virtual ~MyClass();

6: };

7:

8: int

9: _tmain( int argc, _TCHAR* argv[] )

10: {

11: Object o;

12: MyClass c;

13:

14: cout << "-- OBJECT --" << endl;

15: cout << "\tClass ID: " << o.GetRuntimeInfo()->ClassId << endl;

16: cout << "\tClass Name: " << o.GetRuntimeInfo()->ClassName << endl;

17: cout << endl;

18:

19: cout << "-- MYCLASS --" << endl;

20: cout << "\tClass ID: " << c.GetRuntimeInfo()->ClassId << endl;

21: cout << "\tClass Name: " << c.GetRuntimeInfo()->ClassName << endl;

22: cout << endl;

23:

24: cout << "Press enter to exit." << endl;

25: cin.get();

26:

27: return 0;

28: }

Part 2

Ok, last time we got the basics of the system in place. Tonight we'll fix up some of the stuff that we had before (in case you were wondering, I'm figuring this out at the same time as you guys), then get the invocation of some functions.

First, our RuntimeInfo struct from last time is a little lacking and should be implementing a little better.

1: class MapComparer

2: {

3: public:

4: bool operator()( const char* s1, const char* s2 ) const

5: {

6: return strcmp(s1, s2) < 0;

7: }

8: };

9:

10: template< class T >

11: class RuntimeInfo

12: {

13: public:

14: static const unsigned int CLASS_NAME_LENGTH = 50;

15:

16: RuntimeInfo( unsigned int classId, const char* name );

17:

18: unsigned int GetClassId() const;

19: const char* GetClassName() const;

20:

21: void RegisterMethod( Method< T >* method );

22:

23: Method< T >* GetMethod( const char* name );

24: std::map< const char*, Method< T >*, MapComparer >* GetMethods();

25:

26: private:

27: unsigned int _classId;

28: char _className[ CLASS_NAME_LENGTH ];

29: std::map< const char*, Method< T >*, MapComparer > _methods;

30: };

The first changes to note is the change from a struct to a class. This is technically only a semantic change in C++, but we'll be adding functionality via functions so it's better to call it a class. Next, I moved the class ID and name to private member data, which it probably should have been in the first place. You should also notice that the class is now a templated class. The templace type will tell us the type for member methods will be called. The last important thing is the addition of the RegisterMethod function. This allows us to register any member function with the class so that we can later invoke it.

Alright, now that we've updated the RuntimeInfo class to support our Methods, let's go ahead and define those. =)

1: template< class OwnerType >

2: class Method

3: {

4: public:

5: typedef void (OwnerType::*m0)();

6:

7: Method( const char* name, m0 method );

8: virtual ~Method();

9:

10: const char* GetName() const;

11:

12: void Invoke( OwnerType& obj );

13:

14: protected:

15: Method( const char* name );

16:

17: private:

18: char* _methodName;

19: m0 _method;

20: };

Our Method class is pretty straight forward; it will be used to define functions that have no return type, and for now, take no parameters. First, this class is templated to define the type for the function that is being passed in. For instance, if I have a class MyInt and have exposed Double(), the type would be MyInt. Next thing to note is the typedef for the function signature; this is a function that returns void and takes no parameters. The next interesting piece is the Invoke() function; this is where all of the action will actually be done.

You might be wondering why I've created a protected Method constructor... that's a good question. The reason is that when I start extending this class, I want to be able to take advantage of the base functionality for storing the name.

Alright, so this gives us the ability to call functions that don't return anything, let's extend it so that we can call a function and have it return something interesting.

1: template< class OwnerType, class ReturnType >

2: class Function : public Method< OwnerType >

3: {

4: public:

5: typedef ReturnType (OwnerType::*f0)();

6:

7: Function( const char* name, f0 function );

8: virtual ~Function();

9:

10: ReturnType Invoke( OwnerType& obj );

11:

12: private:

13: f0 _method;

14: };

The major difference between this class and the Method class is that this class takes two template arguments: the type for the member function and the return type for the function. Also notice the overloading of the Invoke() function so that it returns the proper type.

And that's all there is to it. I'll post a little sample application that shows it in use.

1: int

2: _tmain( int argc, _TCHAR* argv[] )

3: {

4: Object o;

5: MyInt i;

6:

7: i.SetValue( 30 );

8:

9: Method< MyInt >* imi = i.GetRuntimeInfo()->GetMethod( "Double" );

10: Function< MyInt, int >* imi2 =

11: static_cast< Function< MyInt, int >* >( i.GetRuntimeInfo()->GetMethod( "GetValue" ) );

12:

13: cout << "-- OBJECT --" << endl;

14: cout << "\tClass ID: " << o.GetRuntimeInfo()->GetClassId() << endl;

15: cout << "\tClass Name: " << o.GetRuntimeInfo()->GetClassName() << endl;

16: cout << endl;

17:

18: cout << "-- MYINT --" << endl;

19: cout << "\tClass ID: " << i.GetRuntimeInfo()->GetClassId() << endl;

20: cout << "\tClass Name: " << i.GetRuntimeInfo()->GetClassName() << endl;

21: cout << "\tOriginal Value: " << i.GetValue() << endl;

22: imi->Invoke( i );

23: cout << "\tDouble Value: " << i.GetValue() << endl;

24: cout << "\tGetValue: " << imi2->Invoke( i ) << endl;

25: cout << endl;

26:

27: cout << "Press enter to exit." << endl;

28: cin.get();

29:

30: return 0;

31: }

So there we have it. We've built a reflection system in C++. Is it the most robust and feature rich, no, most definately not. For instance, there is not way to pass in arguments to either the Method or Function class, there is no way to call the constructor, although, arguably that is just registering the constructor with by using the Function class. But overall, I think I've pretty much met the goal that I set out to achieve.

If you have any questions or comments, I'd be happy to answer them, but I think I'm relatively happy with the reflection system I have in C++... for now atleast. =)

Source Code: Reflection.zip

David 's blog: http://blogs.msdn.com/dowens/default.aspx

  评论这张
 
阅读(3106)| 评论(1)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018