我正在维护一个自定义日志记录系统,该系统使用宏来执行诸如在每条消息中添加时间戳和源文件名之类的操作。所以类似于:
AI_LOG("Hello %s", "World!");
可能导致:
(16.38) HelloWorld.cpp LOG: Hello World!
目前,它在堆栈上创建一个char*缓冲区,将输出的初始部分放在缓冲区的开头,然后使用snprintf将输出复制到缓冲区的剩余部分。这一切都有效。。。除了它在堆栈上创建缓冲区之外,如果我有一个足够深的堆栈,有足够的日志语句,并且缓冲区不太小(例如256个字符),我可能会出现堆栈溢出。我现在需要输出更长的字符串,所以把所有这些缓冲区都放在堆栈上对我来说已经不起作用了。
考虑到这个背景。。。我想转到一个全局字符数组,我会把它做得很大(可能一开始是4K字符)。然而,我的理解是,如果我只是说这样的话:
#define AI_OUTPUT_BUFFER_SIZE 4092
char AI_OUTPUT_BUFFER[AI_OUTPUT_BUFFER_SIZE];
在我的输出系统的.h文件中,然后我冒着由包括该.h文件的每个文件创建单独缓冲区的风险。这真的是个问题吗?如果是这样的话,有没有一种好的方法(与编译器无关,兼容C++98,不使用Boost)来获得我想要的单个缓冲区?
我倾向于在标题中这样做:
#define AI_OUTPUT_BUFFER_SIZE 4096
class AIOutputBuffer
{
public:
static char buffer[AI_OUTPUT_BUFFER_SIZE];
};
然后在.cpp中,我可以:
char GAIA::AIOutputBuffer::buffer[AI_OUTPUT_BUFFER_SIZE];
但这让我头疼。。。也许我还没做好?
在人们建议它之前…是的,我可能会重写它以使用字符串或流。。。但我真的不想重写整个系统,也没有时间和资源来重写。这个系统运行良好,给了我很大的灵活性——我只需要处理内存使用问题。
从评论中(谢谢Dmitri!)我使用了extern,它就像一个符咒。所以在标题中:
#define AI_OUTPUT_BUFFER_SIZE 256
extern char AI_OUTPUT_BUFFER[AI_OUTPUT_BUFFER_SIZE];
然后在.cpp:中
char AI_OUTPUT_BUFFER[AI_OUTPUT_BUFFER_SIZE];
在解决问题的同时,还有一个更好的解决方案依赖于std::string
进行内存管理。此外,它在多线程环境中运行得非常好。
这个想法是使用老式的snprintf
,但让std::string
负责内存管理:
#include <string>
#include <cstdarg>
std::string va(const char* fmt, ...)
{
// make sure your stack can handle a one-time allocation of a buffer of this size
const auto BUFFER_SIZE = 8192;
// ...unless you know you're not going to call this from multiple threads;
// then you can make this buffer static and the size can be increased
char buffer[BUFFER_SIZE];
va_list args;
va_start(args, fmt);
std::vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
buffer[sizeof(buffer) - 1] = ' ';
return buffer;
}
用法:此处无异常:auto logMsg = va("Hello %s", "World!");
。