c++boostc++17boost-process

Passing file or stdin to boost process child ambiguously


I am trying to write a boost::process-based program that is able ambiguously redirect input, output and error streams based on whether to not a file to redirect them into is defined, but I'm struggling to figure out how to approach it:

#include <string>
#include <boost/process.hpp>

int main(){
  std::string in;
  std::string out("out.txt");

  namespace bp = boost::process;

  bp::child c(bp::exe("test.exe"), bp::std_in < (in.empty() ? stdin : in),  bp::std_out > (out.empty() ? stdout : out)); // error

  return 0;
}

The ternary operator won't work cause the types are incompatible, but I don't know how to achieve this. I explored boost::variant and boost::any but to no avail.


Solution

  • Boost has chosen a notation that is intuitive to someone familiar with redirection on the command line. However, remember that this is C++. The < and > symbols are still operators; they did not become command-line arguments. The code bp::std_out > stdout is an expression. It evaluates to an object of some sort that records where the standard output should go.


    One trick for adapting to different types is to adjust the point where the condition is made. Instead of conditionally selecting the argument to operator< and operator>, conditionally select the argument to the bp::child constructor:

    bp::child c(bp::exe("test.exe"),
                in.empty() ? (bp::std_in  < stdin) : (bp::std_in  < in),
                out.empty() ? (bp::std_out > stdout) : (bp::std_out > out));
    

    This would be more obvious if it weren't for operator overloading. Even though both < symbols in the above look the same, they name different operators because the types of their operands are different. Your situation is essentially needing to call either f(bp::std_in, stdin) or g(bp::std_in, in), and there is no way to merge these calls with the ternary operator. It's confusing just because instead of f and g, the operator names are < and <.

    You might want to use helper variables to make the code easier to read:

    auto in_stream  =  in.empty() ? (bp::std_in  < stdin)  : (bp::std_in  < in);
    auto out_stream = out.empty() ? (bp::std_out > stdout) : (bp::std_out > out);
    
    bp::child c(bp::exe("test.exe"), in_stream, out_stream);