流式变量作为 C++ 中的函数参数
streamed variables as a function parameter in C++
当您将变量流式传输到输出流(例如 cout)时,类型转换是自动的。我想弄清楚的是如何通过函数调用来做到这一点,例如:
inline void DEBUG(ostream& s) // Don't know if this prototype is appropriate
{
cout << s;
}
main()
{
int i = 5;
DEBUG("The value is: " << i << endl); // This doesn't compile
DEBUG("The value is: " + i + endl); // Neither does this
}
我在这里发现了类似的问题,但它们都涉及将流对象作为参数传递,而我试图将 "streamed data" 传递给函数已经有流对象,所以它是相反的。这可能吗?我不想求助于显式类型转换。我也找到了this question,但我真的不想写一个完整的记录器class,如果我能避免的话。
目前我将它实现为一个宏,这很有效,但如果可能的话我宁愿使用内联函数。
#define DEBUG(s) (cout << s)
当然编译不过。原因有很多。
首先,运算符 << 没有为标准流定义,而您正试图做到这一点:在 DEBUG() 中将流流转为流。 (双关语)。
其次,未为字符串文字定义运算符 <<,您正试图在此处调用它:
"The value is: " << i
顺便说一下,+ 也没有为文字定义。
要实现您想要看到的语义,您将不得不从流开始。字符串文字需要首先转换为流,然后你可以将 << 应用于它。这是实现你想要的唯一方法。
编辑:
既然明白了道理,我就可以给出更好的答案了。人们试图以多种方式统一隔离不同级别的调试,并且有几个库旨在实现这一目标(log4cpp、boost.log 仅举几例)。在您开始实施自己的日志记录之前,我绝对建议您研究一下。良好的日志记录不仅仅是调试级别。
如果出于任何原因,您想使用自己的自制软件,您可以探索以下两个收据:
- 使用你自己的记录器class(非常罕见的例子之一,接近于
单身的!适合单例的地方)。你可以设置
在您的应用程序开始时记录级别,而不仅仅是
调用 Logger::debug() << ...
- 用宏丰富上面的解决方案。函数的问题在于,与宏不同,它们失去了上下文。所以如果你想记录文件和日志调用的行号(你通常会这样做!),你可能想做 LOG_DEBUG << ...;这里 LOG_DEBUG 会扩展成 Logger::debug() << __FILE__ << ":" << __LINE__ << ....
- 完成此操作后,您会发现有时会调用 << 链中的其他函数。此时您可能会意识到,无论您的调试级别如何,都会调用这些函数,并且可能认为您不想在未启用调试时调用它们(类似于 LOG_DEBUG << " Object now is " << object.serialize(); 因此您需要丰富 LOG_DEBUG 宏,以便在调试级别不匹配时不执行任何操作。
- 传奇还在继续……准备好使用图书馆了吗?
嗯,(至少一些)日志库会做的是创建一个充当流的临时代理对象:
#include <iostream>
struct LoggerProxy {
LoggerProxy(const char* file, int line)
{
std::cout << "File " << file << ", line " << line << ": ";
}
template<typename T>
LoggerProxy& operator<<(T&& t)
{
std::cout << t;
return *this;
}
};
#define LOG_DEBUG LoggerProxy{__FILE__, __LINE__}
int main()
{
LOG_DEBUG << "Value is: " << 4;
}
你可以用它做很多花哨的事情,比如调试级别检查、输出到不同的流或多个后端(比如同时输出到 std::cout/cerr 和日志文件)等等.
当您将变量流式传输到输出流(例如 cout)时,类型转换是自动的。我想弄清楚的是如何通过函数调用来做到这一点,例如:
inline void DEBUG(ostream& s) // Don't know if this prototype is appropriate
{
cout << s;
}
main()
{
int i = 5;
DEBUG("The value is: " << i << endl); // This doesn't compile
DEBUG("The value is: " + i + endl); // Neither does this
}
我在这里发现了类似的问题,但它们都涉及将流对象作为参数传递,而我试图将 "streamed data" 传递给函数已经有流对象,所以它是相反的。这可能吗?我不想求助于显式类型转换。我也找到了this question,但我真的不想写一个完整的记录器class,如果我能避免的话。
目前我将它实现为一个宏,这很有效,但如果可能的话我宁愿使用内联函数。
#define DEBUG(s) (cout << s)
当然编译不过。原因有很多。
首先,运算符 << 没有为标准流定义,而您正试图做到这一点:在 DEBUG() 中将流流转为流。 (双关语)。
其次,未为字符串文字定义运算符 <<,您正试图在此处调用它:
"The value is: " << i
顺便说一下,+ 也没有为文字定义。
要实现您想要看到的语义,您将不得不从流开始。字符串文字需要首先转换为流,然后你可以将 << 应用于它。这是实现你想要的唯一方法。
编辑:
既然明白了道理,我就可以给出更好的答案了。人们试图以多种方式统一隔离不同级别的调试,并且有几个库旨在实现这一目标(log4cpp、boost.log 仅举几例)。在您开始实施自己的日志记录之前,我绝对建议您研究一下。良好的日志记录不仅仅是调试级别。
如果出于任何原因,您想使用自己的自制软件,您可以探索以下两个收据:
- 使用你自己的记录器class(非常罕见的例子之一,接近于 单身的!适合单例的地方)。你可以设置 在您的应用程序开始时记录级别,而不仅仅是 调用 Logger::debug() << ...
- 用宏丰富上面的解决方案。函数的问题在于,与宏不同,它们失去了上下文。所以如果你想记录文件和日志调用的行号(你通常会这样做!),你可能想做 LOG_DEBUG << ...;这里 LOG_DEBUG 会扩展成 Logger::debug() << __FILE__ << ":" << __LINE__ << ....
- 完成此操作后,您会发现有时会调用 << 链中的其他函数。此时您可能会意识到,无论您的调试级别如何,都会调用这些函数,并且可能认为您不想在未启用调试时调用它们(类似于 LOG_DEBUG << " Object now is " << object.serialize(); 因此您需要丰富 LOG_DEBUG 宏,以便在调试级别不匹配时不执行任何操作。
- 传奇还在继续……准备好使用图书馆了吗?
嗯,(至少一些)日志库会做的是创建一个充当流的临时代理对象:
#include <iostream>
struct LoggerProxy {
LoggerProxy(const char* file, int line)
{
std::cout << "File " << file << ", line " << line << ": ";
}
template<typename T>
LoggerProxy& operator<<(T&& t)
{
std::cout << t;
return *this;
}
};
#define LOG_DEBUG LoggerProxy{__FILE__, __LINE__}
int main()
{
LOG_DEBUG << "Value is: " << 4;
}
你可以用它做很多花哨的事情,比如调试级别检查、输出到不同的流或多个后端(比如同时输出到 std::cout/cerr 和日志文件)等等.