Search code examples
c++boost-asioboost-process

Reading output of blocking process


I have some simple application:

//first.exe

int main()
{
  std::cout << "1" << std::endl;
  std::cout << "2" << std::endl;
  std::cout << "3" << std::endl;

  std::cin.get();

  return 0;
}

And I want to invoke a callback in separate application when "2" occurs in stdout of application run as a child process:

//second.exe

int main()
{
  boost::asio::io_context context;
  boost::process::async_pipe out(context);
  boost::asio::streambuf buffer;

  boost::process::child("first.exe", boost::process::std_out > out);

  boost::asio::async_read_until(out, buffer, "2", [](boost::system::error_code code, std::size_t size)
  {
    //do something
  });

  context.run();

  return 0;
}

The problem is that I have to press any button in parent process due to std::cin.get() in child process to make io_context execute callback function. Do you have any suggestions to make it work as intended?


Solution

  • In this case I don't think you need to do a thing (the std::cin read might just fail):

    Live On Coliru

    #include <boost/process.hpp>
    #include <boost/asio.hpp>
    #include <boost/process/async.hpp>
    #include <iostream>
    #include <iomanip>
    
    int main()
    {
        namespace bp = boost::process;
        boost::asio::io_context context;
        bp::async_pipe out(context);
    
        bp::child c("./first.exe", bp::std_out > out);
    
        boost::asio::streambuf buffer;
        boost::asio::async_read_until(out, buffer, "2", [&](boost::system::error_code code, std::size_t size) {
            if (code) {
                std::cerr << "Oops: " << code.message() << std::endl;
            } else {
                std::cerr << "received: " << size << " bytes: ";
    
                auto b = buffers_begin(buffer.data()), m = b+size, e = buffers_end(buffer.data());
    
                std::clog << std::quoted(std::string(b, m)) << std::endl;
                std::clog << "Note that we read more bytes: " << std::quoted(std::string(m, e)) << std::endl;
    
                buffer.consume(size);
            }
        });
    
        context.run();
        return c.exit_code();
    }
    

    Prints

    received: 3 bytes: "1
    2"
    Note that we read more bytes: "
    3
    "
    

    Cleaner

    For cleanness, you could simply close std_in:

    bp::child c("./first.exe", bp::std_out > out, bp::std_in.close());
    

    And to be proper, also add the context for async io:

    bp::child c("./first.exe", bp::std_out > out, bp::std_in.close(), context);
    

    Those all work (see live).

    More complicated

    What if you actually need to supply input to get output? Or you need to supply input based on the output? Then the commenter is right: attach a pipe to std_in (or have a buffer be written asynchronously).

    Sending a Fixed Buffer

    Live On Coliru

    • first.cpp

      #include <iostream>
      int main() {
          using namespace std;
          string s;
          while (cin >> s)
              cout << "reversed: " << string(s.rbegin(), s.rend()) << endl;
      }
      
    • main.cpp

      #include <boost/process.hpp>
      #include <boost/asio.hpp>
      #include <boost/process/async.hpp>
      #include <iostream>
      #include <iomanip>
      
      int main() {
          namespace bp = boost::process;
          boost::asio::io_context context;
          bp::async_pipe out(context);
      
          std::string i = "hello\nwo2rld\n";
          bp::child c("./first.exe", bp::std_out > out, bp::std_in < boost::asio::buffer(i), context);
      
          boost::asio::streambuf buffer;
          boost::asio::async_read_until(out, buffer, "2", [&](boost::system::error_code code, std::size_t size) {
              if (code) {
                  std::cerr << "Oops: " << code.message() << std::endl;
              } else {
                  std::cerr << "received: " << size << " bytes: ";
      
                  auto b = buffers_begin(buffer.data()), m = b+size, e = buffers_end(buffer.data());
      
                  std::clog << std::quoted(std::string(b, m)) << std::endl;
                  std::clog << "Note that we read more bytes: " << std::quoted(std::string(m, e)) << std::endl;
      
                  buffer.consume(size);
              }
          });
      
          context.run();
          return c.exit_code();
      }
      

    Prints

    received: 30 bytes: "reversed: olleh
    reversed: dlr2"
    Note that we read more bytes: "ow
    "
    

    Sending Dynamic Data

    Both synchronously and asynchronously:

    #include <boost/process.hpp>
    #include <boost/asio.hpp>
    #include <boost/process/async.hpp>
    #include <iostream>
    #include <iomanip>
    
    int main() {
        namespace bp = boost::process;
        boost::asio::io_context context;
        bp::async_pipe out(context), in(context);
    
        bp::child c("./first.exe", bp::std_out > out, bp::std_in < in, context);
    
        boost::asio::write(in, boost::asio::buffer("hello ", 6));
    
        boost::asio::streambuf buffer;
        boost::asio::async_read_until(out, buffer, "2", [&](boost::system::error_code code, std::size_t size) {
            if (code) {
                std::cerr << "Oops: " << code.message() << std::endl;
            } else {
                std::cerr << "received: " << size << " bytes: ";
    
                auto b = buffers_begin(buffer.data()), m = b+size, e = buffers_end(buffer.data());
    
                std::clog << std::quoted(std::string(b, m)) << std::endl;
                std::clog << "Note that we read more bytes: " << std::quoted(std::string(m, e)) << std::endl;
    
                buffer.consume(size);
            }
        });
    
        boost::asio::async_write(in, boost::asio::buffer("wo2rld\n", 7), [&](boost::system::error_code code, std::size_t size) {
            if (code) {
                std::cerr << "Oops: " << code.message() << std::endl;
            } else {
                std::cerr << "sent: " << size << " bytes: ";
            }
        });
    
        context.run();
        return c.exit_code();
    }
    

    Printing again:

    sent: 7 bytes: received: 30 bytes: "reversed: olleh
    reversed: dlr2"
    Note that we read more bytes: "ow
    "