Boost logo

Boost :

Subject: Re: [boost] [gsoc] Boost.Process done
From: Jeremy Maitin-Shepard (jeremy_at_[hidden])
Date: 2010-09-14 17:15:25


On 09/13/2010 12:56 PM, Boris Schaeling wrote:
> On Mon, 13 Sep 2010 01:11:33 +0200, Jeremy Maitin-Shepard
> <jeremy_at_[hidden]> wrote:
>
>> [...]closing it, etc.) From my perspective, this means there is a
>> clear need (by any users that need to do mappings > 3) for a library
>> to handle the file descriptor mapping. You make the argument that
>> boost process is not the right library, but if Boost.Process is being
>> used, it seems clear that the functionality could only be provided by
>> Boost.Process. (To have a convenient interface, I think it would
>> clearly need to be coupled with Boost.Process.)
>
> OK, let's see where we get. Can you write a small sample program
> (doesn't need to be complete) which shows how you would prefer to
> configure file descriptors greater than 3? Maybe we can come up with an
> extension which doesn't make the current API more complicated but still
> gives you the flexibility you need.

To represent existing/open file descriptors, potentially the library
could rely on boost::iostreams::file_descriptor{,_sink,_source}. (These
classes seem to basically do exactly what is needed and nothing more.
They do rely on shared_ptr for reference counting, but reference
counting is convenient.)

The fundamental operations are:

- specifying the default behavior for file descriptors not explicitly
configured (inherit or close)

- for any particular child file number, specify one of:
   1. an existing parent file descriptor to which it should be mapped;
   2. that it should be inherited (a special case of 1, in some sense);
   3. that it should be closed.

To restrict at compile time file numbers to the permitted ones on
Windows, the file number could be specified using an enum type with
values boost::process::std{in,out,err}, as discussed.

Syntax for these basic operations might be:

context ctx;

ctx[bp::stdout] = some_fd;
// Note: some_fd might be an open (filesystem) file, an open socket, etc.

inherit(ctx[3]);
close(ctx[4]);
inherit_by_default(ctx);
close_by_default(ctx);

On top of these basic operations, facilities for creating pipes, opening
/dev/null, etc. could be provided. You could also define e.g.

void inherit_standard_streams(context &ctx) {
   inherit(ctx[stdin]);
   inherit(ctx[stdout]);
   inherit(ctx[stderr]);
}

For pipes, I would think you would first implement a generic pipe
facility, e.g. some function create_pipe() that returns a struct with a
file_descriptor_source member and file_descriptor_sink member (maybe
called source and sink respectively), or possibly std::pair/boost::tuple
could be used, since the types are distinct, thereby avoiding confusion.

You then need some facility for assigning one end of the pipe to a file
descriptor in context, and returning the other end for use in the parent
process. Potentially operator overloading could be used for this
purpose, or otherwise some function call. Operator overloading has the
advantage of succinctly indicating which direction is being assigned, e.g.

file_descriptor_sink stdin_sink = ctx[bp::stdin] << create_pipe();

Although this syntax may seem a bit fancy, it would actually all involve
very little code, I think.

To support /dev/null, you could have define the function:

file_descriptor open_null_device();

and then do e.g.

ctx[bp::stdin] = open_null_device();

I don't know how convenientlyboost::iostreams integrates with asio, but
it seems that is fairly useful independent of Boost Process.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk