C++98宏实现std::tuple

news/2024/7/3 10:27:58

 

原文:http://geek.csdn.net/news/detail/61260

 

最近准备重构一下我的kapok库,让meta函数可以返回元素为kv的tuple,例如:

struct person
{
    std::string name;
    int age;
    META(name, age) //定义一个支持变参的meta函数
};
int main()
{
    person p = {“tom”, 20};
    auto tp = p.meta();
    static_assert(std::is_same(std::tuple<std::pair<std::string, int>>, decltype(tp), “not same”);
    //tp在内存中是这样的 {{“name”:”tom”}, {“age”:20}};
    return 0;
}

类似这个META的实现我在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() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

#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++的巨大威力。除了非常简洁的优点之外,还解决了一个宏元无法彻底解决的问题,宏元需要预先定义一些参数的宏,这些宏定义是有限的,如果参数超出定义的上限就会编译报错,而这个变参版本完全不用担心这个问题,支持任意个参数。

后记

这件事给了我一个启示,如果我们一直按照前人的路去走,就很难超越前人,如果我们运用新的技术,改变思路,常常会化繁为简,化腐朽为神奇。只有通过新技术、新思维去创造、创新才能超越前人。


http://www.niftyadmin.cn/n/3921978.html

相关文章

KBEngine奇技淫巧<三>

KBEngine奇技淫巧之操作处理各种Handler运行机制框架组织处理任务逻辑InitProgressHandlerBaseMessagesForwardClientHandler自成系统的handler总结各种Handler 研读KBEngine源码的时候&#xff0c;你经常会看到各种Handler结尾的类&#xff0c;搞懂这些类是很有必要的。我们以…

[UE4]AI中使用Tick()函数的性能问题

Tick()函数一般是debug时用的&#xff0c;正规的逻辑中&#xff0c;如果要用Tick函数&#xff0c;可以考虑用UE4提供的事件代理(Event Delegate&#xff1a;http://aigo.iteye.com/blog/2301010)。 参考&#xff1a;Using Tick() within AI? https://answers.unrealengine.com…

[UE4]不使用角色蓝图、动画蓝图、状态机,用“24K纯C++”实现动画播放

原文作者&#xff1a;玄冬Wong 不好意思&#xff0c;我稍稍标题党了&#xff0c;目前还不清楚如何用C代码来实现BlendSpace和Montage的逻辑&#xff0c;如果这两个文件也不我们创建了&#xff0c;那么以下内容就是真正意义上的纯C实现角色蓝图和动画蓝图。 逻辑用C&#xff0c…

KBEngine奇技淫巧<四>

KBEngine定时器定时器介绍EventDispatcherTimer 时间 定时器container_timeQueue_TimerTTimeprocessadd总结一下定时器介绍 是时候放出灵魂框架图了&#xff0c;KBEngine最重要的几个类 是的&#xff0c;这个图就说明了KBEngine引擎最重要的几个类。从程序来看&#xff0c;最…

[C++]为什么模板函数的声明与实现都放在.h头文件中

原文&#xff1a;http://www.360doc.com/content/13/0722/14/10072361_301717293.shtml 当你不使用这个模版函数或模版类,编译器并不实例化它 ,当你使用时&#xff0c;编译器需要实例化它&#xff0c; 因为编译器是一次只能处理一个编译单元, 也就是一次处理一个cpp文件,所以…

KBEngine奇技淫巧<五>

KBEngine网络网络模块的组织EventPoller结构图对象组织InputNotificationHandler如何创建各种HandlerLIstener初始化Receiver创建Channel各种协议Handler的调用NetworkInterfaceChannel 就是socket连接NetworkInerface的结构总结一下网络模块的组织 KBEngine最核心的三个模块我…

[UE4]APlayerController::PlayerTick()注意事项

官方文档&#xff1a; APlayerController::PlayerTick() https://docs.unrealengine.com/latest/INT/API/Runtime/Engine/GameFramework/APlayerController/PlayerTick/index.html PlayerController用PlayerTick&#xff0c;Character中有Tick函数&#xff0c;什么时候用Player…

RTS游戏的寻路算法:群体移动时如何绕开友军阻挡

参考&#xff1a; https://www.zhihu.com/question/29885931 wq yang 这个函数&#xff1a; https://github.com/spring/spring/blob/a2a84b654354e5da1f828b5fd7db280d90d0596d/rts/Sim/MoveTypes/ClassicGroundMoveType.cpp#L754 主要算法从这一行开始&#xff1a;https://gi…