我想要做的是创建一种“管道”(如进程之间的管道),但是在同一程序中的c ++ iostream之间。我有一个函数需要输入流作为参数,但我的数据来自输出流。那么有一种标准方法可以将std::ostream
的输出管道输入到std::istream
的输入中吗?
答案 0 :(得分:17)
您可以创建一个std::streambuf
,其中输出转到一个缓冲区,当缓冲区变满时输出std::overflow()
。另一方面,当缓冲区变空时,你会有一个输入缓冲区阻塞underflow()
。显然,阅读和写作将分为两个不同的主题。
棘手的业务是如何同步两个缓冲区:流在访问缓冲区时不使用任何同步操作。只有在调用任何虚函数时,您才能拦截操作并处理同步。另一方面,不使用缓冲区效率相当低。解决这个问题的方法是使用相对较小的输出缓冲区(例如256 char
s)并覆盖sync()
以使用此函数将字符传输到输入缓冲区。 streambuf
将使用互斥锁进行同步,使用条件变量来阻止输出上的完整输入缓冲区和输入上的空输入缓冲区。为了支持干净关闭,还应该有一个函数设置一个标志,表示不再有输入,所有进一步的输出操作都应该失败。
创建实际实现会发现两个缓冲区是不够的:访问输入和输出缓冲区的线程可能在相应的其他缓冲区阻塞时处于活动状态。因此,需要第三个中间缓冲区。通过对上述计划的这一小改动,下面是一些代码(它使用微小的缓冲区来确保实际的溢出和下溢;对于实际使用,至少输入缓冲区可能应该更大)。
// 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";
}
}
答案 1 :(得分:2)
DietmarKühl的帖子非常完整。它提供了一种完成此线程间方法的方法。但是有一种使用boost::iostreams
的简单方法。我今天需要学习如何处理流(设备)和过滤器时遇到了这个问题。我的第一次尝试才奏效。这是您问题的一线解决方案。
boost::iostreams::copy(istream,ostream);
以下是我所在位置的有效示例:
#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);
}
boost流的真正美丽之处在于,您只需添加过滤器即可对流进行转换。就我而言,以及为什么要留下长的testz
字符串,是我要向流程添加过滤器以解压缩数据。
bio::filtering_istreambuf in;
in.push(bio::gzip_decompressor());
in.push(source);
bio::copy(in, std::cout);
嗯,这还行不通,我必须弄清楚我需要什么额外的过滤器才能将电子邮件附件转换为合格的gzip流。但这是处理流的一种极好的方法。代码在顶部保持简单。我只需要将新过滤器推到gzip_decompressor
过滤器的前面即可。