最近在看一个c++各数据类型和 JSON 或 xml 互转的库,重点阅读了其中宏的编写
xpack项目链接
// 结构体格式如下
struct User{
long uid;
string name;
XPACK(A(uid, "id"), O(name)); //用于指示各变量的特殊操作
}
#define XPACK(...) \
X_PACK_COMMON \
X_PACK_DECODE_BEGIN X_PACK_N(X_PACK_L1, X_PACK_L1_DECODE, __VA_ARGS__) } \
X_PACK_ENCODE_BEGIN X_PACK_N(X_PACK_L1, X_PACK_L1_ENCODE, __VA_ARGS__) }
结构体中的变量数目不定,采用可变参数宏:
... 和__VA_ARGS__ 配合使用,... 处填充的内容将填充到___VA_ARGS__ 处
__VA_ARGS__ 替换最后一个具体参数后所有内容包括逗号等
当然c++并不提倡使用可变参数
#define X_PACK_COMMON \
public: \
static bool const __x_pack_value = true;
#define X_PACK_DECODE_BEGIN \
template<class __X_PACK_DOC, class __X_PACK_ME> \
void __x_pack_decode(__X_PACK_DOC& __x_pack_obj, __X_PACK_ME &__x_pack_self, const xpack::Extend *__x_pack_extp) {(void)__x_pack_extp;
// encode function
#define X_PACK_ENCODE_BEGIN \
template <class __X_PACK_DOC, class __X_PACK_ME> \
void __x_pack_encode(__X_PACK_DOC& __x_pack_obj, const __X_PACK_ME &__x_pack_self, const xpack::Extend *__x_pack_extp) const {(void)__x_pack_extp;
从这里开始可以看出XPACK已经被展开成了一个静态常量和两个函数,x_PACK_N() 被包在函数内部
#define X_PACK_N(LEVEL, ACTION, ...) X_PACK_COUNT(LEVEL, ACTION, __VA_ARGS__, _99,_98,_97,_96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,_80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,_64,_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) (ACTION, __VA_ARGS__)
#define X_PACK_COUNT(LEVEL, ACTION, _99,_98,_97,_96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81,_80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,_64,_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,N,...) LEVEL##N
很离谱第一次看到这种操作,算是奇技淫巧了,分析一下做了什么
举个例子:x_PACK_N(L,A, myA, myY) 扩展成X_PACK_COUNT(L, A, myX, myY, 一坨)(ACTION, __VA_ARGS__)
X_PACK_COUNT(L, A, myX, myY, -99, 一坨, _3, _2, _1)
X_PACK_COUNT(L, A, _99, _98, 一坨, _2, _1, N)
N 获取到_2
这个宏相当于是一个截取功能,理解成一个窗口
这一步的结果是X_PACK_L1_2(X_PACK_L1_DECODE, __VA_ARGS__)
注意:此方法适用于gcc,msvc中需要再添加一个宏#define EXPAND(...) __VA_ARGS__ ,并且包含在X_PACK_COUNT() 之外
#define EXPEND(X_PACK_COUNT()) EXPEND(ACTION, __VA_ARGS__)
后面经过几步简单的变换转换成
X_PACK_L1_DECODE(A(uid, "id")) X_PACK_L1_DECODE(O(name))
再来看一下X_PACK_L1_DECODE
/// XPACK /
//=======DECODE
#define X_PACK_L1_DECODE(x) { X_PACK_L1_DECODE_##x }
//----
#define X_PACK_L1_DECODE_X(FLAG, ...) X_EXPAND_FLAG_##FLAG xpack::Extend __x_pack_ext(__x_pack_flag, NULL); X_PACK_N2(X_PACK_L2, X_PACK_DECODE_ACT_O, __VA_ARGS__)
#define X_PACK_L1_DECODE_E(FLAG, ...) X_EXPAND_FLAG_##FLAG xpack::Extend __x_pack_ext(__x_pack_flag, NULL); X_PACK_N2(X_PACK_L2, X_PACK_DECODE_ACT_E, __VA_ARGS__)
#define X_PACK_L1_DECODE_B(FLAG, ...) X_EXPAND_FLAG_##FLAG xpack::Extend __x_pack_ext(__x_pack_flag, NULL); X_PACK_N2(X_PACK_L2, X_PACK_DECODE_ACT_B, __VA_ARGS__)
#define X_PACK_L1_DECODE_AF(FLAG, ...) X_EXPAND_FLAG_##FLAG X_PACK_N2(X_PACK_L2_2, X_PACK_DECODE_ACT_A, __VA_ARGS__) // extend define in ACTION
#define X_PACK_L1_DECODE_O(...) X_PACK_L1_DECODE_X(F(0), __VA_ARGS__)
#define X_PACK_L1_DECODE_M(...) X_PACK_L1_DECODE_X(F(M), __VA_ARGS__)
#define X_PACK_L1_DECODE_A(...) X_PACK_L1_DECODE_AF(F(0), __VA_ARGS__)
#define X_PACK_L1_DECODE_I(...) X_PACK_N2(X_PACK_L2, X_PACK_DECODE_ACT_I, __VA_ARGS__)
//=======ENCODE
#define X_PACK_L1_ENCODE(x) { X_PACK_L1_ENCODE_##x }
//-----
#define X_PACK_L1_ENCODE_X(FLAG, ...) X_EXPAND_FLAG_##FLAG xpack::Extend __x_pack_ext(__x_pack_flag, NULL); X_PACK_N2(X_PACK_L2, X_PACK_ENCODE_ACT_O, __VA_ARGS__)
#define X_PACK_L1_ENCODE_E(FLAG, ...) X_EXPAND_FLAG_##FLAG xpack::Extend __x_pack_ext(__x_pack_flag, NULL); X_PACK_N2(X_PACK_L2, X_PACK_ENCODE_ACT_E, __VA_ARGS__)
#define X_PACK_L1_ENCODE_B(FLAG, ...) X_EXPAND_FLAG_##FLAG xpack::Extend __x_pack_ext(__x_pack_flag, NULL); X_PACK_N2(X_PACK_L2, X_PACK_ENCODE_ACT_B, __VA_ARGS__)
#define X_PACK_L1_ENCODE_AF(FLAG, ...) X_EXPAND_FLAG_##FLAG X_PACK_N2(X_PACK_L2_2, X_PACK_ENCODE_ACT_A, __VA_ARGS__) // extend define in ACTION
#define X_PACK_L1_ENCODE_O(...) X_PACK_L1_ENCODE_X(F(0), __VA_ARGS__)
#define X_PACK_L1_ENCODE_M(...) X_PACK_L1_ENCODE_X(F(M), __VA_ARGS__)
#define X_PACK_L1_ENCODE_A(...) X_PACK_L1_ENCODE_AF(F(0), __VA_ARGS__)
//-----
#define X_PACK_L1_ENCODE_I(...) X_PACK_N2(X_PACK_L2, X_PACK_ENCODE_ACT_I, __VA_ARGS__)
很明显可以看出:用## 来实现switch逻辑
X_PACK_L1_DECODE(A(uid, "id")) 扩展成{X_PACK_L1_DECODE_A(uid, "id")}
后面的操作大同小异,经过几次扩展后从转为N2同时L1转为L2再转成X_PACK_ACT 类型的宏,最后由如下的宏转换成最终代码
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ decode act ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define X_PACK_DECODE_ACT_O(M) \
__x_pack_ext.vsize = sizeof(__x_pack_self.M); \
__x_pack_obj.decode(#M, __x_pack_self.M, &__x_pack_ext);
// enum for not support c++11
#define X_PACK_DECODE_ACT_E(M) \
__x_pack_ext.vsize = sizeof(__x_pack_self.M); \
__x_pack_obj.decode(#M, *((int*)&__x_pack_self.M), &__x_pack_ext);
#define X_PACK_DECODE_ACT_A(M, NAME) \
{ \
static xpack::Alias __x_pack_alias(#M, NAME); \
xpack::Extend __x_pack_ext(__x_pack_flag, &__x_pack_alias); \
const char *__new_name = __x_pack_alias.Name(__x_pack_obj.Type());\
__x_pack_ext.vsize = sizeof(__x_pack_self.M); \
__x_pack_obj.decode(__new_name, __x_pack_self.M, &__x_pack_ext); \
}
|