从一个例子看现代C++的威力
引子
最近准备重构一下我的kapok库,让meta函数可以返回元素为kv的tuple,例如:
struct person
{
std::string name;
int age;
META(name, age) //定义一个支持变参的meta函数
};
int main()
{
person p = {“tom”, };
auto tp = p.meta();
static_assert(std::is_same(std::tuple<std::pair<std::string, int>>, decltype(tp), “not same”);
//在内存中是这样的 {{“name”:”tom”}, {“age”:}};
return ;
}
类似这个META的实现我在msgpack的库里看到了,在这里:
msgpack中仅仅是宏元的代码就数百行了,看起来非常复杂,msgpack之所以用这么复杂的方式去实现恐怕是为了支持c++98/03标准。本来想看看msgpack是如何实现META函数的,但是它的宏元代码读起来比较困难,要读懂估计要花一天的时间,于是作罢。
后来想起群里的ddrm实现了类似的功能,据说没有msgpack这么复杂,简洁一些,于是向ddrm要来了代码(在此对ddrm分享的源码表示感谢)。他的思路也是用宏元,但是比msgpack的代码少很多,将近一百行代码。我不太喜欢这么复杂的代码,我准备用一种更简单的方式去实现这个效果。附上ddrm的代码,大家可以借鉴参考一下。
#ifndef TUPLE_MACRO_DEF_H
#define TUPLE_MACRO_DEF_H #define MARCO_EXPAND(arg_list) arg_list
#define APPLY_VARIADIC_MACRO(macro,tuple) macro tuple #define ADD_REFERENCE(t) std::reference_wrapper<decltype(t)>(t)
#define ADD_REFERENCE_CONST(t) std::reference_wrapper<std::add_const_t<decltype(t)>>(t)
#define PAIR_OBJECT(t) std::make_pair(#t, ADD_REFERENCE(t))
#define PAIR_OBJECT_CONST(t) std::make_pair(#t, ADD_REFERENCE_CONST(t))
#define MAKE_TUPLE(...) auto tuple() { return std::make_tuple(__VA_ARGS__); }
#define MAKE_TUPLE_CONST(...) auto tuple() const { return std::make_tuple(__VA_ARGS__); } /* arg list expand macro, now support 40 args */
#define MAKE_ARG_LIST_1(op, arg, ...) op(arg)
#define MAKE_ARG_LIST_2(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_1(op, __VA_ARGS__))
#define MAKE_ARG_LIST_3(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_2(op, __VA_ARGS__))
#define MAKE_ARG_LIST_4(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_3(op, __VA_ARGS__))
#define MAKE_ARG_LIST_5(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_4(op, __VA_ARGS__))
#define MAKE_ARG_LIST_6(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_5(op, __VA_ARGS__))
#define MAKE_ARG_LIST_7(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_6(op, __VA_ARGS__))
#define MAKE_ARG_LIST_8(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_7(op, __VA_ARGS__))
#define MAKE_ARG_LIST_9(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_8(op, __VA_ARGS__))
#define MAKE_ARG_LIST_10(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_9(op, __VA_ARGS__))
#define MAKE_ARG_LIST_11(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_10(op, __VA_ARGS__))
#define MAKE_ARG_LIST_12(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_11(op, __VA_ARGS__))
#define MAKE_ARG_LIST_13(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_12(op, __VA_ARGS__))
#define MAKE_ARG_LIST_14(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_13(op, __VA_ARGS__))
#define MAKE_ARG_LIST_15(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_14(op, __VA_ARGS__))
#define MAKE_ARG_LIST_16(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_15(op, __VA_ARGS__))
#define MAKE_ARG_LIST_17(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_16(op, __VA_ARGS__))
#define MAKE_ARG_LIST_18(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_17(op, __VA_ARGS__))
#define MAKE_ARG_LIST_19(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_18(op, __VA_ARGS__))
#define MAKE_ARG_LIST_20(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_19(op, __VA_ARGS__))
#define MAKE_ARG_LIST_21(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_20(op, __VA_ARGS__))
#define MAKE_ARG_LIST_22(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_21(op, __VA_ARGS__))
#define MAKE_ARG_LIST_23(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_22(op, __VA_ARGS__))
#define MAKE_ARG_LIST_24(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_23(op, __VA_ARGS__))
#define MAKE_ARG_LIST_25(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_24(op, __VA_ARGS__))
#define MAKE_ARG_LIST_26(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_25(op, __VA_ARGS__))
#define MAKE_ARG_LIST_27(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_26(op, __VA_ARGS__))
#define MAKE_ARG_LIST_28(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_27(op, __VA_ARGS__))
#define MAKE_ARG_LIST_29(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_28(op, __VA_ARGS__))
#define MAKE_ARG_LIST_30(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_29(op, __VA_ARGS__))
#define MAKE_ARG_LIST_31(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_30(op, __VA_ARGS__))
#define MAKE_ARG_LIST_32(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_31(op, __VA_ARGS__))
#define MAKE_ARG_LIST_33(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_32(op, __VA_ARGS__))
#define MAKE_ARG_LIST_34(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_33(op, __VA_ARGS__))
#define MAKE_ARG_LIST_35(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_34(op, __VA_ARGS__))
#define MAKE_ARG_LIST_36(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_35(op, __VA_ARGS__))
#define MAKE_ARG_LIST_37(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_36(op, __VA_ARGS__))
#define MAKE_ARG_LIST_38(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_37(op, __VA_ARGS__))
#define MAKE_ARG_LIST_39(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_38(op, __VA_ARGS__))
#define MAKE_ARG_LIST_40(op, arg, ...) op(arg), MARCO_EXPAND(MAKE_ARG_LIST_39(op, __VA_ARGS__)) /* emmbed marco, using EMMBED_TUPLE(5 , a, b, c, d, e) */
//note use MACRO_CONCAT like A##_##B direct may cause marco expand error
#define MACRO_CONCAT(A, B) MACRO_CONCAT1(A, B)
#define MACRO_CONCAT1(A, B) A##_##B #define MAKE_ARG_LIST(N, op, arg, ...) \
MACRO_CONCAT(MAKE_ARG_LIST, N)(op, arg, __VA_ARGS__) #define EMMBED_TUPLE(N, ...) \
MAKE_TUPLE(MAKE_ARG_LIST(N, PAIR_OBJECT, __VA_ARGS__)) \
MAKE_TUPLE_CONST(MAKE_ARG_LIST(N, PAIR_OBJECT_CONST, __VA_ARGS__)) // see http://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5
#define RSEQ_N() \
,,,, \
,,,,,,,,,, \
,,,,,,,,,, \
,,,,,,,,,, \
,,,,,,,,,, \
,,,,,,,,,, \
,,,,,,,,, #define ARG_N( \
_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,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N, ...) N #define GET_ARG_COUNT(...) APPLY_VARIADIC_MACRO(GET_ARG_COUNT_INNER,(__VA_ARGS__, RSEQ_N()))
#define GET_ARG_COUNT_INNER(...) APPLY_VARIADIC_MACRO(ARG_N, (__VA_ARGS__)) #define EMMBED(...) EMMBED_TUPLE(GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) #endif
10行代码解决问题
我探索了一些可能的方案后,最终找到了一种满意的方案,最终的实现代码不过十来行,非常简洁,感谢c++11/14,正是现代C++才让代码变得如此简洁。
实现思路也比较简单,问题的本质是将输入的参数值变成一个pair,key是值的字面量,value是变量本身的值,所以生成的pair是这样的std::pair<std::string, someval>, 另外这个pair是每一个输入参数值都会生成一个pair,如果是一系列的参数值就要生成一系列的pair,这就要求支持变参。所以问题的关键是要在展开变参的过程中将这些pair放到一个tuple中,这对于c++11/14来说是不难办到的,下面是实现代码:
#define MAKE_PAIR(text) std::pair<std::string, decltype(text)>{#text, text} template<typename T>
constexpr static inline auto apply(T const & args)
{
return args;
} template<typename T, typename T1, typename... Args>
constexpr static inline auto apply(T const & t, const T1& first, const Args&... args)
{
return apply(std::tuple_cat(t, std::make_tuple(MAKE_PAIR(first))), args...);
} #define META(...) auto meta(){ return apply(std::tuple<>(), __VA_ARGS__); }
使用现代C++之后,仅仅需要10行代码就可以实现之前需要上百行甚至数百行代码才能实现的目标,这无疑体现了现代C++的巨大威力。除了非常简洁的优点之外,还解决了一个宏元无法彻底解决的问题,宏元需要预先定义一些参数的宏,这些宏定义是有限的,如果参数超出定义的上限就会编译报错,而这个变参版本完全不用担心这个问题,支持任意个参数。
update
#include <array>
#include <string>
#include <tuple>
template<size_t N>
std::array<std::string, N> split(const std::string& s, const char delimiter)
{
size_t start = ;
size_t end = s.find_first_of(delimiter); std::array<std::string, N> output; size_t i = ;
while (end <= std::string::npos)
{
output[i++] = std::move(s.substr(start, end - start));
if (end == std::string::npos)
break; start = end + ;
end = s.find_first_of(delimiter, start);
} return output;
} template<size_t N, typename T>
static inline auto make(const std::array<std::string, N>&ar, size_t index, const T& args)
{
return args;
} template<size_t N, typename T, typename T1, typename... Args>
static inline auto make(const std::array<std::string, N>&ar, size_t index, T const & t, T1& first, Args&... args)
{
return make(ar, index + , std::tuple_cat(t, std::make_tuple(std::pair<std::string, T1&>(ar[index], first))), args...);
} #define VA_ARGS_NUM(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value #define META(...) auto meta(){\
auto ar = split<VA_ARGS_NUM(__VA_ARGS__)>(#__VA_ARGS__, ',');\
return make(ar, , std::tuple<>(), __VA_ARGS__);\
}
后记
这件事给了我一个启示,如果我们一直按照前人的路去走,就很难超越前人,如果我们运用新的技术,改变思路,常常会化繁为简,化腐朽为神奇。只有通过新技术、新思维去创造、创新才能超越前人。
从一个例子看现代C++的威力的更多相关文章
- Spark小课堂Week7 从Spark中一个例子看面向对象设计
Spark小课堂Week7 从Spark中一个例子看面向对象设计 今天我们讨论了个问题,来设计一个Spark中的常用功能. 功能描述:数据源是一切处理的源头,这次要实现下加载数据源的方法load() ...
- 一个例子看懂所有nodejs的官方网络demo
今天看群里有人用AI技术写了个五子棋,正好用的socket.io,本身我自己很久没看nodejs了,再加上Tcp/IP的知识一直很弱,我就去官网看了下net.socket 发现之前以为懂的一个官方例子 ...
- 关于类、方法、对象(实例):通过一个例子看一下self都做了哪些事情
我们在定义一个类时,经常会在类的各个方法中看到self,那么在程序执行时self到底起了什么作用,什么时候要加self,这一点需要我们思考并好好理解.之前在学习时没有想这么多,加之用pycharm写代 ...
- Erlang 程序引发共享内存 bug 的一个例子
虽然 Erlang 的广告说得非常好,functional.share-nothing.消息传递,blah blah 的,好像用 Erlang 写并发程序就高枕无忧了,但是由于 Erlang 信奉高度 ...
- Webpack入门——使用Webpack打包Angular项目的一个例子
2016.1.22,对大多数人来说,这是一个非常平常的日子,但这却是我决定在博客园写博客的日子.虽然注册博客园的博客已有4年8个月,却一直没有动手写过一篇博客,原因是觉得自己水平不行,写不出好东西,所 ...
- 《The art of software testing》的一个例子
这几天一直在看一本书,<The art of software testing>,里面有一个例子挺有感触地,写出来和大家分享一下: [问题] 从输入对话框中读取三个整数值,这三个整数值代表 ...
- XML的应用 ---- 从一个范例看xml数据、xsd验证、xslt样式
从一个范例看XML的应用 引言 如果你已经看了Asp.Net Ajax的两种基本开发模式 这篇文章,你可能很快会发现这样一个问题:在那篇文章的方式2中,客户端仅仅是发送了页面上一个文本框的内容到服务端 ...
- 用例子看ASP.NET Core Identity是什么?
原文:用例子看ASP.NET Core Identity是什么? 目录 前言 基于声明的认证(Claims-based Authentication) Claim 在ASP.NET Core Iden ...
- zz:一个框架看懂优化算法之异同 SGD/AdaGrad/Adam
首先定义:待优化参数: ,目标函数: ,初始学习率 . 而后,开始进行迭代优化.在每个epoch : 计算目标函数关于当前参数的梯度: 根据历史梯度计算一阶动量和二阶动量:, 计算当前时刻的下降 ...
随机推荐
- Java 7新方法probeContentType的C#实现方式
在Java 7中增加了新的一个方法——probeContentType,其主要作用是可以判断文件的content type.相应代码如下所示: import java.io.IOException; ...
- 基于Qt的流程设计器(一)
一: 先来看一下界面的截图: 说明: 拖动节点的时候,与该节点相关的箭头连线也会跟着调整: 用户可以使用鼠标从一个节点拖出一个箭头到另一个节点(鼠标在空白区域点击一下,拖出的箭头消失) 这三个 ...
- PC-PC-单片机(Arduino)通信实例
请仔细理解相关参数,如端口设置.IP设置.COM口设置......等等.....不要盲目COPY.....这涉及2台电脑和一个单片机,其中一台电脑作为服务器并与单片机相连,负责通过网络与客户端通信(s ...
- Gatling->次时代性能测试利器
Gatling作为一款开源免费的性能测试工具越来越受到广大程序员的欢迎.免费当然是好的,最缺钱的就是程序员了;开源更好啊,缺啥功能.想做定制化的可以自己动手,丰衣足食.其实我最喜欢的原因是其提供了简洁 ...
- Web开发者的六个代码调试平台
代码调试平台是Web开发者进行开发.测试.分享.协作和交流的网络应用,它们支持实时的编辑.预览HTML.CSS和JavaScript的客户端代码.这些代码调试平台最值得称道的地方在于,它们中的大多数都 ...
- nosql/nodejs基础
nosql定义:nosql--no only sql 目前流行的非关系型数据库:mongodb,redis,cassandra 非关系型数据库和内存存储hashmap数据结构有什么区别?hashmap ...
- 【Android】EventBus 源码解析
EventBus 源码解析 本文为 Android 开源项目实现原理解析 中 EventBus 部分项目地址:EventBus,分析的版本:ccc2771,Demo 地址:EventBus Demo分 ...
- Bootstrap的学习以及简单运用
<!DOCTYPE html> <html> <head> <title>柠檬学院</title> <meta charset=&qu ...
- js 导出Excel
最近从Silverlight这边转到javascript过来,现在要导出一个导出excel的功能.上级领导指示当页显示多少数据,就导出多少数据,没有必要从后台在去数据.以前也没有接触过这方面的,在网上 ...
- 在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法
今天遇到了一个在servlet的service方法中获取ServletContext对象出现java.lang.NullPointerException(空指针)异常,代码如下: 1 //获取Serv ...