Как связать завершение программы с конечным потоком в Boost.Process 0.5?

В этом простом примере Boost.Process 0.5 (http://www.highscore.de/boost/process0.5/index.html) вывод программы (ls) подает поток. Поток работает отлично, но вопреки ожидаемому потоку после завершения программы поток не становится недействительным (например, конец потока) (аналогично предыдущей версии Boost.Process, например http://www.highscore.de/boost/process/index.html)

Что мне не хватает, чтобы сделать поток (is в примере) автоматически недействительным после выхода дочерней программы?

Возможно, это вариант, который я должен установить в Boost.Streams stream из 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
}

Ответ 1

У меня было частное (фактически через Nabble) сообщение с Борисом Шалином, автором библиотеки. После отказа от нескольких возможностей, таких как ошибки в posix/boost.iostreams, он дал мне небольшую модификацию кода, который работает. В принципе, я могу вывести, что file_descriptor sink должен быть недоступен (уничтожен), чтобы поток возвращал EOF. Рабочий код просто добавляет конкретную область для sink (указан в конце). Я думаю, что это упрощает инкапсуляцию всех в класс pistream. (Следующим шагом в моем списке будет разрешение также выводить на процесс.)

Работает с 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
}

Скомпилируется с c(lang)++ -lboost_system -lboost_iostreams

EDIT: похоже, это работает, что позволяет избежать искусственного масштаба, но может быть запутанным, потому что раковина должна быть временной:

    ...
    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);
    ...

Ответ 2

Это работает с POSIX-подобными системами:

#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
}

Обратите внимание, что он обрабатывает SIGCHLD для асинхронного набора status. Это было взято из http://www.highscore.de/boost/process0.5/boost_process/tutorial.html#boost_process.tutorial.starting_a_program. Эта страница также показывает стиль Windows, который делает то же самое:

#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();