如何将 C 文件指针转换为通用 C++ 流?
How to translate C file pointers into generic C++ streams?
考虑以下 C 代码:
void openFile(const char *mode, char *filename, FILE **fileptr)
{
...
*fileptr = fopen(filename, mode);
...
}
FILE *logstream;
if (LOG_FILE_ENABLED)
{
openFile("w", "mylogfile.txt", logstream);
}
else
{
logstream = stderr;
}
fprintf(logstream, "[DEBUG] Some debug message...\n");
fclose(logstream);
我正在尝试将其翻译成惯用的 C++。我如何重载 openFile()
以使其需要 std::ofstream
,但保持 logstream
流不可知?我假设它会是这样的:
void openFile(const char *mode, char *filename, std::ofstream &ofs)
{
...
ofs.open(filename);
...
}
std::ostream logstream;
if (LOG_FILE_ENABLED)
{
logstream = std::ofstream();
openFile("w", "mylogfile.txt", logstream);
}
else
{
logstream = std::cerr;
}
logstream << "[DEBUG] Some debug message..." << std::endl;
logstream.close();
然而,这显然是非常不正确的——你甚至不能像那样初始化一个普通的 std::ostream
。我应该如何处理这个问题 - 最好避免使用原始指针?
C++ 流库的设计非常古老。
尽管如此 - 它的基本思想是 ostream 或 istream 只是流缓冲区上的包装对象。
因此您可以尝试使用以下代码:
std::ostream get_log(bool str) {
if (str) return std::ostream(new std::stringbuf());
// else
std::filebuf* f = new std::filebuf();
f->open("log", std::ios_base::out);
return std::ostream(f);
}
但是,正如我提到的,这是非常古老的设计 - 所以没有 RAII - 此缓冲区不属于流 - 您需要自行删除它:
int main() {
std::ostream log = get_log(true);
log << "aaa";
std::cout << static_cast<std::stringbuf&>(*log.rdbuf()).str();
delete log.rdbuf(); // (!)
}
所以这不是很好用。
所以我的最后建议 - 在 ostream 上使用智能指针 - 像这样:
std::unique_ptr<std::ostream> get_log(bool str) {
if (str) return new std::ostringstream();
std::ofstream* f = new std::ofstream();
f->open("log", std::ios_base::out);
return f;
}
int main() {
auto log = get_log(true);
*log << "aaa";
}
我会将实际工作转移到一个单独的函数或 lambda 中,该函数或 lambda 接受 std::ostream
作为输入。然后调用者可以决定传入哪种类型的std::ostream
,例如:
void doRealWork(std::ostream &log)
{
...
log << "[DEBUG] Some debug message..." << std::endl;
...
}
if (LOG_FILE_ENABLED)
{
std::ofstream log("mylogfile.txt");
doRealWork(log);
}
else
{
doRealWork(std::cerr);
}
或:
auto theRealWork = [&](std::ostream &&log)
{
...
log << "[DEBUG] Some debug message..." << std::endl;
...
}
if (LOG_FILE_ENABLED) {
theRealWork(std::ofstream{"mylogfile.txt"});
} else {
theRealWork(static_cast<std::ostream&&>(std::cerr));
}
更新:否则,您可以做更多类似的事情:
using unique_ostream_ptr = std::unique_ptr<std::ostream, void(*)(std::ostream*)>;
unique_ostream_ptr logstream;
if (LOG_FILE_ENABLED) {
logstream = unique_ostream_ptr(new std::ofstream("mylogfile.txt"), [](std::ostream *strm){ delete strm; });
} else {
logstream = unique_ostream_ptr(&std::cerr, [](std::ostream *){});
}
*logstream << "[DEBUG] Some debug message...\n";
或:
using shared_ostream_ptr = std::shared_ptr<std::ostream>;
shared_ostream_ptr logstream;
if (LOG_FILE_ENABLED) {
logstream = std::make_shared<std::ofstream>("mylogfile.txt");
} else {
logstream = shared_ostream_ptr(&std::cerr, [](std::ostream*){});
}
*logstream << "[DEBUG] Some debug message...\n";
你快到了;您只需要注意范围规则,以及除了抽象基础 class.
之外没有 std::ostream
这样的东西的事实
所以:
std::ostream* logstreamPtr = nullptr;
std::ofstream ofs;
if (LOG_FILE_ENABLED)
{
logstreamPtr = &ofs;
openFile("w", "mylogfile.txt", ofs);
}
else
{
logstreamPtr = &std::cerr;
}
std::ostream& logstream = *logstreamPtr;
logstream << "[DEBUG] Some debug message..." << std::endl;
logstream.close();
您 不需要 引用 logstream
,但它可以让您免于以后重复引用 logstreamPtr
,那会很无聊。
不要害怕这个原始指针。这是最纯粹的指针应用。如果你愿意,你可以沿着智能指针路线走下去,但你什么也得不到,而且失去了可读性(在某些情况下,还失去了性能)。
顺便说一下,如果您担心性能,不要为每条消息打开和关闭日志文件;那太浪费了。
考虑以下 C 代码:
void openFile(const char *mode, char *filename, FILE **fileptr)
{
...
*fileptr = fopen(filename, mode);
...
}
FILE *logstream;
if (LOG_FILE_ENABLED)
{
openFile("w", "mylogfile.txt", logstream);
}
else
{
logstream = stderr;
}
fprintf(logstream, "[DEBUG] Some debug message...\n");
fclose(logstream);
我正在尝试将其翻译成惯用的 C++。我如何重载 openFile()
以使其需要 std::ofstream
,但保持 logstream
流不可知?我假设它会是这样的:
void openFile(const char *mode, char *filename, std::ofstream &ofs)
{
...
ofs.open(filename);
...
}
std::ostream logstream;
if (LOG_FILE_ENABLED)
{
logstream = std::ofstream();
openFile("w", "mylogfile.txt", logstream);
}
else
{
logstream = std::cerr;
}
logstream << "[DEBUG] Some debug message..." << std::endl;
logstream.close();
然而,这显然是非常不正确的——你甚至不能像那样初始化一个普通的 std::ostream
。我应该如何处理这个问题 - 最好避免使用原始指针?
C++ 流库的设计非常古老。 尽管如此 - 它的基本思想是 ostream 或 istream 只是流缓冲区上的包装对象。
因此您可以尝试使用以下代码:
std::ostream get_log(bool str) {
if (str) return std::ostream(new std::stringbuf());
// else
std::filebuf* f = new std::filebuf();
f->open("log", std::ios_base::out);
return std::ostream(f);
}
但是,正如我提到的,这是非常古老的设计 - 所以没有 RAII - 此缓冲区不属于流 - 您需要自行删除它:
int main() {
std::ostream log = get_log(true);
log << "aaa";
std::cout << static_cast<std::stringbuf&>(*log.rdbuf()).str();
delete log.rdbuf(); // (!)
}
所以这不是很好用。
所以我的最后建议 - 在 ostream 上使用智能指针 - 像这样:
std::unique_ptr<std::ostream> get_log(bool str) {
if (str) return new std::ostringstream();
std::ofstream* f = new std::ofstream();
f->open("log", std::ios_base::out);
return f;
}
int main() {
auto log = get_log(true);
*log << "aaa";
}
我会将实际工作转移到一个单独的函数或 lambda 中,该函数或 lambda 接受 std::ostream
作为输入。然后调用者可以决定传入哪种类型的std::ostream
,例如:
void doRealWork(std::ostream &log)
{
...
log << "[DEBUG] Some debug message..." << std::endl;
...
}
if (LOG_FILE_ENABLED)
{
std::ofstream log("mylogfile.txt");
doRealWork(log);
}
else
{
doRealWork(std::cerr);
}
或:
auto theRealWork = [&](std::ostream &&log)
{
...
log << "[DEBUG] Some debug message..." << std::endl;
...
}
if (LOG_FILE_ENABLED) {
theRealWork(std::ofstream{"mylogfile.txt"});
} else {
theRealWork(static_cast<std::ostream&&>(std::cerr));
}
更新:否则,您可以做更多类似的事情:
using unique_ostream_ptr = std::unique_ptr<std::ostream, void(*)(std::ostream*)>;
unique_ostream_ptr logstream;
if (LOG_FILE_ENABLED) {
logstream = unique_ostream_ptr(new std::ofstream("mylogfile.txt"), [](std::ostream *strm){ delete strm; });
} else {
logstream = unique_ostream_ptr(&std::cerr, [](std::ostream *){});
}
*logstream << "[DEBUG] Some debug message...\n";
或:
using shared_ostream_ptr = std::shared_ptr<std::ostream>;
shared_ostream_ptr logstream;
if (LOG_FILE_ENABLED) {
logstream = std::make_shared<std::ofstream>("mylogfile.txt");
} else {
logstream = shared_ostream_ptr(&std::cerr, [](std::ostream*){});
}
*logstream << "[DEBUG] Some debug message...\n";
你快到了;您只需要注意范围规则,以及除了抽象基础 class.
之外没有std::ostream
这样的东西的事实
所以:
std::ostream* logstreamPtr = nullptr;
std::ofstream ofs;
if (LOG_FILE_ENABLED)
{
logstreamPtr = &ofs;
openFile("w", "mylogfile.txt", ofs);
}
else
{
logstreamPtr = &std::cerr;
}
std::ostream& logstream = *logstreamPtr;
logstream << "[DEBUG] Some debug message..." << std::endl;
logstream.close();
您 不需要 引用 logstream
,但它可以让您免于以后重复引用 logstreamPtr
,那会很无聊。
不要害怕这个原始指针。这是最纯粹的指针应用。如果你愿意,你可以沿着智能指针路线走下去,但你什么也得不到,而且失去了可读性(在某些情况下,还失去了性能)。
顺便说一下,如果您担心性能,不要为每条消息打开和关闭日志文件;那太浪费了。