免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 8137 | 回复: 25
打印 上一主题 下一主题

[C++] 前两天有人提到C++没反射,写了个实验版 [复制链接]

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-10-25 14:04 |只看该作者 |倒序浏览
本帖最后由 windoze 于 2015-10-26 00:34 编辑

C++的确没反射,我一开始搞了一个code generator,后来发现这么搞对于build system实在影响太大,所以后来我干脆转去用宏了。
代码在 https://github.com/windoze/cxl
这是一个header only库,大概这么用:
如果你有一个struct SC

  1. struct SC {
  2.     int m1;
  3.     const double m2;

  4.     SC(int mm1, double mm2, int mm3) : m1(mm1), m2(mm2), m3(mm3) {}
  5.     int get_m3() const { return m3; }
  6.     void set_m3(int x) { m3 = x; }

  7.     int get_m4() const { return 100; }
  8. private:
  9.     int m3;
  10. };
复制代码
在外面定义几个宏:

  1. CXL_EXT_BEGIN_REFLECTED(SC, 4)
  2.         CXL_REFLECTED_MEMBER(0, m1)
  3.         CXL_REFLECTED_MEMBER(1, m2, CXL_SQL_FIELD("field2"))
  4.         CXL_REFLECTED_ATTRIBUTE(2, int, m3, CXL_MEM_GETTER(get_m3), CXL_MEM_SETTER(set_m3))
  5.         CXL_REFLECTED_RO_ATTRIBUTE(3, int, m4, CXL_MEM_GETTER(get_m4))
  6. CXL_EXT_END_REFLECTED()
复制代码
然后这个struct SC就可以支持反射了,比如你可以动态读写field:

  1. SC sc(10, 5.5, 84);
  2. cxl::set(0, sc, 42);
  3. assert(sc.m1==42);
  4. assert(cxl::get<int>(0, sc)==42);
复制代码
如果,field不能写或不能用你提供的类型读写,则会在运行时抛出std::bad_cast,而不是编译错误,比如:

  1. try {
  2.         cxl::set(1, sc, 7.5);
  3.         cxl::get<std::string>(0, sc);
  4.         assert(false);
  5. } catch(std::bad_cast&) {
  6. }
复制代码
你还可以访问每个字段的一些metadata,目前预定义的有name, key, sql_table, sql_field, json_key, xml_node, xml_namespace, csv_field, 如要扩展可以在cxl/reflection/reflection_impl.hpp里加,三句话就行。
这些metadata可以用来干一些有用的事,比如:

  1.     // 玩具SQL生成器
  2.     std::string keys;
  3.     for(size_t i=0; i<cxl::tuple_size<SC>::value; i++) {
  4.         if(!keys.empty()) keys+=", ";
  5.         keys+=get_element_sql_field<SC>(i);
  6.     }
  7.     keys = "SELECT " + keys + " FROM " + cxl::get_sql_table<SC>() + " WHERE "
  8.            + cxl::get_element_sql_field<SC>(0) + "=?";
  9.     assert(keys == "SELECT m1, field2, m3, m4 FROM SC WHERE m1=?");
复制代码

评分

参与人数 2可用积分 +18 信誉积分 +10 收起 理由
evaspring + 8 赞一个!
fender0107401 + 10 + 10 很给力!

查看全部评分

论坛徽章:
89
水瓶座
日期:2014-04-01 08:53:31天蝎座
日期:2014-04-01 08:53:53天秤座
日期:2014-04-01 08:54:02射手座
日期:2014-04-01 08:54:15子鼠
日期:2014-04-01 08:55:35辰龙
日期:2014-04-01 08:56:36未羊
日期:2014-04-01 08:56:27戌狗
日期:2014-04-01 08:56:13亥猪
日期:2014-04-01 08:56:02亥猪
日期:2014-04-08 08:38:58程序设计版块每日发帖之星
日期:2016-01-05 06:20:00程序设计版块每日发帖之星
日期:2016-01-07 06:20:00
2 [报告]
发表于 2015-10-25 16:15 |只看该作者
哈哈,阿猫猛啊。

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
3 [报告]
发表于 2015-10-25 16:28 |只看该作者
回复 2# fender0107401

之前就是你发帖子说这事的吧,我写这个东西就是验证一下思路,,因为我倾向于把尽量多的事放在编译期做完,所以选择了一个把任意结构体往std::tuple上靠拢的方案,运行时的动态性用variant。
你试试看这个方向可行不可行
@yulihua49

