简体   繁体   English

c ++将输出流连接到输入流

[英]c++ connect output stream to input stream

What I would like to do is create a sort of "pipe" (like a pipe between processes), but between c++ iostreams within the same program.我想要做的是创建一种“管道”(就像进程之间的管道),但是在同一程序中的 c++ iostreams 之间。 I have a function that requires an input stream as an argument, but my data is coming from an output stream.我有一个需要输入流作为参数的函数,但我的数据来自输出流。 So is there a standard way to pipe the output of a std::ostream into the input of a std::istream ?那么是否有一种标准方法可以将std::ostream的输出通过管道传输到std::istream的输入中?

You can create a std::streambuf where the output goes to one buffer and std::overflow() blocks when the buffer becomes full.您可以创建一个std::streambuf ,其中输出进入一个缓冲区,并在缓冲区变满时std::overflow()阻塞。 On the other end you'd have an input buffer which blocks on underflow() when the buffer becomes empty.在另一端,您将有一个输入缓冲区,当缓冲区变空时,该缓冲区会在underflow()上阻塞。 Obviously, reading and writing would be in two different threads.显然,读取和写入将在两个不同的线程中。

The tricky business is how to synchronize the two buffers: The streams don't use any synchronization operations while accessing the buffers.棘手的事情是如何同步两个缓冲区:流在访问缓冲区时不使用任何同步操作。 Only when any of the virtual functions is called you can intercept the operation and deal with the synchronization.只有当任何虚函数被调用时,你才能拦截操作并处理同步。 On the other hand, not using a buffer is fairly inefficient.另一方面,不使用缓冲区是相当低效的。 The way I would address this problem is by using a relatively small output buffer (eg 256 char s) and also override sync() to use this function for transfer of characters to the input buffer.我解决这个问题的方法是使用相对较小的输出缓冲区(例如 256 个char )并覆盖sync()以使用此函数将字符传输到输入缓冲区。 The streambuf would use a mutex for the synchronization and a condition variable to block on a full input buffer on output and an empty input buffer on input. streambuf将使用互斥锁进行同步,并使用条件变量来阻塞输出时的完整输入缓冲区和输入时的空输入缓冲区。 To support clean shutdown there should also be a function setting a flag that no more input is coming and all further output operations should fail.为了支持干净关闭,还应该有一个函数设置一个标志,表明没有更多的输入到来并且所有进一步的输出操作都应该失败。

Creating the actual implementation reveals that two buffers aren't sufficient: the threads accessing the input and the output buffer may be active when the respective other buffer blocks.创建实际实现表明两个缓冲区是不够的:访问输入和输出缓冲区的线程可能在其他缓冲区阻塞时处于活动状态。 Thus, a third, intermediate buffer is needed.因此,需要第三个中间缓冲区。 With this small change to the above plan, below is some code (it uses tiny buffers to make sure there are actual overflows and underflows; for a real use at least the input buffer should probably be bigger).通过对上述计划的这个小改动,下面是一些代码(它使用微小的缓冲区来确保有实际的上溢和下溢;对于实际使用,至少输入缓冲区应该可能更大)。

// threadbuf.cpp                                                      -*-C++-*-
// ----------------------------------------------------------------------------
//  Copyright (C) 2013 Dietmar Kuehl http://www.dietmar-kuehl.de         
//                                                                       
//  Permission is hereby granted, free of charge, to any person          
//  obtaining a copy of this software and associated documentation       
//  files (the "Software"), to deal in the Software without restriction, 
//  including without limitation the rights to use, copy, modify,        
//  merge, publish, distribute, sublicense, and/or sell copies of        
//  the Software, and to permit persons to whom the Software is          
//  furnished to do so, subject to the following conditions:             
//                                                                       
//  The above copyright notice and this permission notice shall be       
//  included in all copies or substantial portions of the Software.      
//                                                                       
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES      
//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND             
//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT          
//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,         
//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING         
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR        
//  OTHER DEALINGS IN THE SOFTWARE. 
// ----------------------------------------------------------------------------


