我好像记得大学的教材是这么说的,我们现在学C++了,以前C语言是这样子的,
#define pi 3.1415926
现在学C++推荐这么用
const double pi = 3.1415926;
好吧,不带这么埋汰宏的,感觉宏是C语言的糟粕似的。宏的作用是强势替换展开,它是帮你写程序的,不是给你定义变量的。
众所周知,C/C++运行时无法知道变量名字。比如说这个结构体
struct tagTest
{
int id; //id号
string name; //姓名
string sex; //性别
}
如果要互转json,大概需要这么写
void tagTest::ToJson(json::Value &va)
{
va["id"] = id;
va["name"] = name.c_str();
vc["sex"] = sex.c_str();
}
void tagTest::FromJson(json::Value &va)
{
id = ...;
name = ...;
sex = ...;
}
如果你有100个结构体,就要写100个这样的fromto,还容易写错,程序员虽然一直孜孜不倦的坐在电脑旁码字,但是绝对不是勤劳的人。
要是能自动化就好了呀,下面就开始引入宏了。最终的效果,我们是要这样子的:
struct tagTest
{
tagTest()
{
REG_MEMBERS(id,name,sex) //记录需要转换的成员。
}
int id; //id号
string name; //姓名
string sex; //性别
}
REG_MEMEBERS这个宏的参数是不定长的,不要想着把这个不定长参数直接丢给va_start,va_args,va_end。va_args第2个参数就需要给出类型,可是现在谁也不知道类型啊。
看一下微软的文档,https://docs.microsoft.com/en-us/cpp/preprocessor/variadic-macros?view=msvc-160,气死人了,网上也是到处都这样的代码。C99定义的__VA_AGRS__,难道只是为了方便丢给printf处理吗?
REG_MEMBERS宏定义的格式: #define REG_MEMBERS(...)
第一个需要解决的问题,你到底有几个参数?
先给出这段代码,得出参数个数。
#define ARG_COUNTX(...) A1X(__VA_ARGS__)
#define A3X(x) x
#define A1X(...) A3X(A4X(__VA_ARGS__, 3, 2, 1, 0))
#define A4X(_1_, _2_, _3_, count, ...) count
如果调用ARG_COUNTX(a,b,c),结果=3,也就是参数的数量。不太好理解吧,由于VC有些不是BUG的BUG,中间需要一些宏转换,gcc不需要这样转换,主要看这里
#define A4X(_1_, _2_, _3_, count, ...) count
第一步展开,A4X(a, b, c, 3, 2, 1, 0),第二步展开,a, b, c, 3, 2, 1, 0,看一下A4X的定义,3这个位置就是count,刚好返回了。
_1_, _2_, _3_ 这3个东西只是挤占参数位置的,没实际意义。
如果调用ARG_COUNTX(a,b),结果=2;
看一下展开,第一步展开,A4X(a, b, 3, 2, 1, 0),第二步展开,a, b, 3, 2, 1, 0。哈哈,2刚好又是count的位置,返回2了。
如果调用ARG_COUNTX(a,b,c,d),结果会等于d,因为d刚好在count这个位置上。
只支持3个,太少了。 下面这个支持4个了。 依此类推,可以很多个,100个是没问题。上限是多少,我也不知道,可能和编译器有关系。
#define A1X(...) A3X(A4X(__VA_ARGS__, 4, 3, 2, 1, 0))
#define A4X(_1_, _2_, _3_, _4_, count, ...) count
第二个需要解决的问题,都有哪些参数?
先理解一下这个
#define ARG_1(_1, ...) _1
#define ARG_2(_1, _2, ...) _2
#define ARG_3(_1, _2, _3, ...) _3
ARG_1(a,b,c) 返回a, ARG_2(a,b,c) 返回b,ARG_3(a,b,c) 返回c。
对于ARG_2的展开,_2刚好就是b,对不对?
对于ARG_3的展开,_3刚好就是c,对不对?
因为我们需要考虑100个参数的问题,所以有了下面这个代码,学名是:宏递归。
#define DoNames(x) cout << TOSTRING(x) << endl; //把变量名打印出来
#define ARG_1(x, ...) DoNames(x)
#define ARG_2(x, ...) ARG_1(x) ARG_1(__VA_ARGS__)
#define ARG_3(x, ...) ARG_1(x) ARG_2(__VA_ARGS__)
#define REG_MEMBERS(...) ARG_ ## ARG_COUNTX(__VA_ARGS__)(__VA_ARGS__)
这段代码无法运行的,简洁一点,容易理解。
ARG_COUNTX(a,b,c) 结果=3,
ARG_ ## ARG_COUNTX(__VA_ARGS__),结果就是ARG_3,其中##是宏里面的连接符号。
REG_MEMBERS(a,b,c)的效果实现:ARG_3(a,b,c)。
开始递归,
ARG_3(a, b, c)展开结果: ARG_1(a) ARG_2(b, c)
ARG_2(b, c)展开结果: ARG_1(b) ARG_1(c)
意思就是说,每次从__VA_ARGS__最左边挤出一个参数来,然后把剩下的参数传给下一级。
要扩充数量的话,就是按这样的格式继续增加了
#define ARG_4(x, ...) ARG_1(x) ARG_3(__VA_ARGS__)
#define ARG_5(x, ...) ARG_1(x) ARG_4(__VA_ARGS__)
天啊,我怎么这么啰嗦的。
上面这段代码是无法运行的,需要再转换,比如 ARG_ ## ARG_COUNTX(__VA_ARGS__) 它的##连接结果ARG_ARG_COUNTX(__VA_ARGS__) ,继续不下去的,现在给出正确的代码
#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <typeinfo>
#include <string>
#include "stdarg.h"
using namespace std;
#define ARG_COUNTX(...) A1X(__VA_ARGS__)
#define A3X(x) x
#define A1X(...) A3X(A4X(__VA_ARGS__, 3, 2, 1, 0))
#define A4X(_1_, _2_, _3_, count, ...) count
#define PREPARE_MACRO(x) x
#define CAT_(a, b) a ## b
#define CAT(a, b) CAT_(a, b)
#define TOSTRING(x) #x //转为字符串返回
#define test_output(x) { \
cout << "-----------------------------" << endl; \
cout << "变量名: " << TOSTRING(x) << endl; \
cout << "类型: " << typeid(x).name() << endl; \
cout << "内存地址: " << &x << endl; \
}
#define DoNames(x) test_output(x)
#define ARG_1(x, ...) DoNames(x)
#define ARG_2(x, ...) ARG_1(x) PREPARE_MACRO(ARG_1(__VA_ARGS__))
#define ARG_3(x, ...) ARG_1(x) PREPARE_MACRO(ARG_2(__VA_ARGS__))
#define REG_MEMBERS(...) \
PREPARE_MACRO(CAT(ARG_, ARG_COUNTX(__VA_ARGS__))(__VA_ARGS__))
struct tagTest
{
tagTest()
{
REG_MEMBERS(id,name,sex) //记录需要转换的成员。
}
int id; //id号
string name; //姓名
string sex; //性别
};
int _tmain(int argc, _TCHAR* argv[])
{
tagTest t;
getchar();
return 0;
}
运行结果
-----------------------------
变量名: id
类型: int
内存地址: 002CF748
-----------------------------
变量名: name
类型: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
内存地址: 002CF74C
-----------------------------
变量名: sex
类型: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
内存地址: 002CF76C
经过我啰嗦这么久,我们终于获得了3个有用的东西:变量名、数据类型,指针。 也就是“类型--名字--值”。
关键词:类型--名字--值。定义结构体时,加一段代码:REG_MEMBERS(id,name,sex) ,可以得到“类型--名字--值”列表。
看到这里大家应该就明白了,主要就是__VA_ARGS__获取参数个数和遍历,接着使用typeid()获取变量类型。其实可以利用宏的特性做多事情,比如反射啦,类工厂啦。都是需要宏配合。
让宏展开一万次吧,我只写一行。
如果还是不明白,那我们继续。。。。。
接下来有很多方法,比如说我喜欢给结构体提供一个基类。不管怎么做,原理上都是保存“类型--名字--值”三要素列表。
完整的结构体基类:CBaseStruct
//BaseStrut.h
#pragma once
#include <typeinfo>
#include <string>
#include "stdarg.h"
#include <vector>
using namespace std;
#define STRUCTTYPE_int 1
#define STRUCTTYPE_stdstring 2
class CBaseStruct
{
public:
struct __TagInfo
{
unsigned char type_id; //类型标注
std::string type_name; //类型名称
void *var_address; //变量指针
std::string var_name; //变量名
};
CBaseStruct()
{
}
int __get_count()
{
return __vector.size();
}
__TagInfo &__get_member(int i)
{
return __vector[i];
}
void FromJson()
{
int size = __vector.size();
for (int i=0; i<size; i++)
{
__TagInfo &info = __vector[i];
switch (info.type_id)
{
case STRUCTTYPE_int:
break;
case STRUCTTYPE_stdstring:
break;
}
}
}
void ToJson()
{
int size = __vector.size();
for (int i=0; i<size; i++)
{
__TagInfo &info = __vector[i];
switch (info.type_id)
{
case STRUCTTYPE_int:
cout << info.var_name << " : " << *(int *)info.var_address << endl;
break;
case STRUCTTYPE_stdstring:
cout << info.var_name << " : " << *(std::string *)info.var_address << endl;
break;
}
}
}
std::vector<__TagInfo> __vector;
};
#define REFLECT_Members(a) {\
CBaseStruct::__TagInfo info; \
info.type_name = typeid(a).name(); \
if(info.type_name == "int") info.type_id = STRUCTTYPE_int; \
else if(info.type_name.find("string") != std::string::npos) info.type_id = STRUCTTYPE_stdstring; \
info.var_address = &a; \
info.var_name = #a; \
__vector.push_back(info); \
}
//
#define ARG_COUNTX(...) A1X(__VA_ARGS__)
#define A3X(x) x
#define A1X(...) A3X(A4X(__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, 0))
#define A4X(_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, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, count, ...) count
#define PREPARE_MACRO(x) x
#define CAT_(a, b) a ## b
#define CAT(a, b) CAT_(a, b)
#define ARG_1(x, ...) REFLECT_Members(x)
#define ARG_2(x, ...) ARG_1(x) PREPARE_MACRO(ARG_1(__VA_ARGS__))
#define ARG_3(x, ...) ARG_1(x) PREPARE_MACRO(ARG_2(__VA_ARGS__))
#define ARG_4(x, ...) ARG_1(x) PREPARE_MACRO(ARG_3(__VA_ARGS__))
#define ARG_5(x, ...) ARG_1(x) PREPARE_MACRO(ARG_4(__VA_ARGS__))
#define ARG_6(x, ...) ARG_1(x) PREPARE_MACRO(ARG_5(__VA_ARGS__))
#define ARG_7(x, ...) ARG_1(x) PREPARE_MACRO(ARG_6(__VA_ARGS__))
#define ARG_8(x, ...) ARG_1(x) PREPARE_MACRO(ARG_7(__VA_ARGS__))
#define ARG_9(x, ...) ARG_1(x) PREPARE_MACRO(ARG_8(__VA_ARGS__))
#define ARG_10(x, ...) ARG_1(x) PREPARE_MACRO(ARG_9(__VA_ARGS__))
#define ARG_11(x, ...) ARG_1(x) PREPARE_MACRO(ARG_10(__VA_ARGS__))
#define ARG_12(x, ...) ARG_1(x) PREPARE_MACRO(ARG_11(__VA_ARGS__))
#define ARG_13(x, ...) ARG_1(x) PREPARE_MACRO(ARG_12(__VA_ARGS__))
#define ARG_14(x, ...) ARG_1(x) PREPARE_MACRO(ARG_13(__VA_ARGS__))
#define ARG_15(x, ...) ARG_1(x) PREPARE_MACRO(ARG_14(__VA_ARGS__))
#define ARG_16(x, ...) ARG_1(x) PREPARE_MACRO(ARG_15(__VA_ARGS__))
#define ARG_17(x, ...) ARG_1(x) PREPARE_MACRO(ARG_16(__VA_ARGS__))
#define ARG_18(x, ...) ARG_1(x) PREPARE_MACRO(ARG_17(__VA_ARGS__))
#define ARG_19(x, ...) ARG_1(x) PREPARE_MACRO(ARG_18(__VA_ARGS__))
#define ARG_20(x, ...) ARG_1(x) PREPARE_MACRO(ARG_19(__VA_ARGS__))
#define ARG_21(x, ...) ARG_1(x) PREPARE_MACRO(ARG_20(__VA_ARGS__))
#define ARG_22(x, ...) ARG_1(x) PREPARE_MACRO(ARG_21(__VA_ARGS__))
#define ARG_23(x, ...) ARG_1(x) PREPARE_MACRO(ARG_22(__VA_ARGS__))
#define ARG_24(x, ...) ARG_1(x) PREPARE_MACRO(ARG_23(__VA_ARGS__))
#define ARG_25(x, ...) ARG_1(x) PREPARE_MACRO(ARG_24(__VA_ARGS__))
#define ARG_26(x, ...) ARG_1(x) PREPARE_MACRO(ARG_25(__VA_ARGS__))
#define ARG_27(x, ...) ARG_1(x) PREPARE_MACRO(ARG_26(__VA_ARGS__))
#define ARG_28(x, ...) ARG_1(x) PREPARE_MACRO(ARG_27(__VA_ARGS__))
#define ARG_29(x, ...) ARG_1(x) PREPARE_MACRO(ARG_28(__VA_ARGS__))
#define ARG_30(x, ...) ARG_1(x) PREPARE_MACRO(ARG_29(__VA_ARGS__))
#define ARG_31(x, ...) ARG_1(x) PREPARE_MACRO(ARG_30(__VA_ARGS__))
#define ARG_32(x, ...) ARG_1(x) PREPARE_MACRO(ARG_31(__VA_ARGS__))
#define ARG_33(x, ...) ARG_1(x) PREPARE_MACRO(ARG_32(__VA_ARGS__))
#define ARG_34(x, ...) ARG_1(x) PREPARE_MACRO(ARG_33(__VA_ARGS__))
#define ARG_35(x, ...) ARG_1(x) PREPARE_MACRO(ARG_34(__VA_ARGS__))
#define ARG_36(x, ...) ARG_1(x) PREPARE_MACRO(ARG_35(__VA_ARGS__))
#define ARG_37(x, ...) ARG_1(x) PREPARE_MACRO(ARG_36(__VA_ARGS__))
#define ARG_38(x, ...) ARG_1(x) PREPARE_MACRO(ARG_37(__VA_ARGS__))
#define ARG_39(x, ...) ARG_1(x) PREPARE_MACRO(ARG_38(__VA_ARGS__))
#define ARG_40(x, ...) ARG_1(x) PREPARE_MACRO(ARG_39(__VA_ARGS__))
#define ARG_41(x, ...) ARG_1(x) PREPARE_MACRO(ARG_40(__VA_ARGS__))
#define ARG_42(x, ...) ARG_1(x) PREPARE_MACRO(ARG_41(__VA_ARGS__))
#define ARG_43(x, ...) ARG_1(x) PREPARE_MACRO(ARG_42(__VA_ARGS__))
#define ARG_44(x, ...) ARG_1(x) PREPARE_MACRO(ARG_43(__VA_ARGS__))
#define ARG_45(x, ...) ARG_1(x) PREPARE_MACRO(ARG_44(__VA_ARGS__))
#define ARG_46(x, ...) ARG_1(x) PREPARE_MACRO(ARG_45(__VA_ARGS__))
#define ARG_47(x, ...) ARG_1(x) PREPARE_MACRO(ARG_46(__VA_ARGS__))
#define ARG_48(x, ...) ARG_1(x) PREPARE_MACRO(ARG_47(__VA_ARGS__))
#define ARG_49(x, ...) ARG_1(x) PREPARE_MACRO(ARG_48(__VA_ARGS__))
#define ARG_50(x, ...) ARG_1(x) PREPARE_MACRO(ARG_49(__VA_ARGS__))
#define ARG_51(x, ...) ARG_1(x) PREPARE_MACRO(ARG_50(__VA_ARGS__))
#define ARG_52(x, ...) ARG_1(x) PREPARE_MACRO(ARG_51(__VA_ARGS__))
#define ARG_53(x, ...) ARG_1(x) PREPARE_MACRO(ARG_52(__VA_ARGS__))
#define ARG_54(x, ...) ARG_1(x) PREPARE_MACRO(ARG_53(__VA_ARGS__))
#define ARG_55(x, ...) ARG_1(x) PREPARE_MACRO(ARG_54(__VA_ARGS__))
#define ARG_56(x, ...) ARG_1(x) PREPARE_MACRO(ARG_55(__VA_ARGS__))
#define ARG_57(x, ...) ARG_1(x) PREPARE_MACRO(ARG_56(__VA_ARGS__))
#define ARG_58(x, ...) ARG_1(x) PREPARE_MACRO(ARG_57(__VA_ARGS__))
#define ARG_59(x, ...) ARG_1(x) PREPARE_MACRO(ARG_58(__VA_ARGS__))
#define ARG_60(x, ...) ARG_1(x) PREPARE_MACRO(ARG_59(__VA_ARGS__))
#define ARG_61(x, ...) ARG_1(x) PREPARE_MACRO(ARG_60(__VA_ARGS__))
#define ARG_62(x, ...) ARG_1(x) PREPARE_MACRO(ARG_61(__VA_ARGS__))
#define ARG_63(x, ...) ARG_1(x) PREPARE_MACRO(ARG_62(__VA_ARGS__))
#define ARG_64(x, ...) ARG_1(x) PREPARE_MACRO(ARG_63(__VA_ARGS__))
#define ARG_65(x, ...) ARG_1(x) PREPARE_MACRO(ARG_64(__VA_ARGS__))
#define ARG_66(x, ...) ARG_1(x) PREPARE_MACRO(ARG_65(__VA_ARGS__))
#define ARG_67(x, ...) ARG_1(x) PREPARE_MACRO(ARG_66(__VA_ARGS__))
#define ARG_68(x, ...) ARG_1(x) PREPARE_MACRO(ARG_67(__VA_ARGS__))
#define ARG_69(x, ...) ARG_1(x) PREPARE_MACRO(ARG_68(__VA_ARGS__))
#define ARG_70(x, ...) ARG_1(x) PREPARE_MACRO(ARG_69(__VA_ARGS__))
#define ARG_71(x, ...) ARG_1(x) PREPARE_MACRO(ARG_70(__VA_ARGS__))
#define ARG_72(x, ...) ARG_1(x) PREPARE_MACRO(ARG_71(__VA_ARGS__))
#define ARG_73(x, ...) ARG_1(x) PREPARE_MACRO(ARG_72(__VA_ARGS__))
#define ARG_74(x, ...) ARG_1(x) PREPARE_MACRO(ARG_73(__VA_ARGS__))
#define ARG_75(x, ...) ARG_1(x) PREPARE_MACRO(ARG_74(__VA_ARGS__))
#define ARG_76(x, ...) ARG_1(x) PREPARE_MACRO(ARG_75(__VA_ARGS__))
#define ARG_77(x, ...) ARG_1(x) PREPARE_MACRO(ARG_76(__VA_ARGS__))
#define ARG_78(x, ...) ARG_1(x) PREPARE_MACRO(ARG_77(__VA_ARGS__))
#define ARG_79(x, ...) ARG_1(x) PREPARE_MACRO(ARG_78(__VA_ARGS__))
#define ARG_80(x, ...) ARG_1(x) PREPARE_MACRO(ARG_79(__VA_ARGS__))
#define ARG_81(x, ...) ARG_1(x) PREPARE_MACRO(ARG_80(__VA_ARGS__))
#define ARG_82(x, ...) ARG_1(x) PREPARE_MACRO(ARG_81(__VA_ARGS__))
#define ARG_83(x, ...) ARG_1(x) PREPARE_MACRO(ARG_82(__VA_ARGS__))
#define ARG_84(x, ...) ARG_1(x) PREPARE_MACRO(ARG_83(__VA_ARGS__))
#define ARG_85(x, ...) ARG_1(x) PREPARE_MACRO(ARG_84(__VA_ARGS__))
#define ARG_86(x, ...) ARG_1(x) PREPARE_MACRO(ARG_85(__VA_ARGS__))
#define ARG_87(x, ...) ARG_1(x) PREPARE_MACRO(ARG_86(__VA_ARGS__))
#define ARG_88(x, ...) ARG_1(x) PREPARE_MACRO(ARG_87(__VA_ARGS__))
#define ARG_89(x, ...) ARG_1(x) PREPARE_MACRO(ARG_88(__VA_ARGS__))
#define ARG_90(x, ...) ARG_1(x) PREPARE_MACRO(ARG_89(__VA_ARGS__))
#define ARG_91(x, ...) ARG_1(x) PREPARE_MACRO(ARG_90(__VA_ARGS__))
#define ARG_92(x, ...) ARG_1(x) PREPARE_MACRO(ARG_91(__VA_ARGS__))
#define ARG_93(x, ...) ARG_1(x) PREPARE_MACRO(ARG_92(__VA_ARGS__))
#define ARG_94(x, ...) ARG_1(x) PREPARE_MACRO(ARG_93(__VA_ARGS__))
#define ARG_95(x, ...) ARG_1(x) PREPARE_MACRO(ARG_94(__VA_ARGS__))
#define ARG_96(x, ...) ARG_1(x) PREPARE_MACRO(ARG_95(__VA_ARGS__))
#define ARG_97(x, ...) ARG_1(x) PREPARE_MACRO(ARG_96(__VA_ARGS__))
#define ARG_98(x, ...) ARG_1(x) PREPARE_MACRO(ARG_97(__VA_ARGS__))
#define ARG_99(x, ...) ARG_1(x) PREPARE_MACRO(ARG_98(__VA_ARGS__))
#define REG_MEMBERS(...) \
__vector.reserve(ARG_COUNTX(__VA_ARGS__)); \
PREPARE_MACRO(CAT(ARG_, ARG_COUNTX(__VA_ARGS__))(__VA_ARGS__))
调用部份,main.cpp
#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include "BaseStruct.h"
struct tagTest : public CBaseStruct
{
tagTest()
{
REG_MEMBERS(id,name,sex); //记录需要转换的成员。
}
int id; //id号
string name; //姓名
string sex; //性别
};
int _tmain(int argc, _TCHAR* argv[])
{
tagTest t;
t.id = 123456;
t.name = "张三";
t.sex = "男";
t.ToJson();
getchar();
return 0;
}
运行结果
id : 123456
name : 张三
sex : 男
你可以扩展支持的数据类型, 可以扩展新的接口,譬如:FromXml, ToXml, FromRecordSet, ToRecordSet。
另一个常用的方法,不使用基类。 把vector直接展开到结构体中。类似于
struct tagTest : public CBaseStruct
{
int id; //id号
string name; //姓名
string sex; //性别
REG_MEMBERS(id,name,sex); //记录需要转换的成员。
};
好处也是很明显的,相关的语法提示可能没了。
本文为博主原创,转载请注明原作者与出处。
(完)