引子

最近准备重构一下我的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++的威力的更多相关文章

  1. Spark小课堂Week7 从Spark中一个例子看面向对象设计

    Spark小课堂Week7 从Spark中一个例子看面向对象设计 今天我们讨论了个问题,来设计一个Spark中的常用功能. 功能描述:数据源是一切处理的源头,这次要实现下加载数据源的方法load() ...

  2. 一个例子看懂所有nodejs的官方网络demo

    今天看群里有人用AI技术写了个五子棋,正好用的socket.io,本身我自己很久没看nodejs了,再加上Tcp/IP的知识一直很弱,我就去官网看了下net.socket 发现之前以为懂的一个官方例子 ...

  3. 关于类、方法、对象(实例):通过一个例子看一下self都做了哪些事情

    我们在定义一个类时,经常会在类的各个方法中看到self,那么在程序执行时self到底起了什么作用,什么时候要加self,这一点需要我们思考并好好理解.之前在学习时没有想这么多,加之用pycharm写代 ...

  4. Erlang 程序引发共享内存 bug 的一个例子

    虽然 Erlang 的广告说得非常好,functional.share-nothing.消息传递,blah blah 的,好像用 Erlang 写并发程序就高枕无忧了,但是由于 Erlang 信奉高度 ...

  5. Webpack入门——使用Webpack打包Angular项目的一个例子

    2016.1.22,对大多数人来说,这是一个非常平常的日子,但这却是我决定在博客园写博客的日子.虽然注册博客园的博客已有4年8个月,却一直没有动手写过一篇博客,原因是觉得自己水平不行,写不出好东西,所 ...

  6. 《The art of software testing》的一个例子

    这几天一直在看一本书,<The art of software testing>,里面有一个例子挺有感触地,写出来和大家分享一下: [问题] 从输入对话框中读取三个整数值,这三个整数值代表 ...

  7. XML的应用 ---- 从一个范例看xml数据、xsd验证、xslt样式

    从一个范例看XML的应用 引言 如果你已经看了Asp.Net Ajax的两种基本开发模式 这篇文章,你可能很快会发现这样一个问题:在那篇文章的方式2中,客户端仅仅是发送了页面上一个文本框的内容到服务端 ...

  8. 用例子看ASP.NET Core Identity是什么?

    原文:用例子看ASP.NET Core Identity是什么? 目录 前言 基于声明的认证(Claims-based Authentication) Claim 在ASP.NET Core Iden ...

  9. zz:一个框架看懂优化算法之异同 SGD/AdaGrad/Adam

    首先定义:待优化参数:  ,目标函数: ,初始学习率 . 而后,开始进行迭代优化.在每个epoch  : 计算目标函数关于当前参数的梯度:  根据历史梯度计算一阶动量和二阶动量:, 计算当前时刻的下降 ...

随机推荐

  1. JSON与XML的区别

    1.定义介绍 (1).XML定义扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许 ...

  2. TypeScript 0.9.1 发布,新增 typeof 关键字

    TypeScript 0.9.1 发布了,该版本提升了编译器和语言的性能,增加新的语言特性 typeof ,更好的 this 处理等.详细介绍请看发行说明. TypeScript 是微软新推出的一种语 ...

  3. 深入理解java虚拟机【Java内存结构】

    Java虚拟机规范规定的java虚拟机内存其实就是java虚拟机运行时数据区,其架构如下: 其中方法区和堆是由所有线程共享的数据区. Java虚拟机栈,本地方法栈和程序计数器是线程隔离的数据区. (1 ...

  4. 小觑数据库(SqlServer)查询语句执行过程

    近年来,越来越多的NoSql产品不断的以技术革命的者的身份跳出来:“你看哥是多么的快,你们关型型数据库真是战五渣阿”.是的,高性能的场景下NoSql真的很出彩.而我们关系型数据库只能在墙角哭泣&quo ...

  5. Mac下安装zshell

    Mac 下安装zshell 什么是shell 大多数命令行用户接触最多的是Bash,因为Bash是各个版本操作系统(Linux&Mac)的默认shell. 查看当前使用的shell $ ech ...

  6. [C++] socket - 3 [线程简单例子 代码]

    #include<windows.h> #include<stdio.h> DWORD WINAPI myfun1(LPVOID lpParameter);//声明线程函数 D ...

  7. 关于消除if-else的简单总结

    if-else是计算机语言中基本的分支语句,虽然很简单,但可能会出现滥用的情况,如图: 这种标记判断,嵌套成这样(其实没有必要嵌套),会让别人觉得作者水平很低. 可以看出,这些if仅仅是在判断一个变量 ...

  8. spring mvc ajax 提交复杂数组类型

    The server refused this request because the request entity is in a format not supported by the reque ...

  9. Hive 安装配置

    系统:Ubuntu 64 bit系统 step1:创建hive文件目录,并在hive目录下创建tmp,warehouse 和 log目录: Step2:解压hive安装包: Step3:创建配置文件: ...

  10. 1-MSP430点亮一个灯

    为了写一篇文章做铺垫--提醒着自己,,,,,, 现在看一下程序 还是说一下是怎么关掉的 往WDTCTL寄存器里写入了0x5A00,,为什么要写这个呢! 那么 WDTCTL = 0x005A  + 0x ...