Chinaunix

标题: C++ 如何反射调用方法 [打印本页]

作者: buaaspy    时间: 2013-12-10 10:58
标题: C++ 如何反射调用方法
Hi~最近想实现一个简单的C++反射应用,根据URI调用指定对象的指定方法,大致如下:
  1. //@(path="/api/ubuntu")
  2. DECLARE_DYN_CLASS(Test) {
  3. public:
  4.   Test()
  5.   { }

  6.   //@(path="/display", method="UPDATE")
  7.   void display()
  8.   { std::cout << "Test::display()" << std::endl; }
  9. };

  10. //@(path="/api/lxc")
  11. DECLARE_DYN_CLASS(Api) {
  12. public:
  13.   Api()
  14.   { }

  15.   //@(path="/create", method="POST")
  16.   void do_post()
  17.   { std::cout << "Api::do_post()" << std::endl; }

  18.   //@(path="/create", method="GET")
  19.   void do_get()
  20.   { std::cout << "Api::do_get()" << std::endl; }  
  21. };

  22. int main()
  23. {
  24.   Test* test = (Test*)ClassFactory::create_instance_for_name("Test");
  25.   test->display();

  26.   Api* api = (Api*)ClassFactory::create_instance_for_name("Api");
  27.   api->do_post();
  28.   api->do_get();

  29.   return 0;
  30. }
复制代码
问题是不知道如何反射调用类的成员函数,目前想到的方法是:
typedef void (ClassType::*PMF)(std::string request);
然后将成员函数指针强制转换为int*存到一个map里,可是会出现
链接错误,折腾了好几天了,哪位高手给指点迷津下~多谢了^^
Demo.rar (55.68 KB, 下载次数: 4)
作者: truekbcl    时间: 2013-12-11 10:09
Test* test = (Test*)ClassFactory::create_instance_for_name("Test");
test->display();
----------
这样的语法,表明Test与是否反射没有关系。反射应该是
Foo * f = create_instance("test");
f->invoke("display");

作者: ljpdxj    时间: 2013-12-11 10:21
本帖最后由 ljpdxj 于 2013-12-11 10:22 编辑

楼上太高深了!
作者: sxcong    时间: 2013-12-11 10:47
其实COM也算是反射吧,所有的函数都在IDL里写着。换成spring,只不过改成 xml了。不过spring可以赋值,这东西和程序读配置文件有多大区别呢
作者: buaaspy    时间: 2013-12-11 11:22
回复 2# truekbcl

确实如此,
目前我能做到的:
1)类对应的URI 映射到 类名;方法对应的URI和METHOD 映射到 方法名
2)类名 映射到 创建实例的方法指针;方法名 映射到 成员函数指针
接下来如何组装不知道了。。。其实不一定要达到真正的反射,只是想用C++简单模拟Spring根据注解装配对象和引导请求的过程,
还希望能给点进一步的建议,感激不尽


   
作者: zhujiang73    时间: 2013-12-11 12:09
回复 1# buaaspy


    这事还是改编译器比较好。
作者: 群雄逐鹿中原    时间: 2013-12-11 12:33
高端!大气!上档次!

作者: yao050421103    时间: 2013-12-11 12:44
这个问题的难点在于可变参数的封装,因为成员函数的参数是不确定的。
使用map的方式,只能针对特定签名形式的成员函数进行封装,而无法提供统一的接口。

可以考虑使用代码生成技术,使用python之类的脚本语言读入定义的C++文件,将class解析出来,然后针对各个成员函数生成Invoke方法。

buaaspy 发表于 2013-12-11 11:22
回复 2# truekbcl

确实如此,

作者: windoze    时间: 2013-12-11 13:52
本帖最后由 windoze 于 2013-12-11 13:57 编辑

其实吧,你为什么一定要把这些东西塞到一个class里呢?
为什么不干脆写成这样:

  1. typedef std::map<std::pair<std::string, std::string>, std::function<void()>> action_map;
  2. action_map the_map({
  3.         {
  4.                 {"GET", "/some/path"},
  5.                 [](){ std::cout << "GET /some/path" << std::endl; }
  6.         },
  7.         {
  8.                 {"POST", "/some/path"},
  9.                 [](){ std::cout << "POST /some/path" << std::endl; }
  10.         },
  11.         {
  12.                 {"GET", "/some/other/path"},
  13.                 [](){ std::cout << "GET /some/other/path" << std::endl; }
  14.         },
  15. });
复制代码
使用的时候你可以这样:

  1. action_map::const_iterator i=the_map.find({method, path});
  2. if(i==the_map.end()) {
  3.     // Send 404 response
  4. } else {
  5.     i->second();
  6. }
复制代码

作者: buaaspy    时间: 2013-12-11 18:09
回复 8# yao050421103

好主意!
作者: buaaspy    时间: 2013-12-11 18:10
回复 9# windoze

好主意!
   
作者: truekbcl    时间: 2013-12-11 19:43
buaaspy 发表于 2013-12-11 11:22
回复 2# truekbcl

确实如此,

想做到其他语言那样的反射,只可能在编译器做手脚,否则只能做到局部反射。最简单的大致如下:
1、定义一个method类,放置所有需要调用的函数类型
class method
{
    string _method_name;
    method * _next;
    const string & method_name() { return _method_name(); }
    virtual void invoke();
    virtual void invoke(int );
};
这表明,反射需要用到 obj->xxx(), obj->xxx(int)这两个类型的函数。


2、然后定义Class,
class Class
{
    method*>   _methods;
    method * get_method(const string & methodName);
};

3、在类中使用:
class A
{
    void test0(){ cout << "test"<<endl; }
    void test1(int x) { cout << "text " << x << endl; }
   
    // 以下2段代码,用宏生成,并且把method
    class method_A_test_0 : public method
   {
        method_A_test_0(const string & func):_method_name(func){}
        virtual void invoke() {test0(); }
   };

  class method_A_test_1 : public method
   {
        method_A_test_1(const string & func):_method_name(func){}
        virtual void invoke(int x) {test1(x); }
   };
};

4、
把A类中的method派生类放入到一个Class中,用静态变量Class即可。
比如在定义派生类时,生成链表,并放入到Class静态变量。

5、定义类工厂
class ClassFactory
{
   void register(const string & className);
   Class * get(const string & cn);
};


6、使用:
Class * p = ClassFactory.get("A");
p->get_method("test0").invoke();
p->get_method("test1").invoke(2);


改进:可以用模板与循环宏生成更多类型的参数,但是参数仍然只能是原始类型。但是也麻烦很多,我觉得意义不大。
另外,把method这个基类用宏来定义,如果在不同地方使用,修改也简单,其他代码都不变。
不建议使用反射,显然要慢很多。

作者: lost_templar    时间: 2013-12-12 01:04
本帖最后由 lost_templar 于 2013-12-12 01:10 编辑

组合使用 sfinae 与 enable_if,我实现了一个简单的,纯人肉的……

  1. #include <type_traits>
  2. #include <iostream>

  3. #define DETECT_METHOD(method)                                                   \
  4.     template<typename T, typename Sign>                                         \
  5.     struct class_has_method_##method                                         \
  6.     {                                                                           \
  7.         template <typename U, U> struct type_check;                             \
  8.         template <typename V> static char &chk(type_check<Sign, &V::method> *); \
  9.         template <typename   > static long  &chk(...);                          \
  10.         static bool const value = sizeof(chk<T>(0)) == sizeof(char);            \
  11.     }

  12. struct Test
  13. {
  14.     void display()
  15.     {
  16.         std::cout << "Test::display() called.\n";
  17.     }
  18. };

  19. struct Api
  20. {
  21.     void do_post()
  22.     {
  23.         std::cout << "Api::do_post() called.\n";
  24.     }
  25.     void do_get()
  26.     {
  27.         std::cout << "Api::do_get() called.\n";
  28.     }
  29. };

  30. DETECT_METHOD( display );

  31. template<typename T>
  32. typename std::enable_if<class_has_method_display<T, void( T::* )()>::value>::type display( T* pt )
  33. {
  34.     ( *pt ).display();
  35. }

  36. template<typename T>
  37. typename std::enable_if < !class_has_method_display<T, void( T::* )()>::value >::type display( T* pt )
  38. {
  39.     std::cerr << "No Method display defined for this struct.\n";
  40. }

  41. DETECT_METHOD( do_get );

  42. template<typename T>
  43. typename std::enable_if<class_has_method_do_get<T, void( T::* )()>::value>::type do_get( T* pt )
  44. {
  45.     ( *pt ).do_get();
  46. }

  47. template<typename T>
  48. typename std::enable_if < !class_has_method_do_get<T, void( T::* )()>::value >::type do_get( T* pt )
  49. {
  50.     std::cerr << "No Method do_get defined for this struct.\n";
  51. }

  52. DETECT_METHOD( do_post );

  53. template<typename T>
  54. typename std::enable_if<class_has_method_do_post<T, void( T::* )()>::value>::type do_post( T* pt )
  55. {
  56.     ( *pt ).do_post();
  57. }

  58. template<typename T>
  59. typename std::enable_if < !class_has_method_do_post<T, void( T::* )()>::value >::type do_post( T* pt )
  60. {
  61.     std::cerr << "No Method do_post defined for this struct.\n";
  62. }

  63. int main()
  64. {
  65.     /*
  66.     Test* test = (Test*)ClassFactory::create_instance_for_name("Test");
  67.     test->display();

  68.     Api* api = (Api*)ClassFactory::create_instance_for_name("Api");
  69.     api->do_post();
  70.     api->do_get();
  71.     */
  72.     Test* test = new Test {};
  73.     display( test );
  74.     do_post( test );
  75.     do_get( test );
  76.     delete test;
  77.     Api* api = new Api {};
  78.     display( api );
  79.     do_post( api );
  80.     do_get( api );
  81.     delete api;
  82.     return 0;
  83. }


复制代码
输出是:
  1. Test::display() called.
  2. No Method do_post defined for this struct.
  3. No Method do_get defined for this struct.
  4. No Method display defined for this struct.
  5. Api::do_post() called.
  6. Api::do_get() called.
复制代码
只是不知是否合乎楼主需求。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2