
Sub-processing with modern C++ - ingve
http://templated-thoughts.blogspot.com/2016/03/sub-processing-with-modern-c.html
======
vmorgulis
I handle that with two variadic functions (arguments are stringified and
spaced):

    
    
        template <typename... A> int shell(A... arguments)
        template <typename... A> string execute(A... arguments)
    

shell() does a system() and returns the exit code (so the output is echoed in
the terminal).

execute() launch a process with popen() and returns the captured steam as a
string.

About capture of stderr with fork(), I like the way it's done in Node.js:
[https://nodejs.org/api/child_process.html](https://nodejs.org/api/child_process.html)

shell:
[https://github.com/vmorgulys/sandbox/blob/master/stackcity/s...](https://github.com/vmorgulys/sandbox/blob/master/stackcity/source/core/i.function.shell.cpp)

pfile:
[https://github.com/vmorgulys/sandbox/blob/master/stackcity/s...](https://github.com/vmorgulys/sandbox/blob/master/stackcity/source/core/class.pfile.cpp)

------
ant6n

        auto ret = sp::call({"ls", "-l"}, shell{true});
    

How does this "shell{true}" bit work? It looks like a named parameter, but I
thought those don't exist in C++.

~~~
jsnell
It's making an object of type "shell" with true as a constructor argument. But
I believe it works like named parameters in that you could use any combination
of parameters in any order. This works by making sp::call a variadic template,
with the parameter "parsing" happening at compile time based on the types of
these wrapper objects.

~~~
souperhanz
Exactly. I imagine it's something like the following:

    
    
      #include <iostream>
      #include <string>
      #include <utility>
      namespace sp {
        template <typename T>
        struct option {
          template <typename U>
          option(U&& u) : mValue(std::forward<U>(u)) {}
          const T& getValue() const { return mValue; }
          private:
          T mValue;
        };
        struct shell : option<bool> { using option<bool>::option; };
        struct output : option<std::string> { using option<std::string>::option; };
        struct options {
          bool mShell;
          std::string mOutput;
        };
        void applyOpt(options& opts, const shell& opt)
        {
          opts.mShell = opt.getValue();
          std::cout << "shell=" << (opts.mShell ? "true" : "false") << '\n';
        }
        void applyOpt(options& opts, const output& opt)
        {
          opts.mOutput = opt.getValue();
          std::cout << "output=" << opts.mOutput << '\n';
        }
        void applyOpts(options&) {}
        template <typename HeadOpt, typename... TailOpts> void applyOpts(options& opts, HeadOpt&& headOpt, TailOpts&&... tailOpts)
        {
          applyOpt(opts, std::forward<HeadOpt>(headOpt));
          applyOpts(opts, std::forward<TailOpts>(tailOpts)...);
        }
        template <typename... Opts> int call(std::initializer_list<const char*> args, Opts&&... opts)
        {
          std::cout << "args=";
          const char* sep = "";
          for (auto&& s : args)
          {
            std::cout << sep << s;
            sep = " ";
          }
          std::cout << '\n';
          options options;
          applyOpts(options, std::forward<Opts>(opts)...);
          return 0;
        }
      }
      int main()
      {
        sp::call({"ls", "-l"}, sp::shell{true}, sp::output{"foo.txt"});
        return 0;
      }

~~~
ant6n
Oh neat. Also thanks for reducing the use to a 'small' example.

Why does the author use a struct and "shell{true}" rather than creating a
function or a class that would allow "shell(true)". Love for braces?
Performance?

EDIT: also I wonder whether there's a way to create helper definitions to
allow defining named parameters like this in a more concise way. This
recursive template expansion business tends to result in long code.

~~~
ant6n
Ok, so it seems the use of braces (use of "Uniform initialization syntax") in
constructors is to reduce the ambiguity with declaring functions and
declaring+initializing variables. This wiki entry has more:

[https://en.wikipedia.org/wiki/Most_vexing_parse](https://en.wikipedia.org/wiki/Most_vexing_parse)

~~~
souperhanz
It's a very good day. I get to just say "exactly" again. :)

------
d0mine
It is incorrect to use:

    
    
      rc = subprocess.call(["ls", "-l"], shell=True)
    

It means a different thing. OP should use either:

    
    
      rc = subprocess.call(["ls", "-l"])
    

Or:

    
    
      rc = subprocess.call("ls -l", shell=True)
    

Note: there is no need to use the shell or even to use the external processs
in this case.

The example shows the usability issue in the API. Newly designed C++ API
shouldn't copy such errors (if it has the same behavior). A list argument with
shell=True should be rejected.

------
hellofunk
What does that curly brace pair mean in isolation?

auto ret = sp::call({"ls", "-l"});

It's not a lambda or a typical block. I don't think I've seen that before.

~~~
msbarnett
Specifically in this case it calls

    
    
        template<typename... Args> int call(std::initializer_list<const char*> plist, Args&&... args)
    

Where "ls", and "-l" form the contents of the initializer list.

------
amelius
It's about time.