#include <algorithm>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <thread>

// ----------------------------------------------------------------------------

class threadbuf
    : public std::streambuf
{
private:
    typedef std::streambuf::traits_type traits_type;
    typedef std::string::size_type      string_size_t;

    std::mutex              d_mutex;
    std::condition_variable d_condition;
    std::string             d_out;
    std::string             d_in;
    std::string             d_tmp;
    char*                   d_current;
    bool                    d_closed;

public:
    threadbuf(string_size_t out_size = 16, string_size_t in_size = 64)
        : d_out(std::max(string_size_t(1), out_size), ' ')
        , d_in(std::max(string_size_t(1), in_size), ' ')
        , d_tmp(std::max(string_size_t(1), in_size), ' ')
        , d_current(&this->d_tmp[0])
        , d_closed(false)
    {
        this->setp(&this->d_out[0], &this->d_out[0] + this->d_out.size() - 1);
        this->setg(&this->d_in[0], &this->d_in[0], &this->d_in[0]);
    }
    void close()
    {
        {
            std::unique_lock<std::mutex> lock(this->d_mutex);
            this->d_closed = true;
            while (this->pbase() != this->pptr()) {
                this->internal_sync(lock);
            }
        }
        this->d_condition.notify_all();
    }

private:
    int_type underflow()
    {
        if (this->gptr() == this->egptr())
        {
            std::unique_lock<std::mutex> lock(this->d_mutex);
            while (&this->d_tmp[0] == this->d_current && !this->d_closed) {
                this->d_condition.wait(lock);
            }
            if (&this->d_tmp[0] != this->d_current) {
                std::streamsize size(this->d_current - &this->d_tmp[0]);
                traits_type::copy(this->eback(), &this->d_tmp[0],
                                  this->d_current - &this->d_tmp[0]);
                this->setg(this->eback(), this->eback(), this->eback() + size);
                this->d_current = &this->d_tmp[0];
                this->d_condition.notify_one();
            }
        }
        return this->gptr() == this->egptr()
            ? traits_type::eof()
            : traits_type::to_int_type(*this->gptr());
    }
    int_type overflow(int_type c)
    {
        std::unique_lock<std::mutex> lock(this->d_mutex);
        if (!traits_type::eq_int_type(c, traits_type::eof())) {
            *this->pptr() = traits_type::to_char_type(c);
            this->pbump(1);
        }
        return this->internal_sync(lock)
            ? traits_type::eof()
            : traits_type::not_eof(c);
    }
    int sync()
    {
        std::unique_lock<std::mutex> lock(this->d_mutex);
        return this->internal_sync(lock);
    }
    int internal_sync(std::unique_lock<std::mutex>& lock)
    {
        char* end(&this->d_tmp[0] + this->d_tmp.size());
        while (this->d_current == end && !this->d_closed) {
            this->d_condition.wait(lock);
        }
        if (this->d_current != end)
        {
            std::streamsize size(std::min(end - d_current,
                                          this->pptr() - this->pbase()));
            traits_type::copy(d_current, this->pbase(), size);
            this->d_current += size;
            std::streamsize remain((this->pptr() - this->pbase()) - size);
            traits_type::move(this->pbase(), this->pptr(), remain);
            this->setp(this->pbase(), this->epptr());
            this->pbump(remain);
            this->d_condition.notify_one();
            return 0;
        }
        return traits_type::eof();
    }
};

// ----------------------------------------------------------------------------

static void writer(std::ostream& out)
{
    for (std::string line; std::getline(std::cin, line); )
    {
        out << "writer: '" << line << "'\n";
    }
}

// ----------------------------------------------------------------------------

static void reader(std::istream& in)
{
    for (std::string line; std::getline(in, line); )
    {
        std::cout << "reader: '" << line << "'\n";
    }
}

