1

I'm working some some C++ on Windows that will allow me to launch python.exe (2.7) and interact with it using stdin, stdout, and stderr. I'm using Visual Studio 2015, Boost 1.59, and Boost Process 0.5.

I've been successful in launching python.exe by setting the command line to do something, such as "python -c "print 'hello world'", and the stdout captures "hello world".

Here's that code:

#include <boost/process.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <iostream>
#include <fstream>

namespace bp = boost::process;
namespace io = boost::iostreams;
using namespace bp;
using namespace bp::initializers;

bp::pipe create_async_pipe(std::string desc)
{
#if defined(BOOST_WINDOWS_API)
    std::string name = "\\\\.\\pipe\\boost_process_async_io\\" + desc;
    HANDLE handle1 = ::CreateNamedPipeA(name.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, 0, 1, 8192, 8192, 0, NULL);
    HANDLE handle2 = ::CreateFileA(name.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    return make_pipe(handle1, handle2);
#elif defined(BOOST_POSIX_API)
    return create_pipe();
#endif
}

int main()
{
    bp::pipe pIn = create_async_pipe("stdout");
    //bp::pipe pOut = create_async_pipe("stdin");
    {
        //io::file_descriptor_sink stdout_sink("C:\\WA\\output.txt");
        io::file_descriptor_sink stdout_sink(pIn.sink, io::close_handle);
        //io::file_descriptor_source stdin_source(pOut.source, io::close_handle);
        bp::child c = execute(
            run_exe("C:\\Python27\\python.exe"),
            set_cmd_line(L"python -c \"print 'hello world'\""),
            bind_stdout(stdout_sink),
            bind_stderr(stdout_sink)//,
            //bind_stdin(stdin_source)
            );
    }

    io::file_descriptor_source stdout_source(pIn.source, io::close_handle);
    //io::file_descriptor_sink stdin_sink(pOut.sink, io::close_handle);

    io::stream<io::file_descriptor_source> is(stdout_source);
    //io::stream<io::file_descriptor_sink> os(stdin_sink);

    //os << "print 'hello world'\r\nexit()\r\n";

    std::string output;
    std::getline(is, output);
    std::cout << output << std::endl;   
}

If I remove the set_cmd_line() or change the string to L"python", I would expect Python to launch into interactive mode, just as if I executed "python.exe" from the command line.

That code being here:

#include <boost/process.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <iostream>
#include <fstream>

namespace bp = boost::process;
namespace io = boost::iostreams;
using namespace bp;
using namespace bp::initializers;

bp::pipe create_async_pipe(std::string desc)
{
#if defined(BOOST_WINDOWS_API)
    std::string name = "\\\\.\\pipe\\boost_process_async_io\\" + desc;
    HANDLE handle1 = ::CreateNamedPipeA(name.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, 0, 1, 8192, 8192, 0, NULL);
    HANDLE handle2 = ::CreateFileA(name.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    return make_pipe(handle1, handle2);
#elif defined(BOOST_POSIX_API)
    return create_pipe();
#endif
}

int main()
{
    bp::pipe pIn = create_async_pipe("stdout");
    //bp::pipe pOut = create_async_pipe("stdin");
    {
        //io::file_descriptor_sink stdout_sink("C:\\WA\\output.txt");
        io::file_descriptor_sink stdout_sink(pIn.sink, io::close_handle);
        //io::file_descriptor_source stdin_source(pOut.source, io::close_handle);
        bp::child c = execute(
            run_exe("C:\\Python27\\python.exe"),
            **//set_cmd_line(L"python -c \"print 'hello world'\""),**
            bind_stdout(stdout_sink),
            bind_stderr(stdout_sink)//,
            //bind_stdin(stdin_source)
            );
    }

    io::file_descriptor_source stdout_source(pIn.source, io::close_handle);
    //io::file_descriptor_sink stdin_sink(pOut.sink, io::close_handle);

    io::stream<io::file_descriptor_source> is(stdout_source);
    //io::stream<io::file_descriptor_sink> os(stdin_sink);

    //os << "print 'hello world'\r\nexit()\r\n";

    std::string output;
    std::getline(is, output);
    std::cout << output << std::endl;   
}

When I run the second example, python will run just momentarily, and then close.

Some background on this program. I want to create a Python logger that reads a Python file line by line, and executes it line by line as if it's being written in the interpreter. Therefore, there will be code such as:

pyChild.waitForPrompt();             // Waits for >>>, >>?, ..., etc.
pyChild.write("print 'hello world'); // >>> print 'hello world'
std::cout << pyChild.readLine();     // hello world

I'm not tied to boost, and I have tried other options such as Poco and the MSDN Windows example here without success.

Of course, after getting the stdout/stderr piped correctly will be to get stdin working as well. I have tried to get that to work as well, but have failed.

Thanks in advance!

RyanN
  • 11
  • 1
  • Maybe you'll be better served by [embedding](https://docs.python.org/2/extending/embedding.html) the interpreter in your application and running a REPL using the [code](https://docs.python.org/2/library/code.html) module. – Eryk Sun Dec 18 '15 at 22:21
  • While that doesn't answer my question, I will explore this route. I'd prefer not to embed Python as the Python configuration can change depending on what computer this is installed on. I would have to compile for specific Python versions. – RyanN Dec 21 '15 at 16:46
  • Try forcing Python into interactive mode with "-i" command line option. Can you enter statements in the terminal/console? – Eryk Sun Dec 21 '15 at 19:07
  • Same problem. Python doesn't continue running. I would expect it to continue running until I issue it an "exit()", but it stops after the first command is executed. – RyanN Dec 28 '15 at 12:37

1 Answers1

1

Maybe too late... but it is working for me with the -i and -u options: Using boost 1.69, python 3.7

//create synchronous pipe streams
bp::opstream pyin;
bp::ipstream pyout;
//launch child process with the options for unbuffered std_in and out and interpreter
bp::child c("python -u -i", bp::std_in < pyin, bp::std_out > pyout);

//send py instruccions to python process and recover stdout
pyin << "print('Hello');" << endl;
string pyout_str_hello;
pyout >> pyout_str_hello;

pyin << "print('World');" << endl;
string pyout_str_world;
pyout >> pyout_str_world;

cout << pyout_str_hello << ' ' << pyout_str_world << endl;
Pep Cots
  • 11
  • 1