2

This is a question about Boost.Process 0.5, not any later or earlier version, Boost now contains a Boost.Process library with a different syntax and features.

Suppose I have a simple program that ask for a number and return another number, namely:

// ask.x,  simple program with IO
#include<iostream>
int main(){
    double n;
    std::cout << "number?" << std::endl;
    std::cin >> n;
    std::cout << n + 1 << std::endl;
}

Now I want to interact with this program programmaticaly by means of Boost.Process 0.5 (http://www.highscore.de/boost/process0.5/boost_process/tutorial.html). When I try to use the library I don't get the expected behavior, the number is never sent to the program. (reading the first line is ok). I tried to write a generalization of the example described in http://www.highscore.de/boost/process0.5/boost_process/tutorial.html#boost_process.tutorial.synchronous_i_o but I failed.

MWE, the first half is a lot of necessary boilerplate, also where I think I make the mistake.

#include <boost/process.hpp> // version 0.5
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <string>
#define ALSOSEND // for fully interactive case
using namespace boost;
int main() {
    // Boilerplate code, see input only example here https://stackoverflow.com/questions/12329065/how-to-bind-program-termination-with-end-of-stream-in-boost-process-0-5
    process::pipe pi = boost::process::create_pipe();
    process::pipe po = boost::process::create_pipe();
    {
        iostreams::file_descriptor_sink sink(
            pi.sink, 
            iostreams::close_handle
        );
        iostreams::file_descriptor_source source(
            po.source, 
            boost::iostreams::close_handle
        );
        process::execute(
            process::initializers::run_exe("./ask.x"), 
            process::initializers::bind_stdout(sink)
    #ifdef ALSOSEND
            , process::initializers::bind_stdin(source)
    #endif
        );
    }

    iostreams::file_descriptor_source fdsource(pi.source,  iostreams::close_handle);
    iostreams::stream<iostreams::file_descriptor_source> is(fdsource);
    iostreams::file_descriptor_sink fdsink(po.source,  iostreams::close_handle);
    iostreams::stream<iostreams::file_descriptor_sink> os(fdsink);

    // actual interaction with the process
    std::string line;
    std::getline(is, line);
    assert(line == "number?");
    std::cout << "sending: " << "5" << std::endl;
    os << "5" << std::endl; // RUN GETS STUCK HERE
    std::getline(is, line);
    assert(line == "6");
}

Obviously I don't understand the logic of sinks and sources. I also tried using a single pipe for sink and source but it didn't work.

How can I make the program both read and write from and to the executed project?

I cannot find an example which both input and output are interleaved.


EDIT to show the working example with an earlier version of the library

This how it used to be done in Boost.Process GSOC2010 (not 0.5 as in the question above), note that the actual interaction with the program is the same as above.

#include <boost/filesystem.hpp> // quasibug in process GSOC2010 needs to include filesystem BEFORE
#include <boost/process.hpp> // version GSOC2010 (not 0.5)
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <string>

using namespace boost;
int main() {
    // boiler plate code
    std::vector<std::string> args;
    process::context ctx;
    ctx.process_name       = "askprocess";
    ctx.streams[process::stdout_id] = boost::process::behavior::pipe();
    ctx.streams[process::stdin_id ] = boost::process::behavior::pipe();

    process::child c = create_child("./ask", args, ctx);
    process::pistream is(c.get_handle(process::stdout_id));
    process::postream os(c.get_handle(process::stdin_id ));

    // actual interaction with the process
    std::string line;
    std::getline(is, line);
    assert(line == "number?");
    std::cout << "sending: " << "5" << std::endl;
    os << "5" << std::endl; // RUN GETS STUCK HERE
    std::getline(is, line);
    assert(line == "6");
}
alfC
  • 14,261
  • 4
  • 67
  • 118
  • 1
    Improved versions (you had confusing copies of the device objects): [test.cpp](http://paste.ubuntu.com/10722864/), [child.cpp](http://paste.ubuntu.com/10722869/). It [works some times](http://paste.ubuntu.com/10722872/) but I haven't found how to make it work reliably – sehe Apr 02 '15 at 09:17
  • It works sometimes? :( . I just copied your code and it gets stuck from the start (not printing even the first line). Boost 1.55, gcc 4.9.2, (or clang 3.5), Fedora 22. Very sad. Once again I have to revert to Boost.Process 0.3. – alfC Apr 02 '15 at 09:29
  • Here: ubuntu, boost 1.57, gcc 4.8.2. Now, are you implying it works in BP0.3? – sehe Apr 02 '15 at 09:34
  • @sehe, No, in Boost.Process 0.3 the code is completely different (no boost.iostream, sinks, etc) and something with this goal was easy to achieve. BTW, an unrelated thing, we determined time ago that that the sink has to go out of scope right after execute to behave well http://stackoverflow.com/questions/12329065/how-to-bind-program-termination-with-end-of-stream-in-boost-process-0-5?rq=1 – alfC Apr 02 '15 at 10:26
  • @sehe, I added and edit at the end with the full code that it used to work with Boost.Process GSOC2010. It looked easier (although there is still quite a lot of error prone boilerplate) and, on top of that, it works as expected (compiled, run and interacted well). Maybe I am making a mistake at trying to use Boost.Process 0.5 syncroneously, maybe the library is designed with asyncronous use in mind (which I am not used to). – alfC Apr 03 '15 at 08:55

1 Answers1

2

Seems to be a typo in the following line:

iostreams::file_descriptor_sink fdsink(po.source,  iostreams::close_handle);

Must be:

iostreams::file_descriptor_sink fdsink(po.sink,  iostreams::close_handle);