我有一个基于printf
样式格式的日志框架:
void Logger::debug(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
this->output(DebugLevel, fmt, args);
va_end(args);
}
如果Logger::output
抛出,编译器是否会正确展开堆栈,还是需要在catch子句中添加带va_end(args)
的try / catch块?这可能是RAII',还是va_end
太神奇了?如果可能,请包括对标准的引用。
答案 0 :(得分:6)
不,他们不能。他们不能因为它们是宏的原因是愚蠢的。宏可以从构造函数和析构函数中使用而没有任何问题。但是,va_start
和va_end
具有必须从同一函数调用它们的特定要求。将它们移动到单独的函数是无效的。 C ++指的是C标准,C标准说“va_start
和va_copy
宏的每次调用都应该通过相同函数中va_end
宏的相应调用来匹配。” (7.15.1)如果从助手类的析构函数中调用va_end
,它可能会起作用,也可能不起作用。由于它不符合标准的要求,因此行为未定义。
编辑:至于另一个问题,当抛出异常时,你是否需要va_end
,可以做出合法的论证“va_end
宏的调用”实际上并不需要代码到达您调用该宏的位置(因为宏调用严格来说只是一个编译时操作),但强烈建议您确实需要它。是的,如果有可能存在异常,请使用try
/ catch
。 C99基本原理在其va_copy
的描述中简要地指出va_start
可以分配内存。 (我知道实际上没有实现它的实现。)在这样的实现上,va_end
然后将释放该内存,因此跳过va_end
会导致内存泄漏。
答案 1 :(得分:1)
否va_start
和va_end
是宏。所以,他们不能RAII
。此外,例外情况不需要特别小心。
答案 2 :(得分:0)
是的,这可以使用Boost.ScopeExit进行RAII,即使va_start / va_end是宏的。