c++ 标准无操作输出流

unftdfkk  于 2023-03-05  发布在  其他
关注(0)|答案(7)|浏览(225)

有没有一种方法可以创建一个基本上不做任何事情的ostream示例?
例如:

std::ostream dummyStream(...);
dummyStream << "Nothing will be printed";

我可以只创建一个ostringstream,但是数据将被缓冲(我真的不想用它们做任何事情,所以这会增加无用的开销)。
你知道吗?

  • *[edit]**找到了这个related question,它很适合我的需要。但是,我认为如果有一个答案来说明如何用标准的c++创建一个 * valid *(没有badbit)输出流,这可能会很有用。
ee7vknir

ee7vknir1#

你需要一个定制的streambuf。

class NullBuffer : public std::streambuf
{
public:
  int overflow(int c) { return c; }
};

然后,可以在任何ostream类中使用此缓冲区

NullBuffer null_buffer;
std::ostream null_stream(&null_buffer);
null_stream << "Nothing will be printed";

streambuf::overflow是当缓冲区必须将数据输出到流的实际目的地时调用的函数。当调用overflow时,上面的NullBuffer类不做任何事情,因此使用它的任何流都不会产生任何输出。

dluptydi

dluptydi2#

如果要禁用日志输出,dummyStream仍然会导致参数被求值。如果要最小化禁用日志时的影响,可以依赖条件,例如:

#define debugStream \
    if (debug_disabled) {} \
    else std::cerr

如果你有这样的代码:

debugStream << "debugging output: " << foo() << std::endl;

如果debug_disabled为真,则不计算任何参数。

cld4siwp

cld4siwp3#

新建河流类的基本方法是:
1.从std::streambuf派生类;
1.重写该类中的虚函数。这是真实的工作的地方。在你的情况下,空的实现就足够了。
1.从std::ostream派生一个类,它有一个成员,即streambuf类。

  1. streamclass的构造函数应该将指向该成员的指针转发给std::ostream的基构造函数。
    不过,恐怕您无法摆脱格式化步骤。
    希望这能给你一些启示;抱歉,我没有时间把这个问题扩展成一个完整的答案。

更新:有关详细信息,请参见john's answer

6pp0gazn

6pp0gazn4#

对于运行时可控的日志消息重定向,结合了john和Sjoerd思想的独立解决方案:

class DebugStream {
private:
    class NullStream : public std::ostream {
    private:
        class NullBuffer : public std::streambuf {
        public:
            int overflow(int c) override { return c; }
        } buffer_;
    public:
        NullStream() : std::ostream(&buffer_) {}
    } null_;

    std::ostream &output_;
    bool enabled_;

public:
    DebugStream(std::ostream &output = std::cout) : output_(output), enabled_(false) {}
    void enable(const bool enable) { enabled_ = enable; }

    template <typename T> std::ostream& operator<<(const T &arg) {
        if (enabled_) return output_ << arg;
        else return null_ << arg;
    }
};

extern DebugStream debug_stream;
#define TRACE_ENABLE(x) debug_stream.enable(x)
#define TRACELN(x) debug_stream << x << std::endl
#define TRACE(x) debug_stream << x

然后你可以做一些事情,比如:

TRACELN("The value of x is " << x " and the value of y is " << y);

使用#define跟踪宏到空语句,可以很容易地从发布版本中完全删除跟踪语句。
但是,您仍然需要在全局的某个地方定义debug_stream

vnjpjtjt

vnjpjtjt5#

如果你担心调试器的开销,那么你可以写一个非常简单的代码来清除编译时的调试消息,这就是我在我的c++程序中使用的。

#include <iostream>
#define DEBUGGING // Define this in your config.h or not.
#ifdef DEBUGGING
/*
 * replace std::cout with your stream , you don't need to
 * worry about the context since macros are simply search
 * and replace on compilation.
 */
#define LOG_START std::cout <<
#define LOG_REDIR <<
#define LOG_END   << std::endl;
#else
#define LOG_START if(0){(void)
#define LOG_REDIR ;(void)
#define LOG_END   ;}
#endif // DEBUGGING

int main(){
LOG_START "This is a log message " LOG_REDIR "Still a log message." LOG_END;
return 0;
}

现在,当你做你的项目时,检查用户是否想禁用日志记录,如果是,只要取消定义DEBUGGING宏或任何你选择检查的宏。
现在你的代码将被编译器优化,因为当任何东西被作废时,它将不包含在生成的二进制文件中(大多数情况下),使二进制文件生产准备就绪。

6qqygrtg

6qqygrtg6#

已经有了一些答案,但我认为值得补充,然而,另一个答案建立在我的评论。基本上有三个明显的变化如何创建一个不产生任何数据的流:
1.最简单的版本是将流设置为非操作状态,但这不符合要求工作流的规范:

std::ostream null(nullptr);

1.如其他答案所述,从std::sttreambuf导出流,并仅覆盖overflow(int)以返回成功,而其他人什么也不做:

struct NullBuffer
    : std::streambuf {
    int overflow(int c) override { return c; }
};

这是最短的版本,但正如已经评论过的,我不认为这是最快的方法。
1.我希望流缓冲区实际上使用一个小缓冲区并覆盖xsputn()会更快:

struct NullBuf
    : std::streambuf {
    char buffer[100];
    int overflow(int c) override {
        setp(buffer, buffer + sizeof buffer);
        return c;
    }
    std::streamsize xsputn(const char*, std::streamsize n) override {
        return n;
    }
};

小缓冲区可能更好的原因是流不需要每次都检查缓冲区是否为空并调用virtual函数,而是检查是否有缓冲区并仅偶尔调用virtual函数。
在运行三个版本的基准测试时,我得到了以下结果:

Running ./bin/clang--2a/nullstream.tsk
Run on (8 X 24 MHz CPU s)
CPU Caches:
  L1 Data 64 KiB
  L1 Instruction 128 KiB
  L2 Unified 4096 KiB (x8)
Load Average: 1.22, 1.52, 2.04
-----------------------------------------------------------
Benchmark                 Time             CPU   Iterations
-----------------------------------------------------------
BM_NullStreambuf        101 ns          101 ns      6883531
BM_NullBuffer          1430 ns         1430 ns       488561
BM_NullBuf              748 ns          748 ns       931309

也就是说,想要一个不失败的流而不是简单地完全禁用流是一个巨大的代价。使用缓冲区和覆盖xsput()是一个显著的性能提升。当然,这些是使用特定输出的微基准,尽管我没有试图太聪明:我包含了一个字符串(应该使用xsputn())和一个位数更大的整数,以使流使用std::ostreambuf_iterator<char>并绕过xsputn()
代码是使用编译的

clang++ -std=c++2a  -W -Wall -I/usr/local/include -O3 -c -o nullstream.o nullstream.cpp
clang++ -L/usr/local/lib -o nullstream.tsk nullstream.o -lbenchmark -lbenchmark_main

clang的版本在M2 MacBook上是最新的。我没有在其他计算机上运行它,也没有使用不同的IOStream实现,但我希望得到类似的结果。

#include <benchmark/benchmark.h>
#include <ostream>
#include <limits>

void run_benchmark(benchmark::State& state, std::ostream& out) {
  for (auto _ : state) {
      for (int i{0}; i != 10; ++i) {
          out << (std::numeric_limits<int>::max() - i)
              << "0123456789012345678901234567890123456789"
              << "\n";
      }
  }
}

struct NullBuffer
    : std::streambuf {
    int overflow(int c) override { return c; }
};

struct NullBuf
    : std::streambuf {
    char buffer[100];
    int overflow(int c) override {
    setp(buffer, buffer + sizeof buffer);
        return c;
    }
    std::streamsize xsputn(const char*, std::streamsize n) override {
        return n;
    }
};

static void BM_NullStreambuf(benchmark::State& state) {
  std::ostream null(nullptr);
  run_benchmark(state, null);
}

static void BM_NullBuffer(benchmark::State& state) {
  NullBuffer   buf;
  std::ostream null(&buf);
  run_benchmark(state, null);
}

static void BM_NullBuf(benchmark::State& state) {
  NullBuf      buf;
  std::ostream null(&buf);
  run_benchmark(state, null);
}

BENCHMARK(BM_NullStreambuf);
BENCHMARK(BM_NullBuffer);
BENCHMARK(BM_NullBuf);

BENCHMARK_MAIN();
93ze6v8z

93ze6v8z7#

我需要一个ostream类型的null流,所以我做了如下操作:

struct NullStream: public stringstream {
   NullStream(): stringstream() {}
};

template<typename T>
void operator<<(const NullStream&, const T&) {}

申请代码:

NullStream ns;
ostream &os = ns;
os << "foo";

真正的问题是我继承了所有的公共方法,但我并不关心它们,所以我只是懒得重写它们。

相关问题