论坛徽章:
89
水瓶座
日期:2014-04-01 08:53:31天蝎座
日期:2014-04-01 08:53:53天秤座
日期:2014-04-01 08:54:02射手座
日期:2014-04-01 08:54:15子鼠
日期:2014-04-01 08:55:35辰龙
日期:2014-04-01 08:56:36未羊
日期:2014-04-01 08:56:27戌狗
日期:2014-04-01 08:56:13亥猪
日期:2014-04-01 08:56:02亥猪
日期:2014-04-08 08:38:58程序设计版块每日发帖之星
日期:2016-01-05 06:20:00程序设计版块每日发帖之星
日期:2016-01-07 06:20:00
4 [报告]
发表于 2015-10-25 17:25 |只看该作者
回复 3# windoze

是啊,之前就是我希望C++里面能有反射。那个时候想用C++来代替PHP来做网站的后端。

   

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
5 [报告]
发表于 2015-10-25 20:50 |只看该作者
楼主雄壮V5,87侧漏!

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
6 [报告]
发表于 2015-10-26 13:14 |只看该作者
sf里面有几个cpp的反射库。楼主的宏里面加如数字,以及索引,对于维护比较麻烦,可以不用。对于自定义的成员,写traits解决。我个人看法,函数反射基本没有什么用处。要处理成员,外部提供算法比较好,而且好扩展,好维护。

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
7 [报告]
发表于 2015-10-26 14:34 |只看该作者
本帖最后由 yulihua49 于 2015-10-26 15:01 编辑
windoze 发表于 2015-10-25 16:28
回复 2# fender0107401

之前就是你发帖子说这事的吧,我写这个东西就是验证一下思路,,因为我倾向于把 ...

我在做数据库时,写几个通用函数:
select,fetch,insert,update,delete,execute。。。。。。
这些函数在编译时并不知道具体的数据结构。
他们必须解决自动绑定的问题。否则,动辄上百的列都让用户去做,包装的意义少了一大半。

一个例子,如果写一个类似ORACLE的sqlldr的东西,能够加载任意表进数据库,或者反之。
用你这方法,我不知道怎么弄。运行时人家才告诉我表名和对应的数据(编译期解决不了这些问题)。
退一步讲,有时应用程序是知道数据结构的,他把数据提交给上述函数,那些函数是不知道这些数据的结构的。
真反射可以解决上述问题,但是:
一个对象里的内容,有些是要对应到数据库,有些不是,有些临时不是,怎么表示?
一个对象里的内容,内存的类型和格式与数据库的不符,怎么办?
一个对象里的内容,有些是要序列化到JSON或XML,有些不是,有些临时不是,怎么表示?
需要类似JAVAbean的对象,还要加上一些属性来表示。


论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
8 [报告]
发表于 2015-10-26 15:04 |只看该作者
本帖最后由 yulihua49 于 2015-10-26 15:06 编辑
truekbcl 发表于 2015-10-26 13:14
sf里面有几个cpp的反射库。楼主的宏里面加如数字,以及索引,对于维护比较麻烦,可以不用。对于自定义的成员 ...


把Object序列化成JSON、XML,或反之。
各种,很多的Object,只想写两个函数:序列化和反序列化。处理各种对象。怎么办?
现在的状况,每种Object都要写两个,太不优雅了!

论坛徽章:
44
15-16赛季CBA联赛之浙江
日期:2021-10-11 02:03:59程序设计版块每日发帖之星
日期:2016-07-02 06:20:0015-16赛季CBA联赛之新疆
日期:2016-04-25 10:55:452016科比退役纪念章
日期:2016-04-23 00:51:2315-16赛季CBA联赛之山东
日期:2016-04-17 12:00:2815-16赛季CBA联赛之福建
日期:2016-04-12 15:21:2915-16赛季CBA联赛之辽宁
日期:2016-03-24 21:38:2715-16赛季CBA联赛之福建
日期:2016-03-18 12:13:4015-16赛季CBA联赛之佛山
日期:2016-02-05 00:55:2015-16赛季CBA联赛之佛山
日期:2016-02-04 21:11:3615-16赛季CBA联赛之天津
日期:2016-11-02 00:33:1215-16赛季CBA联赛之浙江
日期:2017-01-13 01:31:49
9 [报告]
发表于 2015-10-26 17:08 |只看该作者
回复 7# yulihua49

