Search code examples
c++booststdoutstderrboost-process

How to ensure that we read all lines from boost::child process


I saw the following code on boost::child documentation page where they explain how to read the output of a child process. http://www.boost.org/doc/libs/1_64_0/doc/html/boost_process/tutorial.html They say after running your child process, we can read it via this loop:-

bp::ipstream is; //reading pipe-stream
bp::child c(bp::search_patk("nm"), file, bp::std_out > is);

//then later
while (c.running() && std::getline(is, line) && !line.empty())
        data.push_back(line);

I have 2 questions here :-

  1. If c.running() returns false, we simply exit the loop. And in that case, the stream is above may still carry data which gets lost ?
  2. What is the best way to read stdout and stderr both, while making sure the process exit() doesn't create a deadlock There is a warning on the page saying :-
    The pipe will cause a deadlock if you try to read after nm exited

I wish to capture both stdout and stderr without having to worry about nm has exited or not above.


Solution

  • I think there's no proper way unless you use asynchronous methods.

    Perhaps you can simple get a future to a vector and use string_views into that if you somehow really need it line-by-line.

    std::future<std::vector<char> > output, error;
    
    boost::asio::io_service svc;
    bp::child c(bp::search_path("nm"), file, bp::std_out > output, bp::std_err > error, svc);
    svc.run();
    

    To read exactly like you did before you can use an istream on top of the vector:

    #include <boost/process.hpp>
    #include <boost/iostreams/device/array.hpp>
    #include <boost/iostreams/stream_buffer.hpp>
    #include <iostream>
    
    namespace bp = boost::process;
    namespace bio = boost::iostreams;
    std::string const file = "./a.out";
    
    int main() {
        std::future<std::vector<char> > output, error;
    
        boost::asio::io_service svc;
        bp::child c(bp::search_path("nm"), file, bp::std_out > output, bp::std_err > error, svc);
        svc.run();
    
        //then later
        {
            auto raw = output.get();
            std::vector<std::string> data;
            std::string line;
            bio::stream_buffer<bio::array_source> sb(raw.data(), raw.size());
            std::istream is(&sb);
    
            while (std::getline(is, line) && !line.empty())
                data.push_back(line);
    
            std::cout << data.at(rand()%data.size()) << "\n";
        }
    
    }