POSIX Process Control in C++ |
|
PStreams allows you to run another program from your C++ application and to transfer data between the two programs similar to shell pipelines.
In the simplest case, a PStreams class is like a C++ wrapper for the POSIX.2 functions popen(3) and pclose(3), using C++ IOStreams instead of C's stdio library.
The library provides class templates in the style of the standard IOStreams
that can be used with any standard-conforming C++ compiler on a POSIX platform.
The classes use a streambuf class that uses
fork(2) and the exec(2)
family of functions to create a new process and
to write/read data to/from the process.
The advantages over the standard popen() function are:
printf()/scanf() functions in standard C.popen() vary
between systems. Some systems use bidirectional pipes, allowing reading
and writing on the same stream, but this is not supported everywhere.
Because PStreams doesn't use popen() but re-implements it
at a lower level, bidirectional I/O is available on all systems.stderr. Input PStreams can read
from the process' stderr as well as stdout.popen() the PStreams classes can open a process
specified by a filename and a vector of arguments, similar to the
execv() function.The library is available under the GNU Lesser General Public License
To help improve PStreams see the SourceForge project page.
Latest release is 0.5.2
PStreams is now at beta stage, with working
ipstream and opstream classes for ISO
C++-compliant compilers, providing a convenient C++ alternative to
popen() and much more...
The stream buffer class, pstreambuf, creates a new process
and uses up to three pipes to communicate with it. After the new process
executes a user-supplied command the controlling PStream class has access
to any combination of the process' stdin, stdout
and stderr streams.
Another class, rpstream (Restricted PStream) is similar to
pstream except that the child process' stdout
and/or stderr cannot be read directly from the
rpstream. To read from the process you must call either
rpstream::out() or rpstream::err() to obtain a
reference to an istream that reads from the process'
corresponding output stream. This class is not as well tested as the others
The pstreambuf class can send signals to the controlled
process and can report the process' exit status.
No code-conversion is performed on multi-byte character streams.
It should be possible to use the PStream classes templatized with character
types other than char (e.g. basic_pstream<int>)
but note that characters are transfered in a bytewise manner, so it is the
programmer's responsibility to interpret the resulting character strings.
Since the classes are intended to be used to read/write data between
processes, which will usually share an internal character representation,
rather than to/from files, this behaviour should be sufficient.
For non-ISO C++-compliant compilers there are alternative versions of
all classes.
These alternative versions are unsupported and not under development!
They use non-standard extensions found in some old IOStream implementations
(such as attaching an fstream's buffer to a file descriptor with
fstreambase::attach(int fd)) that may not be available on some
systems.
For GCC 2.7/2.8/2.9x/egcs these classes are functional, but are
implemented using fstream and provide no more functionality
than the pfstream classes provided with libg++.
For all other compilers these classes have never even been compiled, let
alone tested, so use them at your own risk.
The non-compliant versions are defined in the pstream_compat.h file
and can be used by defining BACK_COMPAT
(or GCC_BACK_COMPAT for the libg++ versions)
to be 1.
Instantiating the backward-compatible ipstream or
opstream
currently sets errno to ESPIPE because the
fstreambase tries to seek to the end of the stream, which
is not allowed for pipes. errno is reset by the classes.
The FAQ answers some general questions about PStreams.
PStreams uses Doxygen to automatically
generate API documentation from the sources.
The docs for the latest release are available online.
Docs for previous releases can be generated from the sources.
Latest release is 0.5.2
Releases and anonymous CVS details are on the download page.
Please refer to the doxygen-generated documentation.
Using the PStreams classes is similar to using a std::fstream,
except that a shell command is given rather than a filename:
// print names of all header files in current directory
redi::ipstream in("ls ./*.h");
std::string str;
while (in >> str) {
std::cout << str << std::endl;
}
The command argument is a pointer to a null-terminated string containing a shell command line. This command is passed to /bin/sh using the -c flag; Alias and wildcard interpretation, if any, is performed by the shell.
Alternatively, the process can be started with a vector of arguments:
// remove some files, capturing any error messages
std::vector<std::string> argv;
std::vector<std::string> errors;
argv.push_back("rm");
argv.push_back("./foo.txt");
argv.push_back("./bar.html");
redi::ipstream in("rm", argv, pstreambuf::pstderr);
std::string errmsg;
while (std::getline(in, errmsg)) {
errors.push_back(errmsg);
}
If this form of initialisation is used and the file argument doesn't
contain a slash then the actions of the shell
will be duplicated in searching for an executable in PATH.
The shell will not interpret the other arguments, so wildcard expansion will
not take place if this interface is used.
If an rpstream was used in the example above it would be
necessary to replace the while condition like so:
while (std::getline(in.err(), errmsg)) {
errors.push_back(errmsg);
}
This form can also be used with the unrestricted pstream
and ipstream classes, but it is not strictly necessary.
The inspiration for this project came from seeing a
message
on the libstdc++-v3 mailing list
asking what had happened to the pfstream classes that came
with libstdc++-v2.
Because I'd used a similar C++ wrapper for popen() where I work,
and because I want to learn about Standard IOStreams, I decided to try to
write a solution that would work with standard-conforming C++ libraries.
(The pfstream classes were based on the old AT&T-style
IOStreams and relied on GCC-specific
libraries.) PStreams is the result of that decision.
The following have helped in one way or another:
Angelika Langer & Klaus Kreft for the invaluable examples in their book
Standard C++ IOStreams and Locales.
John Levon for comments and advice on the interface design.
Philippe Elie who wrote the ChildReader class for the
OProfile project, which was a good
starting point when replacing popen() with hand-rolled
fork/exec code.
Per Bothner et al. for the pfstream classes in the old libg++.
SourceForge for hosting this site and the project.
Brett Williams for testing things and finding the bugs I miss.