`execute` is a function that takes `T&&...`, but certain types are going to fail internally. For example, the `execute` signature allows for zero arguments which does not make sense. And what advantage is there to making the first argument templated instead of a `const char*`? Is there an operating system that takes an argument other than a null-terminated c-style string for the command parameter? It is easy to provide forwarding overloads for other types such as boost::filesystem.
Good point, especially since the failure when passing zero arguments will only come up at run-time. That's indeed bad, but could easily be solved with an enable_if or static_assert. It is actually not a requirement to put the command first in the execute function, which is why that is not overloaded but just a part of the T&&...; this is basically inherited from 0.5 and I see no reason, why the command should be first.
And it would be nice if the command-line arguments to the process were not intermingled with the options. What if instead of `execute` you had a function `command` that returned a callable with overloads for `operator|`, `operator<`, and `operator>`:
(command("echo", "-n", "some text") > "foo.txt")(options...) (command("wget", url) | command("sha256sum") > ostream))()
I really like this idea, and thought about features like that, but you always have the downside to it, that it's to limited. But: maybe this can be added, since execute is a template, you could build this with the execute function. The actual Problem I'd have here is, that I don't get a child-handle for each call back, which bothers me. Because this child binds the process by default, which means it waits for the exit on destructor call. This can be changed by calling detach, but it's hard to see how that would be managed in a configurable way in your example. I think that should be considered a library one implements with, i.e. atop boost.process. So basically this constructs a functor: std::futurestd::string fut; auto exec = command("wget", url) | command("sha256sum") > fut ; //and now we invoke the boost.process impl: exec(); Seperating that (though not necessarily in two libraries) has a lot of advantages: boost.process can implement all the low-level stuff in a portable way, and the other library (let's call it boost.neat_process) can than run completely wild with expression-templates without needing to be concerned about the syscalls etc..
I would consider converting static functions into constexpr functors if they are templated - or at least the `execute` function. This would make the functions in the library easier to use with std::bind and boost::fit which might be useful.
Makes sense, I use this pattern for the initializers already anyway.