Sub-processing with modern C++
templated-thoughts.blogspot.com
templated-thoughts.blogspot.com
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
shell: https://github.com/vmorgulys/sandbox/blob/master/stackcity/s...
pfile: https://github.com/vmorgulys/sandbox/blob/master/stackcity/s...
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++. #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;
}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.
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.
auto ret = sp::call({"ls", "-l"});
It's not a lambda or a typical block. I don't think I've seen that before.
template<typename... Args> int call(std::initializer_list<const char*> plist, Args&&... args)
Where "ls", and "-l" form the contents of the initializer list.