使用C++流类来缓冲管道读取是否可行?

5
简而言之,是否可能从流类中进行管道的缓冲读取,类似于这个伪例子所描述的内容。
请忽略您看到的任何琐碎问题(如未检查错误等)。我在我的实际代码中处理所有这些问题。这只是一个伪例子,以传达我的问题。
#include <iostream> // or istream, ifstream, strstream, etc; whatever stream could pull this off
#include <unistd.h>
#include <stdlib.h>
#include <sstream>

void myFunc() {
  int pipefd[2][2] = {{0,0},{0,0}};
  
  pipe2( pipefd[0], O_NONBLOCK );
  pipe2( pipefd[1], O_NONBLOCK );

  if( 0 == fork() ) {
    close( pipefd[0][1] );
    close( pipefd[1][1] );
    dup2( pipefd[0][0], stdout );
    dup2( pipefd[1][0], stderr );
    execv( /* some arbitrary program */ );
  } else {
    close( pipefd[0][0] );
    close( pipefd[1][0] );

    /* cloudy bubble here for the 'right thing to do'.
     * Obviously this is faulty code; look at the intent,
     * not the implementation.
     */
#ifdef RIGHT_THING_TO_DO
    for( int ii = 0; ii < 2; ++ii ) {
      cin.tie( pipefd[ii][1] );
      do {
        cin.readline( /* ... */ );
      } while( /* ... */ );
    }
#else
    // This is what I'm doing now; it works, but I'm
    // curious whether it can be done more concisely
    do {
      do {
        select( /* ... */ );
        for( int ii = 0; ii < 2; ++ii ) {
          if( FD_SET( fd[ii][1], &rfds ) ) {
            read( fd[ii][1], buff, 4096 );
            if( /* read returned a value > 0 */ ) {
              myStringStream << buff;
            } else {
              FD_CLR( fd[ii][1], &rfds );
            }
          }
        }
      } while( /* select returned a value > 0 */ );
    } while( 0 == waitpid( -1, 0, WNOHANG ) );
#endif
  }
}

编辑

下面是一个简单的示例,展示如何使用boost::file_descriptor来处理管道;同样适用于套接字,尽管没有测试过。

这是我编译它的方式:

g++ -m32 -DBOOST_IOSTREAMS_NO_LIB -isystem ${BOOST_PATH}/include \
  ${BOOST_SRC_PATH}/libs/iostreams/src/file_descriptor.cpp blah.cc -o blah

以下是示例:

#include <fcntl.h>
#include <stdio.h>

#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>

int main( int argc, char* argv[] ) {
  // if you just do 'using namespace...', there's a
  // namespace collision with the global 'write'
  // function used in the child
  namespace io = boost::iostreams;

  int pipefd[] = {0,0};
  pipe( pipefd, 0 );  // If you use O_NONBLOCK, you'll have to
                      // add some extra checks to the loop so
                      // it will wait until the child is finished.

  if( 0 == fork() ) {
    // child
    close( pipefd[0] ); // read handle
    dup2( pipefd[1], FILENO_STDOUT );
    printf( "This\nis\na\ntest\nto\nmake sure that\nit\nis\working as expected.\n" );
    return 0; // ya ya, shoot me ;p
  }

  // parent

  close( pipefd[1] ); // write handle

  char *buff = new char[1024];
  memset( buff, 0, 1024 );

  io::stream<io::file_descriptor_source> fds(
    io::file_descriptor_source( pipefd[0], io::never_close_handle ) );

  // this should work with std::getline as well
  while(   fds.getline( buff, 1024 )
        && fds.gcount() > 0 // this condition is not enough if you use
                            // O_NONBLOCK; it should only bail if this
                            // is false AND the child has exited
       ) {
    printf( "%s,", buff );
  }

  printf( "\n" );
}
2个回答

5
确实有。有一本书《C++标准库:教程与参考》,其中提供了一个示例,说明如何制作一个可以包装文件描述符(例如从pipe()获得的描述符)的std::streambuf。基于此,创建一个流非常简单。
编辑:这是那本书的链接:http://www.josuttis.com/libbook/ 此外,这里还有一个使用文件描述符的输出缓冲区示例:http://www.josuttis.com/libbook/io/outbuf2.hpp.html 另外,这里还有一个输入缓冲区的示例:http://www.josuttis.com/libbook/io/inbuf1.hpp.html

好的。我可能会先尝试一下boost建议,但我会研究一下它是如何工作的(以防需要实现它)。 - Brian Vandenberg
拥有选择总是很好 :-) 我曾经在管道、FIFO和BSD套接字周围实现了streambufs。我厌倦了使用旧的C函数,并想学习如何创建自定义流。Josutis的书是一本很好的资源 :-) - bstamour

1

您希望创建一个可以使用现有文件描述符创建的流,或者一个可以自己创建管道的流。不幸的是,没有这样的标准流类型。

您可以编写自己的流,或者例如使用 boost::iostreams::file_descriptor

编写您自己的流涉及创建basic_streambuf的子类,然后创建一个非常简单的basic_i / ostream的子类,它几乎什么也不做,只是持有您的streambuf类并提供方便的构造函数。


不需要子类化basic_i/ostream——只需创建一个工厂方法,返回一个应用了你的新streambuf的普通basic_i/ostream即可。(例如:ostream Factory() { ostream out; out.rdbuf(new YourBuff); return out; }) - Billy ONeal
对于提出的建议表示赞同。虽然我不反对自己编写,但考虑到这些东西的年代久远,似乎已经有人解决了这个问题。 - Brian Vandenberg
使用boost选项可能存在一个潜在问题;引用自文档:“目前,文件描述符设备可能无法与以非阻塞模式打开的文件描述符正常工作。” - Brian Vandenberg
boost::file_descriptor 最终实现起来相当简单和直接。你需要使用 boost::iostreams::stream 进行包装,但除此之外,我没有遇到任何真正的问题来让它工作。 - Brian Vandenberg
1
还有一个特定于libstdc++的 __gnu_cxx::stdio_filebuf,http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-api-4.5/a00074.html - nos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接