C ++过滤流中字节的最佳方法

时间:2016-11-30 14:00:35

标签: c++ filter stream buffer

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

巧合的是,我想说我想创建一个对输入进行base64编码的类,并将输出写入stdout。

在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中实现它?

2 个答案:

答案 0 :(得分: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 &, T value)方法)。

如果您的编码器不是真正有状态的,那么您不需要对它进行引用,并将B64Encoder声明为具有全局实例的空标记类型以获得相同的效果 - 仅存在选择operator<<重载的情况。

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

答案 1 :(得分:0)

您可以这样做:

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

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