c++实现反射类


#ifndef UNISE_SINGLETON_H_
#define UNISE_SINGLETON_H_

#include <boost/noncopyable.hpp>
#include <pthread.h>

namespace unise
{

/// @brief Thread-safe, no-manual destroy Singleton template
template<typename T>
class Singleton : boost::noncopyable
{
public:
    /// @brief Get the singleton instance
    static T* get() {
        pthread_once(&_p_once, &Singleton::_new);
        return _instance;
    }

private:
    Singleton();
    ~Singleton();

    /// @brief Construct the singleton instance
    static void _new() {
        _instance = new T();
    }

    /// @brief  Destruct the singleton instance
    /// @note Only work with gcc
    __attribute__((destructor)) static void _delete() {
        typedef char T_must_be_complete[sizeof(T) == 0 ? -1 : 1];
        (void) sizeof(T_must_be_complete);
        delete _instance;
    }

    static pthread_once_t _p_once;      ///< Initialization once control
    static T*             _instance;    ///< The singleton instance
};

template<typename T>
pthread_once_t Singleton<T>::_p_once = PTHREAD_ONCE_INIT;

template<typename T>
T* Singleton<T>::_instance = NULL;

}

#endif





// factory.h
// Usage:
//     class BaseClass {  // base class
//       ...
//     };
//     REGISTER_FACTORY(BaseClass);
//     #define REGISTER_BASECLASS(name) REGISTER_CLASS(BaseClass, name)
//
//     class Sub1 : public BaseClass {
//       ...
//     };
//     REGISTER_BASE(Sub1);
//     class Sub2 : public BaseClass {
//       ...
//     };
//     REGISTER_BASE(Sub2);
//
// Note that REGISTER_BASE(sub1) should be put in cc file instead of h file,
// to avoid multi-declaration error when compile.
//
// Then you could get a new object of the sub class by:
//    Base *obj = BaseClassFactory::get_instance("Sub1");
// or get a get_singleton object of the sub class by:
//    Base* obj = BaseClassFactory::get_singleton("Sub1");
//
// This is convenient when you need decide the class at runtime or by flag:
//    string name = "Sub1";
//    if (...)
//      name = "Sub2";
//    Base *obj = BaseClassFactory::get_instance(name);
//
// If there should be only one instance in the program by desgin,
// get_uniq_instance could be used:
//    Base *obj = BaseClassFactory::get_uniq_instance();

#ifndef UNISE_FACTORY_H_
#define UNISE_FACTORY_H_

#include <map>
#include <string>
#include "unise/singleton.h"

namespace unise
{

/// @brief Like boost any but no type check on any_cast
class Any
{
public:
    Any() : _var_ptr(NULL) {}

    template<typename T>
    Any(const T &value) : _var_ptr(new Type<T>(value)) {}

    Any(const Any &other) : _var_ptr(other._var_ptr ? other._var_ptr->clone() : NULL) {}

    ~Any() {
        delete _var_ptr;
    }

    template<typename T>
    T *any_cast() {
        return _var_ptr ? &static_cast<Type<T> *>(_var_ptr)->_var : NULL;
    }

private:
    /// @brief Interface class to hold all types
    class Typeless
    {
    public:
        virtual ~Typeless() {}
        virtual Typeless *clone() const = 0;
    };

    /// @brief Type calss template to hold a specific type
    template<typename T>
    class Type : public Typeless
    {
    public:
        explicit Type(const T &value) : _var(value) {}
        virtual Typeless *clone() const {
            return new Type(_var);
        }
        T _var;             ///< The real variable of a specific type
    };
    Typeless *_var_ptr;     ///< Typeless variable pointer
};

/// @brief Concrete object factory `interface`
struct ConcreteFactory {
    typedef Any(*FactoryIntf)();
    FactoryIntf  get_instance;      ///< Function pointer to get instance
    FactoryIntf  get_singleton;     ///< Function pointer to get singleton
};

/// @note The use of a POD struct instead of an overridable class interface
/// makes map destruction safer and easier.
typedef std::map<std::string, ConcreteFactory> FactoryMap;
typedef std::map<std::string, FactoryMap> BaseClassMap;

/// @brief The use of a function instead of a global variable makes
/// construction safer. Because we are using __attribute__((constructor)).
BaseClassMap& g_factory_map();
}  // namespace unise

/// @brief Define an abstract factory for base_class, which uses
/// the global concrete object factory map to produce objects.
#define REGISTER_FACTORY(base_class) \
    class base_class ## Factory { \
        typedef ::unise::Any Any; \
        typedef ::unise::FactoryMap FactoryMap; \
    public: \
        static base_class *get_instance(const ::std::string &name) { \
            FactoryMap &map = ::unise::g_factory_map()[#base_class]; \
            FactoryMap::iterator iter = map.find(name); \
            if (iter == map.end()) { \
                return NULL; \
            } \
            Any object = iter->second.get_instance(); \
            return *(object.any_cast<base_class*>()); \
        } \
        static base_class* get_singleton(const ::std::string& name) { \
            FactoryMap& map = ::unise::g_factory_map()[#base_class]; \
            FactoryMap::iterator iter = map.find(name); \
            if (iter == map.end()) { \
                return NULL; \
            }\
            Any object = iter->second.get_singleton(); \
            return *(object.any_cast<base_class*>()); \
        } \
        static const ::std::string get_uniq_instance_name() { \
            FactoryMap &map = ::unise::g_factory_map()[#base_class]; \
            if (map.empty() || map.size() != 1) { \
                return ""; \
            } \
            return map.begin()->first; \
        } \
        static base_class *get_uniq_instance() { \
            FactoryMap &map = ::unise::g_factory_map()[#base_class]; \
            if (map.empty() || map.size() != 1) { \
                return NULL; \
            } \
            Any object = map.begin()->second.get_instance(); \
            return *(object.any_cast<base_class*>()); \
        } \
        static bool is_valid(const ::std::string &name) { \
            FactoryMap &map = ::unise::g_factory_map()[#base_class]; \
            return map.find(name) != map.end(); \
        } \
    }; \
 
/// @brief Define a concrete object factory for sub_calss, and register it to
/// the base_class entry in global object factory map, on application
/// start.
#define REGISTER_CLASS(base_class, sub_class) \
    namespace { \
    ::unise::Any sub_class##get_instance() { \
        return ::unise::Any(new sub_class()); \
    } \
    ::unise::Any sub_class##get_singleton() { \
        return ::unise::Any(::unise::Singleton<sub_class>::get()); \
    } \
    __attribute__((constructor)) void register_factory_##sub_class() { \
        ::unise::FactoryMap &map = ::unise::g_factory_map()[#base_class]; \
        if (map.find(#sub_class) == map.end()) { \
            ::unise::ConcreteFactory factory = {&sub_class##get_instance, \
                                                 &sub_class##get_singleton}; \
            map[#sub_class] = factory; \
        } \
    } \
    }

#endif



// factory.cc
#include "unise/factory.h"

namespace unise
{

BaseClassMap& g_factory_map()
{
    static BaseClassMap factory_map;
    return factory_map;
}

}  // namespace unise


  在很多程序设计中,经常会遇到这样的需求,即可以通过类的名字得到对应类型的对象,尤其是一种数据需要很多策略处理的时候。比如对于网页类型的识别,一篇网页可能是视频类型、新闻类型、图片类型、网站首页、百科等很多类型中的一种,网页类型对于搜索引擎来说是非常重要的,计算rank的时候网页类型往往是一个非常重要的因子。具体实现的时候,网页类型识别的策略可以封装在类中,这样一个策略就可以设计成一个类。但是后期随着对网页理解的越来越深入,就会出现以下两种情景:

  1. 需要添加新的网页类型,因此需要添加对应的类型识别类;
  2. 有些类型已经不再需要或者是进行了重新划分,那么需要删除掉这些类型或者是让这些类型识别模块不再生效。
  这种应用场景下,添加或移除网页类型识别模块时,最好能够非常方便,并且不会影响到已有的程序。
  一个比较好的方案是,定义一个类型识别的基类PageTypeDetector,每个类型识别策略都继承自这个基类。比如需要一个新闻页识别的新策略,那么定义类NewsPageTypeDetector,该类继承PageTypeDetector。在添加NewsPageTypeDetector到网页类型识别的主程序时,在配置文件中进行配置,添加NewsPageTypeDetector类,让该类生效,而主程序和其他类型识别策略的程序都不需要进行改动。另外,如果不再需要图片网页类型识别,那么就把图片类型识别对应的类名直接从配置发文件中删除即可。
  为了实现上述目标,我们需要从类名到类型的映射,可以称为反射。因为配置文件中的信息在程序内部得到的都是纯字符串,程序需要根据字符串生成对应的识别类。当然,这个在本身已包含反射机制的程序设计语言中很容易实现,比如JAVA,但是由于C++中语言本身不支持这种机制,因此,需要用其他的方法来模拟这种机制。
  首先,我们从最简单的方式开始,定义一个工厂方法,该方法负责根据类名生成相应类的对象,函数定义可以如下:
1
PageTypeDetector* DetectorFactoryCreate( const  string& class_name);

  生成新闻网页类型识别的类可以如下调用:

1
PageTypeDetector* news_page_detector = DetectorFactoryCreate( "NewsPageTypeDetector" );

  DetectorFactoryCreate工厂方法中的实现逻辑大致是这样:

1
2
3
4
5
if  (class_name ==  "NewsDocTypeDetector" ) {
      return  new  NewsDocTypeDetector;
else  if  (class_name ==  "..." ) {
      return  new  ...;
}

  使用如上工厂方法创建类的方式具有非常明显的缺陷,每添加或删除一个新类,都需要修改工厂方法内的程序(添加if判断或者删除if判断,并且需要添加新类的头文件或者类声明),当然了,因为程序有了修改所以就需要重新编译(如果很多其他模块依赖该程序的话,重新编译也是一笔不小的开销)。显然,这种方式虽然简单,但是极不易于维护。

  这里,提出一个使用非常方便并且易于维护的解决方案,那就是使用宏。虽然c++创始人 Bjarne Stroustrup极力反对使用宏,但是在一些特定的场景中合理的使用宏会带来意想不到的效果。
  首先,从使用宏最简单的一个实现开始,目标是可以通过类的名字得到相应的对象,因此应该有个方法类似于如下:
1
Any GetInstanceByName( const  string& class_name);

  返回值为Any,因为不知道返回值究竟是什么类型,所以假定可以返回任何类型,这里的Any使用的是Boost中的Any。该方法中需要new一个类型为class_name的对象返回,那么应该如何new该对象呢?借用上面使用工厂方法的经验,可以进一步使用工厂类,对于每个类,都有一个相应的工厂类ObjectFactoryClassName,由该工厂类负责生成相应的对象(为什么要使用工厂类?后面再作简单介绍)。

  有了工厂类,也需要将类名与工厂类对应起来,对应方式可以使用map<string, ObjectFactory*> object_factory_map,object_factory_map负责从类名到相应工厂类的映射,这样,就可以通过类的名字找到对应ObjectFactory,然后使用ObjectFactory生成相应的对象。但是如何将相应的工厂类添加到object_factory_map中去呢,我们需要在定义新类的时候就将对应的工厂类添加到object_factory_map中,这里需要一个函数负责添加工厂类到object_factory_map中去(为什么需要一个函数负责?最后作简单说明)。

  负责将新类对应的工厂类添加到全局变量object_factory_map的函数必须在使用object_factory_map之前执行。gcc中有一个关键字__attribute__((constructor)) ,使用该关键字声明的函数就可以在main函数之前执行。到现在,程序的结构类似这样:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 负责实现反射的文件reflector.h:
 
map<string, ObjectFactory*> object_factory_map;
Any GetInstanceByName( const  string& name) {
     if  (object_factory_map.find(name) != object_factory_map.end()) {
         return  object_factory_map[name]->NewInstance();
     }
     return  NULL;
}
 
#define REFLECTOR(name) \
class  ObjectFactory##name { \
public : \
   Any NewInstance() { \
     return  Any( new  name); \
     } \
}; \
void  register_factory_##name() { \
     if  (object_factory_map.find(#name) == object_factory_map.end()) { \
       object_factory_map[#name] =  new  ObjectFactory##name(); \
     } \
} \
__attribute__(constructor) void  register_factory##name();
 
 
// 调用文件test.cc
class  TestClass {
public :
   void  Out() {
     cout <<  "i am TestClass"  << endl;
   }
};
REFLECTOR(TestClass);
 
// main函数
int  main() {
   Any instance = GetInstanceByName( "TestClass" );
   TestClass* test_class = instance.any_cast<TestClass>();
   return  0; 
}

     注:##是连接符,将两个字符串连接起来,#是将name作为字符串处理。

  到这里还有一个问题,全局变量ObjectFactoryMap是不能放在头文件中的,因为如果多个类包含该头文件时,就会出现重复定义的错误,是编译不过的。因此,将该变量放在其源码reflector.cc文件中:

1
2
3
4
5
6
7
8
9
10
11
12
// reflector.h,包含声明:
extern  map<string, ObjectFactory*> object_factory_map;
Any GetInstanceByName( const  string& name);
 
// reflector.cc:
map<string, ObjectFactory*> object_factory_map;
Any GetInstanceByName( const  string& name) {
     if  (object_factory_map.find(name) != object_factory_map.end()) {
         return  object_factory_map[name]->NewInstance();
     }
     return  NULL;
}

  上述程序编译能够通过,但是运行时出错,后来定位到是在使用全局变量object_factory_map时出错,经过调试了很久,在网上查相应的资料也没找到。经过不停的尝试,才发现原来是全局变量object_factory_map没有初始化,在仔细的测试了以后发现,是__attribute__((constructor))与全局变量类构造函数的执行顺序的问题,一般全局变量是在__attribute__(constructor)前完成初始化的,但是如果__attribute__是在main函数所在的文件,而全局变量是在其他文件定义的,那么__attribute__(constructor)就会在全局变量类构造函数前面执行,这样,上面的程序在全局变量类还没有完成初始化,也就是还没有执行构造函数,就在__attribute__(constructor)声明的函数中进行了使用,因此会出现问题。不过,在执行__attribute__时已经看到了全局变量的定义,只是没有执行全局变量的构造函数(这里,如果全局变量不是类,而是普通类型,是没有问题的)。所以,程序的结构还需要进一步修改。

    现在解决如何定义和使用全局变量object_factory_map的问题。既然我们不能直接使用该变量,那么可以通过显示调用函数来返回该变量,如果直接在函数中new一个对象返回的话,那么每次调用都会new一个新的对象,而我们全局只需要一个该对象,这时该是static出现的时候了。我们可以这样定义:
1
2
3
4
5
// reflector.cc
map<string, ObjectFactory*>& object_factory_map() {
     static  map<string, ObjectFactory*>* factory_map =  new  map<string, ObjectFactory*>;
     return  *factory_map;
}

这样定义还有另外一个优点,程序只是在真正需要调用g_objectfactory_map时才会生成相应的对象,而如果程序没有调用,也不会生成对应的对象。当然,在这里new一个对象的代价不大,但是如果new的对象非常耗时的话,这种使用函数中static变量代替全局变量方法的优势就非常明显了。到现在反射程序变成如下这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 负责实现反射的文件reflector.h:
 
// 工厂类的基类
class  ObjectFactory {
  public :
   virtual  Any NewInstance() {
     return  Any();
   }
};
 
map<string, ObjectFactory*>& object_factory_map();
Any GetInstanceByName( const  string& name);
 
#define REFLECTOR(name) \
class  ObjectFactory##name :  public  ObjectFactory { \
  public : \
   Any NewInstance() { \
     return  Any( new  name); \
   } \
}; \
void  register_factory_##name() { \
     if  (object_factory_map().find(#name) == object_factory_map().end()) { \
       object_factory_map()[#name] =  new  ObjectFactory##name(); \
     } \
} \
__attribute__(constructor) void  register_factory##name()
 
 
 
// reflector.cc
 
map<string, ObjectFactory*>& object_factory_map() {
     static  map<string, ObjectFactory*>* factory_map =  new  map<string, ObjectFactory*>;
     return  *factory_map;
}
 
Any GetInstanceByName( const  string& name) {
     if  (object_factory_map().find(name) != object_factory_map().end()) {
         return  object_factory_map()[name]->NewInstance();
     }
     return  NULL;
}

  到现在接近尾声了,不过在很多时候,我们都是在已有基类的基础上添加新的类,就好比上述网页识别的程序,各个识别策略类都继承共同的基类,这样,我们可以进一步修改反射程序,将GetInstanceByName放在另外一个类中,返回的是基类的指针,因此在定义基类时也需要注册一个宏,如下所示,同时需要修改objector_factory_map的结构为map<string, map<string, ObjectFactory> >,第一个key是基类的名字,第二map中的key是生成类的名字,基类宏的定义类似如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define REFLECTOR_BASE(base_class) \
class  base_class##Reflector { \
  public : \
   static  base_class* GetInstanceByName( const  string& name) { \
      map<string, ObjectFactory*>& map = object_factory_map()[#base_class]; \
      map<string, ObjectFactory*>::iterator iter = map.find(name); \
      if  (iter == map.end()) { \
        return  NULL; \
      } \
       Any object = iter->second->NewInstance(); \
       return  *(object.any_cast<base_class*>()); \
} \
};

  这里就不再详细讲修改后的代码了,有兴趣的朋友可以自己实现。

 

注:

  至于上面为什么需要使用工厂类,而不是直接new一个对应的对象返回,原因是直接new是不可以的。例如如下定义:
1
2
3
4
#define REFLECT(name) \
Any GetInstanceByName( const  string& class_name) {
     return  Any( new  name);
}

  如果是多个类使用的话,那么就会出现多个函数的定义。如果也借助工厂类的实现,如下实现:

1
2
3
4
#define REFLECT(name) \
Any GetInstanceByName##name( const  string& class_name) {
     return  Any( new  name);
}

   这样是不会出现重复定义了,但是这样在生产新的对象时需要指定特定的函数,这不又回到原点了吗?因此工厂类充当的是个中介的角色,我们可以保存工厂类,然后根据名称寻找特定的工厂类来生成对应的对象。

注:

 为什么需要使用函数添加工厂类?因为在程序中,全局空间中只能是变量的声明和定义,而不能是语句,例如:

可以这样写:
int a = 10;
int main() {}
但是不能这样写:
int a;
a = 10;
int main() {}
 

需要注意的知识点:

  1. 工厂模式;
  2. 全局变量的定义需要注意,不能定义在头文件中(当如,如果经过特殊处理,例如使用#ifndef保护另说);
  3. Any类型的实现;(准备写另外一篇文章来探讨其实现细节)
  4. 宏的定义以及使用;(基本覆盖了宏的所有知识)
  5. 全局变量构造函数与__attribute__((constructor))的执行顺序;(调试了很久)
  6. __attribute__((constructor))的问题;(编译器有关,放在函数定义前或定义后)
  7. 全局空间只能是声明或者定义,不能是语句;
  8. static在函数中的使用;
  9. 全局变量类的定义与使用。

http://www.cnblogs.com/feixue/p/cplusplus_reflector.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可升级, Reflector是一款比较强大的反编译工具,相信很多朋友都用过它,但reflector本身有很多局限性,比如只能一个一个的查看方法等,但幸好reflector支持插件功能目前网上有很多reflector的插件,本人找取了两个应用范围很广,并且广受好评的插件:Reflector.FileDisassembler和Reflector.FileGenerator和Reflector进行了整合,放在了一个压缩包中,大家可以直接解压缩后就开始使用,其中FileGenerator在网上没有现成dll,本人编译后将其取出,放在了压缩包中,一直在苦苦找寻的朋友这次可以拿来直接用了:) 本压缩包中Reflector.exe已经升级为最新的5.0版本,具体的使用方法不多说了,只简单介绍一下本压缩包的使用步骤: 先下载本压缩包并解压缩,运行其中的Reflector.exe(主程序),初次运行会弹出错误提示对话框,因为引用的两个插件DLL是按照我本地实际情况配置的,所以你还需要根据你的实际情况来从新配置一下,方法很简单,我们拿引用FileGenerator.dll来做一个范例, 首先介绍一下Filegenerator,FileGenerator插件的作用是:根据dll文件,把里面的源文件导出成文件,导出来的文件除了没有注释,变量名也变了,其它的可谓是没有差别。对于一些比较好的控件,如果不是开源的,完全可以导出成文件,然后自己加上注释,少许修改,很好的利用起来。(不开源的dll,用起来也不放心啊) 具体的初始化设置方法:点击View->Add-Ins...,弹出一个窗口,然后点击Add->选择文件夹里面的:"FileGenerator.dll",点击close. 然后回到Reflector窗口,Tool->Generator File(s)... 右边就出现了插件的窗口,选中左边的dll文件,点击右边的导出文件,源代码就全部导出来了! 另一个插件Reflector.FileDisassembler.dll设置方法也和这个一样,另外再提供一些Reflector的相关插件下载,

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值