boost

Async boost::process

Using all 3 pipes of a child process asynchronously.

#include <vector>
#include <string>
#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <boost/process/windows.hpp>

int Run(
    const std::string& exeName,         ///< could also be UTF-16 for Windows
    const std::string& args,            ///< could also be UTF-16 for Windows
    const std::string& input,           ///< [in] data for stdin
    std::string& output,                ///< [out] data from stdout
    std::string& error                  ///< [out] data from stderr
)
{
    using namespace boost;

    asio::io_service ios;

    // stdout setup
    //
    std::vector<char> vOut(128 << 10);           // that worked well for my decoding app.
    auto outBuffer{ asio::buffer(vOut) };
    process::async_pipe pipeOut(ios);

    std::function<void(const system::error_code & ec, std::size_t n)> onStdOut;
    onStdOut = [&](const system::error_code & ec, size_t n)
    {
        output.reserve(output.size() + n);
        output.insert(output.end(), vOut.begin(), vOut.begin() + n);
        if (!ec)
        {
            asio::async_read(pipeOut, outBuffer, onStdOut);
        }
    };

    // stderr setup
    //
    std::vector<char> vErr(128 << 10);
    auto errBuffer{ asio::buffer(vErr) };
    process::async_pipe pipeErr(ios);

    std::function<void(const system::error_code & ec, std::size_t n)> onStdErr;
    onStdErr = [&](const system::error_code & ec, size_t n)
    {
        error.reserve(error.size() + n);
        error.insert(error.end(), vErr.begin(), vErr.begin() + n);
        if (!ec)
        {
            asio::async_read(pipeErr, errBuffer, onStdErr);
        }
    };

    // stdin setup
    //
    auto inBuffer{ asio::buffer(input) };
    process::async_pipe pipeIn(ios);

    process::child c(
        exeName + " " + args,                   // exeName must be full path
        process::std_out > pipeOut, 
        process::std_err > pipeErr, 
        process::std_in < pipeIn
    );

    asio::async_write(pipeIn, inBuffer, 
        [&](const system::error_code & ec, std::size_t n) 
        {
            pipeIn.async_close();                     //  tells the child we have no more data
        });

    asio::async_read(pipeOut, outBuffer, onStdOut);
    asio::async_read(pipeErr, errBuffer, onStdErr);

    ios.run();
    c.wait();
    return c.exit_code();                            // return the process' exit code.
}

IMPORTANT for boost 1.64

There is a bug/fix for boost 1.64, the bug only affects Windows, apparently.

reference: https://github.com/klemens-morgenstern/boost-process/issues/90 and https://github.com/klemens-morgenstern/boost-process/commit/74814e46c1614850a8e447fd689c21cf82f36ceb

in file boost\process\detail\windows\async_pipe.hpp, line 79:

    ~async_pipe()
    {
//fix
        //if (_sink .native()  != ::boost::detail::winapi::INVALID_HANDLE_VALUE_)
        //    ::boost::detail::winapi::CloseHandle(_sink.native());
        //if (_source.native() != ::boost::detail::winapi::INVALID_HANDLE_VALUE_)
        //    ::boost::detail::winapi::CloseHandle(_source.native());
        boost::system::error_code ec;
        close(ec);
//fix
    }

This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow