简单的QDebug封装

以前习惯了使用qDebug()<<来打印调试输出. 但是新公司是Linux的程序, 默认它会打印到控制台上, 这就很令人讨厌了. 如果直接关掉QDebug, 又会让别的也用不了. 就自然想能不能通过DEBUGRELEASE来区分. 然后使用<<, 没法简单的做这件事情. 就想简单地将它封装一下, 封装成函数调用的方式. 想起很早以前玩过的C语言宏的花招, 试了两下就试出来了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define CONCATE(x,y) x##y
#define GET_NTH_ARG(_1, _2, _3, _4, _5, _6, _7, _8, n, ...) n
#define GET_ARG_COUNT(...) GET_NTH_ARG(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

#define _dbg_show(arg) << arg
#define _DEBUG_1(f, arg) f(arg)
#define _DEBUG_2(f, arg, ...) f(arg) _DEBUG_1(f, __VA_ARGS__)
#define _DEBUG_3(f, arg, ...) f(arg) _DEBUG_2(f, __VA_ARGS__)
#define _DEBUG_4(f, arg, ...) f(arg) _DEBUG_3(f, __VA_ARGS__)
#define _DEBUG_5(f, arg, ...) f(arg) _DEBUG_4(f, __VA_ARGS__)
#define _DEBUG_6(f, arg, ...) f(arg) _DEBUG_5(f, __VA_ARGS__)
#define _DEBUG_7(f, arg, ...) f(arg) _DEBUG_6(f, __VA_ARGS__)
#define _DEBUG_8(f, arg, ...) f(arg) _DEBUG_7(f, __VA_ARGS__)

#define _DEBUG_X(i) CONCATE(_DEBUG_, i)
#define _DEBUG_Y(...) _DEBUG_X(GET_ARG_COUNT(__VA_ARGS__))(_dbg_show, __VA_ARGS__)

#ifdef QT_DEBUG
#define SDEBUG(...) qDebug() << "[" << __FUNCTION__ << "]: " _DEBUG_Y(__VA_ARGS__)
#define SWARN(...) qWarning() << "[" << __FUNCTION__ << "]: " _DEBUG_Y(__VA_ARGS__)
#else
#define SDEBUG(...)
#define SWARN(...) qWarning() << "[" << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << "]: " _DEBUG_Y(__VA_ARGS__)
#endif

这样, 就可以在Release版本下将SDEBUG定义成空了.

主要是两个技巧.

一个是GET_ARG_COUNT, 它和GET_NTH_ARG配合, 量出参数的个数. 这里, GET_NTH_ARG就像是一个最大刻度为8的尺子, 组多量出8个参数. 这个宏最多支持8个参数, 如果想做, 只要往上加就可以了.

还有一个是CONCATE这个宏, 在想出这个方法之前, _DEBUG_X是直接这么写的: #define _DEBUG_X(i) _DEBUG_##i, 但是这样怎么也无法正确展开. 最后相处这么一招, 用一个看似多余的宏来确保实现二次展开操作.

至于DEBUG_1, DEBUG_2, ..., 则是手工实现的递归展开的形式, 同样就是简单的展开到8级.

要说为何不用qInstallMessageHandler? 这个就很麻烦, 因为我的程序是一个主程序框架的一个插件, 我总不能去动整个软件吧.

要问别人用什么日志? 他们使用的是一个改造自spdlog, 语法类似于format语法的调试输出工具, 我是很不习惯更不喜欢在调试时用format这种语法, 因为你要记住在格式化串里面写花括号, 一旦忘记了, 就出不来了. 另一个问题是繁琐. 只有那种需要记录到日志文件中的信息, 我才会使用它. 比如说, 加入要打印a的值, 我们需要这么写: log("a={}", a);. 而使用宏, 我们可以简单地再定义一个:

1
#define TSHOW(a)     #a":=" << a << ", "

这样, 我们只需要这样用: SDEBUG(SHOW(a))就可以打印出a=...的效果了.

当然, 这么写很粗糙, 最后总是有一个逗号留着. 但是, 反正是调试, 调试好了就删掉了, 有啥关系呢.