我正在使用一个C++应用程序,它使用特殊的dprintf函数来打印信息,这是一个示例:
dprintf(verbose, "The value is: %d", i);
当我为了测试目的定义详细模式时,我会打印信息,而在正常执行时,我不定义它,也不会在屏幕上看到无用的信息。我的问题是如何实现这个功能或者实现同样的想法?
我尽量避免使用可变参数的C风格函数,主要有两个原因:
我已经想出了一种使用boost::fusion
的方法,以类型安全的方式给出参数。它会迭代这些参数,在遇到%
时打印它们。如果提供太少或太多的参数,将抛出异常。
但是还有一个问题:可变宏在C++中还不是标准。因此,我制作了两个版本。其中一个适用于当前的C++版本,你需要通过以下方式调用它:
dprintf("name: %, value: %\n", ("foo", 42));
那么,使用可变参数宏的另一种版本,可以通过定义一个预处理器符号来使用,这使你能够编写如下代码:
dprintf("name: %, value: %\n", "foo", 42);
这是代码。 boost.fusion
提供了更多关于此的详细信息:
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/include/next.hpp>
#include <stdexcept>
#include <iostream>
template<typename IterS, typename IterSeqE>
void print_vec(IterS b, IterS e, IterSeqE, IterSeqE) {
while(b != e) {
if(*b == '%') {
if(++b != e && *b == '%') {
std::cout << '%';
} else {
throw std::invalid_argument("too many '%'");
}
} else {
std::cout << *b;
}
++b;
}
}
template<typename IterS, typename IterSeqB, typename IterSeqE>
void print_vec(IterS b, IterS e, IterSeqB seqb, IterSeqE seqe) {
while(b != e) {
if(*b == '%') {
if(++b != e && *b == '%') {
std::cout << '%';
} else {
std::cout << *seqb;
return print_vec(b, e, next(seqb), seqe);
}
} else {
std::cout << *b;
}
++b;
}
throw std::invalid_argument("too few '%'");
}
template<typename Seq>
void print_vec(std::string const& msg, Seq const& seq) {
print_vec(msg.begin(), msg.end(), begin(seq), end(seq));
}
#ifdef USE_VARIADIC_MACRO
# ifdef DEBUG
# define dprintf(format, ...) \
print_vec(format, boost::fusion::make_vector(__VA_ARGS__))
# else
# define dprintf(format, ...)
# endif
#else
# ifdef DEBUG
# define dprintf(format, args) \
print_vec(format, boost::fusion::make_vector args)
# else
# define dprintf(format, args)
# endif
#endif
// test, using the compatible version.
int main() {
dprintf("hello %, i'm % years old\n", ("litb", 22));
}
#ifdef DEBUG
#define dprintf(format, ...) real_dprintf(format, __VA_ARGS__)
#else
#define dprintf
#endif
这里的real_dprintf()是被调用的“真实”函数,而dprintf()只是一个包装调用的宏。
预处理器方案是可行的,但每次从一种方式转换到另一种方式都需要重新构建,这可能会让人感到烦恼。我通常会在运行时做出决定。首先,我声明:
static void do_nothing(const char *fmt, ...) { (void)fmt; }
extern void real_dprintf(const char *fmt, ...);
void (*dprintf)(const char *fmt, ...) = do_nothing;
然后在初始化代码中,我有以下内容:
if (getenv("APPLICATION") && strstr(getenv("APPLICATION"), "dprintf"))
dprintf = real_dprintf;
这样我就可以通过改变环境变量的值来快速更改模式。