Some of these questions haven't even been asked once, and if I write this maybe they never will be. If you have questions not answered here please send them to the author or one of the PStreams mailing lists.
pstream always fail ?pstreambuf destructor ?EOF to the child process?open() ?Aside from all the usual reasons to prefer IOStreams to stdio, the PStreams
classes give you access to any combination of a process' stdin,
stdout and stderr, as well as interfaces to
wait(2), kill(2)
and other system calls.
Most implementations of popen(3) only support
unidirectional I/O and do not
give you as much control of the process.
Simple text-based IPC between two processes. There are more flexible and powerful forms of IPC, but for many applications sending and receiving text on the standard streams is sufficient. PStreams makes this almost as easy as file IO (as it should be in UNIX!)
The original aim was to re-use functionality of existing programs rather than re-implement them, e.g. read the contents of a directory using ls(1), or send an email using mail(1), or perform arbitrary precision arithmetic using dc(1). The overhead of forking and communicating with a new process might not matter if a well-known and well-tested program already exists with the required functionality. This fits well in the UNIX tradition of combining simple tools into more complex systems using pipes and filters.
You can also use PStreams within a GUI application to run a child process and capture it's output for display in the parent process.
pstream always fail ?
PStreams uses pipes (see pipe(2)) to communicate
with the controlled process and it is not possible to reposition a stream
associated with a pipe.
The pstreambuf class supports putting up to
pstreams::pbackfail characters back
into the stream buffer, but this does not return the characters to the pipe
or have any effect on the controlled process.
Use open(string, vector<string>, pmode)
and call is_open().
Call pstreambuf::status() after the process has exited.
ipstream s("hostname");
s.close();
if (s.rdbuf()->exited())
std::cout << "Exit status was " << r.rdbuf()->status();
pstreambuf destructor ?
Waits for child process to exit. Before destructor runs you should ensure
the process exits (e.g. by sending EOF if it reads from
stdin) or send a signal using pstreambuf::kill().
TODO add a "kill_in_dtor" member to streambuf so child killed in dtor, so throwing an exception doesn't hang in dtor if child still running.
EOF to the child process?If the child process needs to read all data before it produces output
and/or exits then you must close the child process' stdin
so that it receives the end-of-file indicator. This is done by calling
pstreambuf::peof(), or inserting the peof
manipulator into the stream.
See How do I send EOF to the child process?
If the child process has produced output but reading from the stream blocks
indefinitely then it is probably caused by the buffering done by
pstreambuf. By default pstreambuf will try to
fill its internal buffer when reading from the child process; if the number
of characters available in the pipe is less than the buffer size then the
read will block until more input is available. (The buffer size is given
by the constant pstreams::bufsz.)
The std::istream interface is not really designed for
non-blocking I/O but we do have the std::streambuf::in_avail()
function, which tells you how many characters can be read from the stream
without blocking. PStreams uses in_avail() to avoid the default
blocking behaviour when reading. If in_avail() is called
on a pstreambuf when its internal character buffer is empty,
a non-blocking read will be used to fill the buffer. So if
in_avail() returns a positive number then that many characters
are already in the buffer and can be read immediately without
blocking.
Rather than of calling in_avail() directly, you can use
std::istream::readsome(char_type* s, streamsize n) which
will read min(rdbuf()->in_avail(), n) characters and store
them at s.
PStreams does not provide any way of setting a time out but it's fairly easy to implement using signals. A signal can be scheduled using alarm(2) or setitimer(2) and a handler can set a flag to indicate the signal was raised. If the flag is set when the read returns it indicates that the signal was raised before the read completed, so you should take appropriate action, such as killing the child process.
If using alarm signals in this way, be sure to reset the alarm, the flag and the signal handler after the read returns.
open() ?
The pstreambuf::open() overload taking a vector of arguments
uses execvp(3) so emulates the behaviour of
the shell in searching for a file to execute.
The single-argument overload takes a shell command which will be run as
/bin/sh -c command by calling
execl(3).
TODO allow user to specify execl/execlp and execv/execvp ?
TODO allow user to specify something other than /bin/sh ?
You should be able to use PStreams with any fairly standard-conforming compiler on a POSIX system (i.e. UNIX-like). The most important thing is that the compiler supports standard IOStreams.
__USE_STD_IOSTREAM to get standard IOStreams.
SIGCHLD when child exits etc.
SIGPIPE if write to closed pipe (after child exits).
Usual IOStreams exceptions, none from PStreams itself.
Signals should be handled by the caller.
TODO - make exception-safe, all allocations use ScopeGuard.
PStreams classes are designed to provide the basic exception-safety guarantee. If you find any leaks or crashes they are bugs that will be fixed if you report them.
However, If an exception is thrown and causes an active pstreambuf to be destroyed the process could hang indefinitely if the child process does not exit. TODO - pguard, calls kill in dtor.
No, you must serialise all operations manually, or don't share across threads. I've never tried, so don't be surprised if it all goes wrong. It might be possible to use only const operations on a pstream across threads, but no guarantees whatsoever. Non-const operations such as exited() are not at all thread-safe and must not be run concurrently.
Only by using Cygwin, PStreams only works on POSIX systems.
If you only want a console application you can use the old popen-based
branch (release 0.17), which uses the Win32 functions _popen()
and _pclose(), but these will not work in a GUI program (and
might blow up your PC, if Windows doesn't do it for you.)
This version doesn't have most of the library's features and is no longer
maintained or tested.
You could also try libexecstream instead, which is similar to PStreams but cross-platform.
Not yet. Version 0.7.x will have a new process class that pstreambuf will use, so there will be a number of changes due to that. There may also have to be changes to accomodate a Win32 port being worked on. Since all PStreams classes are templates it is not possible to hide changes to the implementation in a library, so even if the public interface remains stable you might need to recompile all your code that uses PStreams if you use a newer version of pstream.h.
I might also consider the following changes before version 1.0.0:
is_open() could be renamed to be clear it means "process was executed".
exited() could be renamed to be clear it means "executed process has stopped running".