首页
社区
课程
招聘
[讨论]能不能写出这样的宏?能参数枚举与拼接
发表于: 2014-9-5 15:40 8241

[讨论]能不能写出这样的宏?能参数枚举与拼接

2014-9-5 15:40
8241
主要是用来当LOG输出
宏的功能如下:
#define XXX(...)
1.宏是一个可变参数的宏
2.宏内能否知道多个少参数,并拼接出format格式串来,再打印出变量的值?


如:

XXX(A, B, N),可分别将A,B,N的值打出来
如结果为字符串“A=1, B=2, N=3"

XXX(A, B, C, D),可分别将A,B,C,D的值打出来
如结果为字符串“A=1, B=2, C=3, D=100"

XXX(),表明没有参数需要打印,
结果字符串"",这个注意哦,可不是普通的变参能搞定的

我的设想是先在宏里要知道有多少个参数,再将参数拼接成format字符串,再通过变参直接调用给sprintf,但感觉在宏知道知道参数并拼接有一难度啊

所以问下,能实现成这样复杂的功能吗?

我列出我现在的实现方法吧:
void LOG(const char *format, ...)
{
	char szText[MAX_PATH];

	va_list arg;
	va_start(arg, format);
	vsprintf_s(szText, format, arg);
	va_end(arg);

	OutputDebugStringA(szText);
}


调用时得这样:
LOG("%s (m_pObj= %p) = %p\r\n", __FUNCTION__, m_pObj, n);

这样写不好的原因在于:
对于不同的函数,得大量修改第一个参数format!
所以我寻求一种在宏内,能根据参数名自动拼接并打印的方法

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (22)
雪    币: 2105
活跃值: (424)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
#ifdef DEBUG
#define dbgPrintf dbgPrintf__
#else
#define dbgPrintf
#endif
int dbgPrintf__(const char *fmt, ...)
{
	va_list args;
	char buffer[1024];
	int i;
	
	va_start(args, fmt);
	i=vsnprintf(buffer, sizeof(buffer)-1, fmt,args);
	va_end(args);

	OutputDebugString(buffer);
	return i;
}

2014-9-5 15:47
0
雪    币: 185
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
# 可以用于符号拼接。 楼主先试度定字符。  然后把定字符的改成valist
2014-9-5 16:04
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
[QUOTE=exile;1314534]#ifdef DEBUG
#define dbgPrintf dbgPrintf__
#else
#define dbgPrintf
#endif
int dbgPrintf__(const char *fmt, ...)
{
        va_list args;
        char buffer[...[/QUOTE]

你没看我的内容呀
我不想要fmt这个参数,很麻烦的
我就想直接把几个变量传进去,省去自己不停的改写fmt的内容

你的无法实现成我想要的调用方法,
如:dbgPrintf(A,B,C)
而一定要写成:
dbgPrintf("%s = %d, %s = %d, %s = %d",A,B,C)
写个fmt费死劲了,要这样我直接用printf了,是吧,根本不需要宏
2014-9-5 16:18
0
雪    币: 14
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5

路过回复一下:
较新的编译器(现在一般都支持的)宏可以用__VA_ARGS__支持动态参数,比如
#define LogError(Format, ...)        xxx( Format, __VA_ARGS__)

xxx的实现可以用vDbgPrintEx或者CString.FormatV等等快速实现,自己可以参见printf或者DbgPrint实现。

比较旧的编译器可以用()来包含动态参数,可以参见kdPrint
2014-9-5 16:19
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
是要拼接,但个数呢?
举例看看,真心想找到好用的宏
2014-9-5 16:21
0
雪    币: 1443
活跃值: (101)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
参数都是%d类型的么?还是需要支持%s之类的?
2014-9-5 16:22
0
雪    币: 14
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
[QUOTE=exile;1314534]#ifdef DEBUG
#define dbgPrintf dbgPrintf__
#else
#define dbgPrintf
#endif
int dbgPrintf__(const char *fmt, ...)
{
        va_list args;
        char buffer[...[/QUOTE]

宏比函数的好处可以方便处理一些编译器信息,例如插入函数名等等:
#define LogError(Format, ...)        Log(Log_Error,        __FUNCTION__, __LINE__, Format, __VA_ARGS__)
2014-9-5 16:23
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
我就是要舍弃前面的format,而通过宏来完成自动参数拼接成format啊
你和楼上都转回原路了,对我来说没意义,我早就实现成这样了

但当要大量输出时,几千行时,不停的改写format是非常麻烦的事
2014-9-5 16:24
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
10
对,我现在就用到了__FUNCTION__,
貌似宏没有办法实现参数枚举并加#进行连接哦
2014-9-5 16:25
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11
恩,大家似乎想法完全一样,都是简单的printf了

关键如何自动生成前面的format,其它都好说
2014-9-5 16:29
0
雪    币: 236
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
可以用函数重载 或是模板实现
2014-9-5 17:15
0
雪    币: 236
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
光用宏的话无法 推导参数的类型
2014-9-5 17:20
0
雪    币: 2105
活跃值: (424)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
那你看std::cout好了。
2014-9-5 18:31
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
其实我不需要参数类型,统一按%p进行打印就行了
2014-9-5 18:32
0
雪    币: 14
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
不用format宏无法推导参数个数,除非带个参数个数进去,或者根据参数定义多个宏,Log(n,...) 或者LogN(),都影响美观,很久以前就干过这样的。

用模板可以实现这样的功能
boost里面的function有类似的功能
利用typelist和模板递归可以实现,

---

实在不行用默认参数,Log(P1 p1=NULL, .... ,Pn pn = NULL); 蛋疼

其实也许你根本不需要这样的功能,记得以前想实现一个类似function的功能,搞了很多宏和模板,太琐碎,后来直接用脚本生成了Function0,Fucntion1,...FuncionN, 简单明了快捷
我记得有人说过,我们很多时间都浪费在寻找捷径上面,而且不是经常走的捷径。
2014-9-5 19:50
0
雪    币: 236
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
// testLog.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdarg.h>


//下面这些宏 可以求出 "..." 参数的个数
#define COUNT_PARMS2(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _, ...) _
#define REPEAT_PARAMS(...) (__VA_ARGS__)
#define COUNT_PARMS(...) COUNT_PARMS2 REPEAT_PARAMS(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

// 输出不同个数参数的宏
#define Log0()
#define Log1(_arg1_) _LogA_Helper(#_arg1_, _arg1_)
#define Log2(_arg1_, _arg2_) _LogA_Helper(#_arg1_, _arg1_, #_arg2_, _arg2_)
#define Log3(_arg1_, _arg2_, _arg3_) _LogA_Helper(#_arg1_, _arg1_, #_arg2_, _arg2_, #_arg3_, _arg3_)
#define Log4(_arg1_, _arg2_, _arg3_, _arg4_) _LogA_Helper(#_arg1_, _arg1_, #_arg2_, _arg2_, #_arg3_, _arg3_, #_arg4_, _arg4_)

//根据参数个数,拼接出调用的宏 Log1(...), Log2(...), ...
#define CONNECT(a, b) CONNECT1(a, b)
#define CONNECT1(a, b) CONNECT2(a, b)
#define CONNECT2(a, b) a##b
#define Logs(...) CONNECT(CONNECT(Log, COUNT_PARMS(__VA_ARGS__)), REPEAT_PARAMS(__VA_ARGS__))

/// ////////////////////////////////////////////////////////////////////////////////
/// 重载的输出单个参数:可以根据类型确定输出格式
#define _MyPrintf printf
void _PrintOneParam(char value)
{
    _MyPrintf("'%c'", value);
}
void _PrintOneParam(const char * szLog)
{
    _MyPrintf("\"%s\"", szLog);
}
void _PrintOneParam(int value)
{
    _MyPrintf("%d", value);
}
void _PrintOneParam(float value)
{
    _MyPrintf("%f", value);
}
void _PrintOneParam(double value)
{
    _MyPrintf("%lf", value);
}
/// ...
/// ////////////////////////////////////////////////////////////////////////////////


/// ////////////////////////////////////////////////////////////////////////////////
/// 输出名字和值
template<typename T1>
void _LogA_Helper(const char * szT1Name, T1& t1)
{
    _MyPrintf("["); _MyPrintf("%s=", szT1Name); _PrintOneParam(t1); _MyPrintf("]");
    _MyPrintf("\r\n");
}
template<typename T1,typename T2>
void _LogA_Helper(const char * szT1Name, T1& t1, const char * szT2Name, T2& t2)
{
    _MyPrintf("["); _MyPrintf("%s=", szT1Name); _PrintOneParam(t1); _MyPrintf("]");
    _MyPrintf("["); _MyPrintf("%s=", szT2Name); _PrintOneParam(t2); _MyPrintf("]");
    _MyPrintf("\r\n");
}
template<typename T1,typename T2, typename T3>
void _LogA_Helper(const char * szT1Name, T1& t1, const char * szT2Name, T2& t2, const char * szT3Name, T3& t3)
{
    _MyPrintf("["); _MyPrintf("%s=", szT1Name); _PrintOneParam(t1); _MyPrintf("]");
    _MyPrintf("["); _MyPrintf("%s=", szT2Name); _PrintOneParam(t2); _MyPrintf("]");
    _MyPrintf("["); _MyPrintf("%s=", szT3Name); _PrintOneParam(t3); _MyPrintf("]");
    _MyPrintf("\r\n");
}
template<typename T1,typename T2, typename T3, typename T4>
void _LogA_Helper(const char * szT1Name, T1& t1, const char * szT2Name, T2& t2, const char * szT3Name, T3& t3, const char *szT4Name, T4& t4)
{
    _MyPrintf("["); _MyPrintf("%s=", szT1Name); _PrintOneParam(t1); _MyPrintf("]");
    _MyPrintf("["); _MyPrintf("%s=", szT2Name); _PrintOneParam(t2); _MyPrintf("]");
    _MyPrintf("["); _MyPrintf("%s=", szT3Name); _PrintOneParam(t3); _MyPrintf("]");
    _MyPrintf("["); _MyPrintf("%s=", szT4Name); _PrintOneParam(t4); _MyPrintf("]");
    _MyPrintf("\r\n");
}
/// ...
/// ////////////////////////////////////////////////////////////////////////////////

int _tmain(int argc, _TCHAR* argv[])
{
    int a = 1;
    int b = 2;
    double c = 1.2;
    Logs(a,b,c);
    return 0;
}

2014-9-5 20:26
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
18
其实重点是只要在宏里取得到每个参数名字就就办法了
用#把它们全连接成format,直接__VA_ARGS__给sprintf就行了
2014-9-9 09:55
0
雪    币: 236
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
需要参数的类型信息,不然不知道输出格式. 比如  double 类型 和 string类型 %后面的内容不一样.

我上面那个内容在 vc2010上测试了一下 . 可以 输出 参数名=参数值的格式.
2014-9-9 11:04
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
20
嗯,这个好,我测试一下
我现在实现的方法是写N个宏,参数个数不一样,统一都按%p打印,没你这个好
2014-9-9 11:40
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
21
宏的好处是不用写太多函数的实现,函数这点就麻烦,模板有空研究下

这个研究确实是费时,但做过后,对将来解决类似问题还是有一定意义的
你研究过,所以给出的结论就比较有意义,起码不会再走太多弯路
2014-9-9 11:42
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
现在vs2010及以上版本已经有functional了~不过用脚本或者vi宏生成代码也很方便~
2014-9-9 13:16
0
雪    币: 2242
活跃值: (488)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
23
歪个楼,c99虽然支持了变参宏,但还是不如c++11的变参模板好使啊

//c++ 11
#include <iostream>

template<typename T>
void debug(T t)
{
	std::cout << t << std::endl;
}

template<typename T, typename... L>
void debug(T t, L... l)
{
	std::cout << t << " ";
	debug(l...);
}

#define log_prefix __FUNCTION__, __LINE__

void main() {
	debug(log_prefix, 1);
	debug(log_prefix, 1, "hello");
	debug(log_prefix, 1, "hello", 3.14);
}
2014-9-9 14:00
0
游客
登录 | 注册 方可回帖
返回
//