你的理解有误,尽管“类型”可以有任意多个,但这些“类型”并不是凭空生成的,每个类型都是基本类型的排列组合,这一点就算对于数据库也一样。
反射的意义不在于处理任意类型,事实上你也没办法写出直接处理任意类型的程序,但反射能告诉你一个特定的类型是由哪些基本类型排列组合而成的,这样你就可以用一组处理基本类型的代码处理任意类型。
以std::tuple为例,它本身是一个template,可以包含任意多个任意类型的成员,如果我要写一个to_json以支持任意一个tuple,我可以这么做:

  1. std::string to_json(int v) {...}
  2. std::string to_json(double v) {...}
  3. std::string to_json(const std::string v) { return std::string("\"") + escape(v) + "\""; }   // escape生成转义后的字符串
  4. ...
  5. // 以上为处理基本类型的代码


  6. template<T> std::string to_json(const std::vector<T> &v) {
  7.     std::string s("[");
  8.     bool first=true;
  9.     for(auto &&e: v) {
  10.         if(first) first=false;
  11.         else s+=", ";
  12.         s+=to_json(e);
  13.     }
  14.     s+= "]";
  15.     return s;
  16. }

  17. template<K, V> std::string to_json(const std::map<K, V> &v) {
  18.     std::string s("{");
  19.     bool first=true;
  20.     for(auto &&e: v) {
  21.         if(first) first=false;
  22.         else s+=", ";
  23.         s+= to_json(e.first) + ":" + to_json(e.second);
  24.     }
  25.     s+= "}";
  26.     return s;
  27. }
  28. //以上为处理STL容器的代码


  29. // 遍历tuple
  30. template<size_t I, size_t N> struct tuple_to_json_impl {
  31.     template<typename T> std::string operator()(const T& t) const {
  32.         std::string s(to_json(std::get<I>(t)));
  33.         // Not the last element, add a separator
  34.         if (I<N-1) s+=", ";
  35.         s+=tuple_to_json_impl<I+1, N>()(t);
  36.         return s;
  37.     }
  38. };
  39. // 遍历tuple结束
  40. template<size_t N> struct tuple_to_json_impl<N, N> {
  41.     template<typename T> std::string operator()(const T& t) const {}
  42. };
  43. typename<typename ...T> std::string to_json(const std::tuple<T...> &t) {
  44.     std::string s("[");
  45.     s+= tuple_to_json_impl<std::tuple<T...>>()(t) + "]";
  46.     return s;
  47. }
复制代码
有了上面这段代码,你就可以用一个叫做"to_json"的函数把基本类型和任意一个tuple类型都变成JSON string,不需要针对每一个不同的tuple都做一遍。
当然,上面这段代码没有运行时的动态性,尽管从代码角度来说它可以处理任意一个tuple,但它所处理的每一个tuple其实都是在编译期确定下来的。

想要在运行时的动态性,可以用boost::variant/cxl::variant(这两个东西功能类似,API也基本一样,我的cxl::variant差不多是一个C++11版的boost::variant,加了些小功能)
现在加入一个variant版的to_json:

  1. struct to_json_visitor {
  2.     template<typename T> std::string operator(const T &t) const {
  3.         return to_json(t);
  4.     }
  5. };
  6. template<typename ...T> to_json(const cxl::variant<T...> &t) {
  7.     return t.apply_visitor(to_json_visitor());
  8. }
复制代码
好了,有了以上的东西,我们可以定义一个JSON数据结构:

  1. struct JSON_object;
  2. typedef cxl::variant<std::nullptr_t, int, double, std::string, std::vector<cxl::recursive_wrapper<JSON_object>>, std::map<cxl::recursive_wrapper<JSON_object>>> JSON_value;
  3. struct JSON_object {
  4.     template<typename T>
  5.     JSON_object(const T&t) : value(t) {}
  6.     JSON_value value;
  7. };
  8. //再加两个用于支持null和任意JSON_object的函数
  9. std::string to_json(std::nullptr_t) { return "null"; }
  10. std::string to_json(const JSON_object &obj) { return to_json(obj.value); }
复制代码
有了上面这些代码,我们可以用JSON_object存放任意结构的JSON对象,比如:

  1. JSON_object obj1=10;
  2. JSON_object obj2=5.6;
  3. JSON_object obj3=std::vector<JSON_object>{1, "this is a string", std::map<JSON_object, JSON_object>{{"key1", "value1"}, {"key2", 200}}};
复制代码
而且还可以用to_json把它们变成JSON string。

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
10 [报告]
发表于 2015-10-27 14:43 |只看该作者
本帖最后由 yulihua49 于 2015-10-27 15:34 编辑
windoze 发表于 2015-10-26 17:08
回复 7# yulihua49

你的理解有误,尽管“类型”可以有任意多个,但这些“类型”并不是凭空生成的,每个 ...


怎样知道我的g++是否支持C++11呢?
看的晕晕,好像tuple可以用于存储模板。
那个get<1>,get<2>也晕,不是<type>吗?怎么玩起下标来了?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP