6
\$\begingroup\$

I have some program which takes input via stdin, writes output to stdout, and errors to stderr. I would like to call that program from another program, supplying data via stdin, and capturing output/errors via stdout and stderr. I have written the below, which seems to work fine - however, I don't know if this is the correct way to do what I want, and I'd really value an honest critique.

A basic exe:

/// TestProgram.cpp
#include <iostream>
#include <string>
#include <vector>

int main()
{
    try
    {
        std::vector<std::string> someTestInput;

        // Read from cin
        {
            std::string input;
            while (std::getline(std::cin, input))
            {
                someTestInput.emplace_back(input);
            }
        }

        // Error if nothing has been input
        if (someTestInput.empty())
        {
            throw std::logic_error("Expected some input and received nothing...");
        }

        // Write to cout
        for (const auto& output : someTestInput)
        {
            std::cout << output << '\n';
        }
    }
    catch (std::exception& e)
    {
        std::cerr << "Error caught: " << e.what() << '\n';
        return 1;
    }

    return 0;
}

And then a calling exe:

/// TestProjectCaller.cpp
#include <vector>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/process.hpp>

static const std::string PATHTOEXE = { "C:\\Test\\TestProgram.exe" };

int main()
{
    try
    {
        std::vector<std::string> args;
        args.emplace_back(PATHTOEXE);

        // Create pipes for stdout, stderr
        boost::process::pipe pstdout = boost::process::create_pipe();
        boost::process::pipe pstderr = boost::process::create_pipe();

        // Map pipe source from stdout and stderr to sources
        boost::iostreams::file_descriptor_source sourcestdout(pstdout.source, boost::iostreams::close_handle);
        boost::iostreams::file_descriptor_source sourcestderr(pstderr.source, boost::iostreams::close_handle);

        // And set up streams for the sources
        boost::iostreams::stream<boost::iostreams::file_descriptor_source> istdout(sourcestdout);
        boost::iostreams::stream<boost::iostreams::file_descriptor_source> istderr(sourcestderr);

        std::unique_ptr<boost::process::child> p;

        // Want to check for process result, but also need to ensure stdin handle is closed properly,
        // so place everything in separate scope
        {
            // Map pipe sink from stdout and stderr to sinks
            boost::iostreams::file_descriptor_sink sinkstdout(pstdout.sink, boost::iostreams::close_handle);
            boost::iostreams::file_descriptor_sink sinkstderr(pstderr.sink, boost::iostreams::close_handle);

            // For stdin, map pipe to source and sink as before - want it to close on exiting this scope
            boost::process::pipe pstdin = boost::process::create_pipe();
            boost::iostreams::file_descriptor_source sourcestdin(pstdin.source, boost::iostreams::close_handle);
            boost::iostreams::file_descriptor_sink sinkstdin(pstdin.sink, boost::iostreams::close_handle);
            boost::iostreams::stream<boost::iostreams::file_descriptor_sink> ostdin(sinkstdin);

            // Call process
            p = std::make_unique<boost::process::child>(boost::process::execute(
                boost::process::initializers::set_args(args),
                boost::process::initializers::throw_on_error(),
                boost::process::initializers::bind_stdout(sinkstdout),
                boost::process::initializers::bind_stderr(sinkstderr),
                boost::process::initializers::bind_stdin(sourcestdin)
                ));

            // Send some test data to cin - comment out the below to test for error case
            ostdin << "Test Input 1\n";
            ostdin << "Some\n";
            ostdin << "Useful\n";
            ostdin << "Data\n";
        }

        // Check if process has exited OK - if not, report errors
        if (boost::process::wait_for_exit(*p))
        {
            // Gather output from stderr
            std::string error;
            while (std::getline(istderr, error))
            {
                std::cout << "Error: " << error << '\n';
            }

            throw std::logic_error("Problem executing TestProgram...");
        }

        // Gather the output
        std::string output;
        while (std::getline(istdout, output))
        {
            std::cout << output << '\n';
        }
    }
    catch (std::exception& e)
    {
        std::cerr << "Error: " << e.what() << '\n';
        return 1;
    }
}

As I said, this seems to work ok, and correctly captures the error condition. I'd also like to know if there are cross-platform considerations - is this portable? I can't try it on a Linux box immediately, but will be able to in a few days.

Environment - Windows 7, VS2013 Professional, boost 1_53, boost.process 0.5

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.