In this simple example of Boost.Process 0.5 ( http://www.highscore.de/boost/process0.5/index.html) the output of a program (ls
) is feeding a stream. The stream works fine but contrary to the expectation the stream doesn't become invalid (e.g. end-of-stream) after the program finishes (similar to previous version of Boost.Process, e.g. http://www.highscore.de/boost/process/index.html)
What am I missing in order to make the stream (is
in the example) automatically invalid after child program exits?
Perhaps is it an option that I have to set in the Boost.Streams stream
of file_descriptor
?
#include <boost/process.hpp> // version 0.5 from http://www.highscore.de/boost/process0.5/process.zip
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <string>
using namespace boost::process;
using namespace boost::process::initializers;
using namespace boost::iostreams;
int main(){
boost::process::pipe p = create_pipe();
file_descriptor_sink sink(p.sink, close_handle);
child c = execute(run_exe("/usr/bin/ls"), bind_stdout(sink));
file_descriptor_source source(p.source, close_handle);
stream<file_descriptor_source> is(source);
std::string s;
while(std::getline(is, s)){
std::cout << "read: " << s << std::endl;
}
std::clog << "end" << std::endl; // never reach
}
I had a private (actually through Nabble) communication with Boris Schaeling, the author of the library. After discarding several possibilities, like bugs in posix/boost.iostreams, he gave me a slight modification of the code that works. Basically, what I can deduce is that the file_descriptor sink
must be out of scope (destroyed) in order for the stream to return an EOF. The working code simply adds a specific scope for sink
(listed at the end). I think this makes easy to encapsulate all in a pistream
kind of class. (Next step in my list will be to allow also output to the process.)
Works with Boost 1.48 (Fedora 17).
#include <boost/process.hpp> // version 0.5
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <string>
using namespace boost::process;
using namespace boost::process::initializers;
using namespace boost::iostreams;
int main() {
pipe p = create_pipe();
{
// note the scope for sink
file_descriptor_sink sink(p.sink, close_handle);
/* child c = */ // not necessary to hold a child object, it seems.
execute(run_exe("/usr/bin/ls"), bind_stdout(sink));
} // note the scope for sink
file_descriptor_source source(p.source, close_handle);
stream<file_descriptor_source> is(source);
std::string s;
while(std::getline(is, s)) {
std::cout << "read: " << s << std::endl;
}
std::clog << "end" << std::endl; // never reach
}
Compiles with c(lang)++ -lboost_system -lboost_iostreams
EDIT: This seems to work as well, which avoid the artificial scope, but can be confusing because the sink has to be a temporary:
...
pipe p = create_pipe();
execute(run_exe("/usr/bin/ls"), bind_stdout(
file_descriptor_sink(p.sink, close_handle)
));
file_descriptor_source source(p.source, close_handle);
...
This works on POSIX-like systems:
#include <boost/process.hpp> // version 0.5 from http://www.highscore.de/boost/process0.5/process.zip
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/asio.hpp>
#include <string>
using namespace boost::process;
using namespace boost::process::initializers;
using namespace boost::iostreams;
int main()
{
boost::asio::io_service io_service;
int status = 1;
boost::asio::signal_set set(io_service, SIGCHLD);
set.async_wait(
[&status](const boost::system::error_code&, int) { ::wait(&status); }
);
boost::process::pipe p = create_pipe();
file_descriptor_sink sink(p.sink, close_handle);
child c = execute(run_exe("/bin/ls"), bind_stdout(sink));
file_descriptor_source source(p.source, close_handle);
stream<file_descriptor_source> is(source);
std::string s;
while(status && std::getline(is, s))
{
std::cout << "read: " << s << std::endl;
}
std::clog << "end" << std::endl; // never reach
}
Note that it handles SIGCHLD to asynchronously set status
. It was taken from http://www.highscore.de/boost/process0.5/boost_process/tutorial.html#boost_process.tutorial.starting_a_program. This page also shows the Windows style of doing the same:
#if defined(BOOST_WINDOWS_API)
DWORD exit_code;
boost::asio::windows::object_handle handle(io_service, c.process_handle());
handle.async_wait(
[&handle, &exit_code](const boost::system::error_code&)
{ ::GetExitCodeProcess(handle.native(), &exit_code); }
);
#endif
io_service.run();