1.前言
本篇博客一个使用 宏 的方式来配置代码的 Debug 与 Release 版本之间的差异。建议为每一个文件都配备单独的宏,方便进行更细粒度的控制。
2.编程框架
Debug 版本的需求:
- 能够在程序发生错误时输出相应的 error code,最好能够带有颜色高亮。
- 能够方便的移除,在 Release 版本中能够一键去除所有的 Debug 输出。
下面的代码是一段通用的版本控制的宏命令,但是这些宏在添加时比较麻烦,每次都需要输入这三段内容。所以我想要介绍一种我感觉比较好的版本控制框架。
#ifdef DEBUG
// debug version content
#else
// release version content
#endif
【1】颜色字。
- 对于人眼的注意力机制的完美适配,会优先注意到颜色和高亮的位置。
- 通过如下的宏定义就能够使用 PRINTCOLOR(CMD_RED, “errorcode %d”, errorcode); 的方式来输出带有颜色的信息。
// below is for debug output
#define CMD_RED FOREGROUND_RED
#define CMD_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#define CMD_YELLOW FOREGROUND_RED |FOREGROUND_GREEN
#define CMD_GREEN FOREGROUND_GREEN
#define CMD_BLUE FOREGROUND_BLUE
#define CMD_PINK FOREGROUND_RED | FOREGROUND_BLUE
#define CMD_CYAN FOREGROUND_GREEN | FOREGROUND_BLUE
#define SETCOLOR(color) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
#define PRINTCOLOR(color, str, ...) SETCOLOR(color); printf(str, ##__VA_ARGS__); SETCOLOR(CMD_WHITE);
【2】DBG宏定义。
- 对于每一个文件,都定义一个 DBG 的宏。通过#ifdef——#else来进行控制宏的不同版本。
- 当宏未定义时,将相应的 PRINT 宏定义为空,这时其中的内容不会被调用,会被编译器优化。
// for debug info in Common
#ifdef DBG_COMMON
#define PRINT_COMMON PRINTCOLOR
#else
#define PRINT_COMMON
#endif
【3】THREE_COMBO宏定义
- 考虑到在 Windows 中一般函数发生错误后需要调用 GetLastError 函数来获取错误代码,所以编写下面的输出三连,能够将有用信息都进行输出,同时为了能够有效的进行 文件级 的粒度控制,所以定义了一个母版宏 THREE_COMBO。
#define THREE_COMBO(name, func) \
PRINT_##name(CMD_RED, "Fail to call the "); \
PRINT_##name(CMD_WHITE, #func"."); \
PRINT_##name(CMD_YELLOW, "Error code is 0x%x. line : %d\n", GetLastError(), __LINE__);
- 通过将母版宏进行实例化,得到每一个文件的宏,这样在该文件中使用对应的宏来输出 Debug 信息。而在 Release 版中,由于 PRINT 宏被定义为了空,所以 THREE_COMBO 中的内容会被编译器优化掉。
#define THREE_COMBO_COMMON(func) THREE_COMBO(COMMON, func)
【4】cout 使用
- 2025年3月,在使用 C++ 中的 cout 函数时发现不能使用上面的方式进行, cout 不是变参函数,需要一个个进行解析。
- 目前只是能够输出 颜色字 但是不能够控制输出的 存在。
- 只需要自定义一个类,然后重载运算符 operator<< ,在其中设置控制台的颜色即可。
- 记住一点,头文件中的函数定义必须添加 static 或 inline 前缀。
// below is for debug output
class FWColor {
WORD wColor;
public:
FWColor(WORD color) : wColor(color) {}
friend std::ostream& operator<<(std::ostream&, const FWColor&);
};
static std::ostream& operator<<(std::ostream& os, const FWColor& clsColor) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), clsColor.wColor);
return os;
}
const FWColor CMD_RED(FOREGROUND_RED);
const FWColor CMD_WHITE(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
const FWColor CMD_YELLOW(FOREGROUND_RED | FOREGROUND_GREEN);
const FWColor CMD_GREEN(FOREGROUND_GREEN);
const FWColor CMD_BLUE(FOREGROUND_BLUE);
const FWColor CMD_PINK(FOREGROUND_RED | FOREGROUND_BLUE);
const FWColor CMD_CYAN(FOREGROUND_GREEN | FOREGROUND_BLUE);
3. 使用例子
// common.h
#ifndef COMMON_H
#define COMMON_H
#include <windows.h>
// below is for debug output
#define CMD_RED FOREGROUND_RED
#define CMD_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#define CMD_YELLOW FOREGROUND_RED |FOREGROUND_GREEN
#define CMD_GREEN FOREGROUND_GREEN
#define CMD_BLUE FOREGROUND_BLUE
#define CMD_PINK FOREGROUND_RED | FOREGROUND_BLUE
#define CMD_CYAN FOREGROUND_GREEN | FOREGROUND_BLUE
#define SETCOLOR(color) SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
#define PRINTCOLOR(color, str, ...) SETCOLOR(color); printf(str, ##__VA_ARGS__); SETCOLOR(CMD_WHITE);
#define THREE_COMBO(name, func) PRINT_##name(CMD_RED, "Fail to call the ");PRINT_##name(CMD_WHITE, #func".");PRINT_##name(CMD_YELLOW, "Error code is 0x%x. line : %d\n", GetLastError(), __LINE__);
// for debug info in Common
#ifdef DBG_COMMON
#define PRINT_COMMON PRINTCOLOR
#else
#define PRINT_COMMON
#endif
#endif
// common.cpp
#include "common.h"
#define DBG_COMMON //if in release comment this definition
int main() {
if(GetCurrentProcess() == FALSE) {
// use the encapsulated output
THREE_COMBO_COMMON(GetCurrentProcess);
// if you want output something else
PRINT_COMMON(CMD_CYAN, "good very %d\n", 1);
}
}