// ----------------------------------------------------------------------------

int main()
{
    try
    {
        threadbuf sbuf;
        std::ostream out(&sbuf);
        std::istream in(&sbuf);

        std::thread write(&::writer, std::ref(out));
        std::thread read(&::reader, std::ref(in));

        write.join();
        sbuf.close();
        read.join();
    }
    catch (std::exception const& ex)
    {
        std::cerr << "ERROR: " << ex.what() << "\n";
    }
}

Dietmar Kühl's post is very complete. Dietmar Kühl 的帖子非常完整。 It provides a way to do this inter-thread, well done.它提供了一种在线程间执行此操作的方法,做得很好。 But there is a simple way using boost::iostreams .但是有一种使用boost::iostreams的简单方法。 I came across this today as I needed to learn how to handle streams(devices) and filters.我今天遇到了这个,因为我需要学习如何处理流(设备)和过滤器。 My first try out it just worked.我第一次尝试它只是有效。 And it is a one line solution for your question.它是针对您的问题的单行解决方案。

boost::iostreams::copy(istream,ostream);

Here is a working example of where I'm at:这是我所在位置的一个工作示例:

#include <iostream>
#include <sstream>
#include <boost/iostreams/copy.hpp>

const char* testz = R"(H4sIAJSHnl4AA61Uy3LCMAw8t19B + wF2QmAoM67ptZ / QU8Y4Alzix9hOH39fhRgnQGd66Qmx
Wq + 0kiZs86Xb2Qf4oKx5fixJ8bjhd / dsB9BshTxiPJsxD876WGuIohFRnECErd / XRmjgb + Jg
7cPs1UjCaEYTC7RQLXc2RC1CBP / SaOEl + e7fEGk1owMj0VMt1fBy + bRaVGWxXpJVWc3nK0bH
ZGJjO1B7YfbncohtYa / M6XW1KJ6KgtEByQQwSXy + KtdrrG + yHr0SzCUvvDNnWyW / a9dtWxUO
MLZj0YrhrTjCJ2yJgYiKA5YYojkqzT2jQ3BGg9udwP43YY57eAeJCi5DMvKyN9QHQ3u / doJD
lNbnrrz9HM0H23kJtXK8IvOqIuW6IAscwohnqrSdwYKMDkHGU034EG2H42pypp + ACrhqFfGc
uLEG0P8EmRJ7 + 06EgIxxEkOLOIQhM45j4vW6Lu4oG2SqARPVTuFFjy8PIBrw9c5bfbmbaeIs
dqvARBcPtYfQtXGiet32n8tP993LJH / pz2jxQpNN7f9TgcmB0RtbPT8dDqOTT8APTbkhEyYE
AAA =)";

int main(){
    std::istringstream source(testz, std::ios_base::in | std::ios_base::binary);

    namespace bio = boost::iostreams;
    bio::copy(source, std::cout);
}

What is really beautiful about the boost streams is that you can just add filters to do conversions on the stream.提升流的真正美妙之处在于您可以添加过滤器来对流进行转换。 In my case, and why I left the long testz string, is I'll add a filter to the process, to unzip the data.就我而言,以及为什么我留下长testz字符串的原因是,我将向该过程添加一个过滤器,以解压缩数据。

bio::filtering_istreambuf in;
in.push(bio::gzip_decompressor());
in.push(source);
bio::copy(in, std::cout);

Well, this is not working yet, I will have to figure out what extra filter I need to turn an email attachment into a qualified gzip stream.好吧,这还不起作用,我必须弄清楚我需要什么额外的过滤器才能将电子邮件附件转换为合格的 gzip 流。 But this is a tearably elegant way to handle streams.但这是处理流的一种优雅的方式。 The code stays simple at the top.代码在顶部保持简单。 I just have to push my new filter in front of the gzip_decompressor filter.我只需要将我的新过滤器放在gzip_decompressor过滤器前面。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM