【问题标题】:C++ Best approach to filtering bytes in a streamC++ 过滤流中字节的最佳方法
【发布时间】:2016-11-30 14:00:35
【问题描述】:

我正在学习 c++(来自 C 和 Java 大学的课程),今天我想编写一个类来过滤从通用流中获取的字节并将其输出写入另一个流。

简而言之,假设我想创建一个对输入进行 base64 编码并将输出写入标准输出的类。

在 bash 中我会写:

echo "some input data" | base64 

在 C++ 中,我想实现一个类 MyB64Encoder,其行为如下:

std::cout << myB64EncoderObject << "some input data";

//Alternatively, is it possible to make it like this?
std::cout << MyB64Encoder << "some input data";

问题是,myB64EncoderObject 当然有一个内部状态和一个内部缓冲区。为了防止阻塞和过多的内存使用,它必须能够读取和处理小块数据,并在处理完每个数据块后立即输出。

还有一些事情需要注意:

  • 对象必须等待输出流能够接收数据
  • 如果没有从对象读取流,则该对象必须抛出错误(有点像断了的管道?)

就效率而言,解决此类问题的最佳方法是什么?我将如何在现代 C++1x 中实现它?

【问题讨论】:

  • 包装一个流(或包装一个流缓冲区)。

标签: c++ filter stream buffer


【解决方案1】:

现有的行为是这样的:

std::cout << myB64EncoderObject << "some input data";

I/O manipulators(例如,std::boolalpha、std::hex、...)。然而,这些只是在它已经知道如何解释的流上设置标志。

如果您想保留该语法,则需要更复杂的东西,即中间包装器:

class B64Wrapper {
    std::ostream &os_;
    B64Encoder &enc_; // only if your encoder is really stateful

public:
    B64Wrapper() = delete;
    B64Wrapper(B64Wrapper&&) = default;
    B64Wrapper(B64Wrapper const&) = default;

    B64Wrapper(std::ostream &os, B64Encoder &enc) : os_(os), enc_(enc) {}


    template <typename T>
    B64Wrapper& operator<< (B64Wrapper &self, T val) {
        self.enc_.encode(os_, val);
        return self;
    }
};

B64Wrapper operator<< (std::ostream &os, B64Encoder &enc) {
    return B64Wrapper(os, enc);
}

(请注意,您仍然需要编写B64Encoder::encode(std::ostream &amp;, T value) 方法)。

如果您的编码器不是真正有状态的,则不需要对其进行引用,并将 B64Encoder 声明为具有全局实例的空标记类型以获得相同的效果 - 在这种情况下,它只存在于选择operator&lt;&lt; 过载。

另一种方法是编写一个std::basic_streambuf 实现,它将输入编码为sputc/sputn/xsputn。它可以将其他所有内容转发到包装的 streambuf 或基类,具体取决于您继承的内容。

【讨论】:

    【解决方案2】:

    你可以这样做:

    class MyEncoder
    {
    public:
    private:
        std::ostream* os = nullptr;
    
        // This overload deals with:
        // std::cout << myEncoder ...
        friend MyEncoder& operator<<(std::ostream& os, MyEncoder& me)
        {
            // grab a reference to the target output stream
            me.os = &os;
            return me;
        }
    
        // This overload deals with:
        // std::cout << MyEncoder() ...
        friend MyEncoder& operator<<(std::ostream& os, MyEncoder&& me)
        {
            // the temporary is currently bound to the l-value parameter me
            // so we can just pass this call on to the previous overload
            return os << me;
        }
    
        // This overload deals with:
        // myEncoder << <anything else>
        template<typename T>
        friend MyEncoder& operator<<(MyEncoder& me, T const& v)
        {
            // only encode if there is an output stream to send the data to
            // this will only be set if one of the above overloads was called
            if(!me.os)
                throw std::runtime_error("no stream to receive encoded data");
    
            // do your encoding here
            (*me.os) << "{encoded: " << v << "}";
    
            return me;
        }
    };
    

    基本上是为了达到这个目的:

    std::cout << MyEncoder() << "some data: " << 45;
    //                                        ^ calls operator<<(MyEncoder&, 45)
    //                       ^ calls operator<<(MyEncoder&, "some data: ")
    //        ^ calls operator<<(std::cout, MyEncoder())
    

    电话从左到右。

    它可能看起来有点复杂,但它基本上涵盖了 3 种不同的调用可能性。

    MyEncoder encoder;
    std::cout << encoder; // MyEncoder& object
    
    std::cout << MyEncoder(); // (temporary) MyEncoder&& object
    
    encoder << "anything else" // A MyEncoder& receiving any other object
    

    前两个运算符被重载以设置内部std::ostream*,第三个运算符被重载以进行实际编码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-11
      • 1970-01-01
      • 1970-01-01
      • 2012-01-15
      • 1970-01-01
      • 1970-01-01
      • 2021-02-05
      • 2020-10-24
      相关资源
      最近更新 更多