forked from OctaForge/libostd
documentation for subprocess
This commit is contained in:
parent
29ba5f22d3
commit
6b228ee6a8
175
ostd/process.hh
175
ostd/process.hh
|
@ -32,6 +32,7 @@ namespace ostd {
|
|||
* @{
|
||||
*/
|
||||
|
||||
/** @brief Thrown on errors in ostd::split_args(). */
|
||||
struct word_error: std::runtime_error {
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
@ -44,8 +45,8 @@ namespace detail {
|
|||
|
||||
/** @brief Splits command line argument string into individual arguments.
|
||||
*
|
||||
* The split is done in a platform specific manner, using wordexp on POSIX
|
||||
* systems and CommandLineToArgvW on Windows. On Windows, the input string
|
||||
* The split is done in a platform specific manner, using `wordexp` on POSIX
|
||||
* systems and `CommandLineToArgvW` on Windows. On Windows, the input string
|
||||
* is assumed to be UTF-8 and the output strings are always UTF-8, on POSIX
|
||||
* the wordexp implementation is followed (so it's locale specific).
|
||||
*
|
||||
|
@ -76,44 +77,137 @@ OutputRange &&split_args(OutputRange &&out, string_range str) {
|
|||
return std::forward<OutputRange>(out);
|
||||
}
|
||||
|
||||
/** @brief Thrown on errors in ostd::subprocess. */
|
||||
struct process_error: std::system_error {
|
||||
using std::system_error::system_error;
|
||||
};
|
||||
|
||||
/** @brief The mode used for standard streams in ostd::subprocess.
|
||||
*
|
||||
* This way you can turn stdin, stdout or stderr of any subprocess into
|
||||
* a stadnard ostd::file_stream or keep them as they are. You can also
|
||||
* redirect stderr into stdout, if stdout itself is redirected then
|
||||
* stderr will point to the newly redirected stdout.
|
||||
*
|
||||
* Only use the `STDOUT` value for stderr stream.
|
||||
*/
|
||||
enum class process_stream {
|
||||
DEFAULT = 0,
|
||||
PIPE,
|
||||
STDOUT
|
||||
DEFAULT = 0, ///< Do not perform any redirection.
|
||||
PIPE, ///< Capture the stream as an ostd::file_stream.
|
||||
STDOUT ///< Writes to stderr will be written to stdout.
|
||||
};
|
||||
|
||||
/** @brief Implements portable subprocess handling.
|
||||
*
|
||||
* This is a universal API with which you can freely manipulate standard
|
||||
* streams of the child process (therefore manipulate its I/O) as well as
|
||||
* get the return code of the child process. It portably covers the role
|
||||
* of `popen` (but it is also more powerful, as it can be bidirectional)
|
||||
* as well as the `exec` family of functions.
|
||||
*/
|
||||
struct OSTD_EXPORT subprocess {
|
||||
process_stream use_in = process_stream::DEFAULT;
|
||||
/** @brief The standard input redirection mode.
|
||||
*
|
||||
* The value is one of ostd::process_stream. Set this before opening
|
||||
* the subprocess. If it's set to `PIPE`, you will be able to write
|
||||
* into the standard input of the child process using the `in` member,
|
||||
* which is a standard ostd::file_stream. Never set it to `STDOUT`
|
||||
* as that will make process opening throw an error. By default
|
||||
* no redirection is done.
|
||||
*
|
||||
* @see ostd::subprocess::in, ostd::subprocess::use_out,
|
||||
* ostd::subprocess::use_err
|
||||
*/
|
||||
process_stream use_in = process_stream::DEFAULT;
|
||||
|
||||
/** @brief The standard output redirection mode.
|
||||
*
|
||||
* The value is one of ostd::process_stream. Set this before opening
|
||||
* the subprocess. If it's set to `PIPE`, you will be able to read
|
||||
* from the standard output of the child process using the `out` member,
|
||||
* which is a standard ostd::file_stream. Setting this to `STDOUT` has
|
||||
* the same effect as `DEFAULT`.
|
||||
*
|
||||
* @see ostd::subprocess::out, ostd::subprocess::use_in,
|
||||
* ostd::subprocess::use_err
|
||||
*/
|
||||
process_stream use_out = process_stream::DEFAULT;
|
||||
|
||||
/** @brief The standard error redirection mode.
|
||||
*
|
||||
* The value is one of ostd::process_stream. Set this before opening
|
||||
* the subprocess. If it's set to `PIPE`, you will be able to read
|
||||
* from the standard error of the child process using the `err` member,
|
||||
* which is a standard ostd::file_stream. Setting this to `STDOUT`
|
||||
* redirects the child process standard error into its stanard output,
|
||||
* no matter what the redirection mode of the standard output is, so
|
||||
* they will be effectively the same stream - if you redirect the
|
||||
* standard output you will also read standard error using `err`.
|
||||
*
|
||||
* @see ostd::subprocess::err, ostd::subprocess::use_in,
|
||||
* ostd::subprocess::use_out
|
||||
*/
|
||||
process_stream use_err = process_stream::DEFAULT;
|
||||
file_stream in = file_stream{};
|
||||
|
||||
/** @brief The standard input stream when redirected.
|
||||
*
|
||||
* If no redirection is done (see ostd::subprocess::use_in) then
|
||||
* this stream will not be opened.
|
||||
*
|
||||
* @see ostd::subprocess::out, ostd::subprocess::err
|
||||
*/
|
||||
file_stream in = file_stream{};
|
||||
|
||||
/** @brief The standard output stream when redirected.
|
||||
*
|
||||
* If no redirection is done (see ostd::subprocess::use_out) then
|
||||
* this stream will not be opened.
|
||||
*
|
||||
* @see ostd::subprocess::in, ostd::subprocess::err
|
||||
*/
|
||||
file_stream out = file_stream{};
|
||||
|
||||
/** @brief The standard error stream when redirected.
|
||||
*
|
||||
* If no redirection is done (see ostd::subprocess::use_err) then
|
||||
* this stream will not be opened.
|
||||
*
|
||||
* @see ostd::subprocess::in, ostd::subprocess::out
|
||||
*/
|
||||
file_stream err = file_stream{};
|
||||
|
||||
/** @brief Initializes a default subprocess.
|
||||
*
|
||||
* No streams will be set to redirect.
|
||||
*/
|
||||
subprocess() {}
|
||||
|
||||
/** @brief Initializes a subprocess with the given stream redirections.*/
|
||||
subprocess(
|
||||
process_stream in_use, process_stream out_use, process_stream err_use
|
||||
):
|
||||
use_in(in_use), use_out(out_use), use_err(err_use)
|
||||
{}
|
||||
|
||||
subprocess(subprocess &&i):
|
||||
/** @brief Moves the subprocess data. */
|
||||
subprocess(subprocess &&i) noexcept:
|
||||
use_in(i.use_in), use_out(i.use_out), use_err(i.use_err),
|
||||
in(std::move(i.in)), out(std::move(i.out)), err(std::move(i.err))
|
||||
{
|
||||
move_data(i);
|
||||
}
|
||||
|
||||
subprocess &operator=(subprocess &&i) {
|
||||
/** @brief Move assigns the subprocess data.
|
||||
*
|
||||
* Effectively equivalent to swap().
|
||||
*/
|
||||
subprocess &operator=(subprocess &&i) noexcept {
|
||||
swap(i);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(subprocess &i) {
|
||||
/** @brief Swaps the data of two subprocess structures. */
|
||||
void swap(subprocess &i) noexcept {
|
||||
using std::swap;
|
||||
swap(use_in, i.use_in);
|
||||
swap(use_out, i.use_out);
|
||||
|
@ -124,25 +218,86 @@ struct OSTD_EXPORT subprocess {
|
|||
swap_data(i);
|
||||
}
|
||||
|
||||
/** @brief Destroys the subprocess.
|
||||
*
|
||||
* If a child process assigned to this structure currently
|
||||
* exists, it will wait for it to finish first by calling close().
|
||||
*/
|
||||
~subprocess();
|
||||
|
||||
/** @brief Waits for a currently running child process to be done.
|
||||
*
|
||||
* If there isn't any child process assigned to this, it will throw
|
||||
* ostd::process_error. It will also throw the same exception if some
|
||||
* other error has occured. It will not throw if the command has
|
||||
* executed but exited with a non-zero code. This code will be
|
||||
* returned instead.
|
||||
*
|
||||
* @returns The child process return code on success.
|
||||
*
|
||||
* @throws ostd::process_error on failure of any kind.
|
||||
*
|
||||
* @see open_path(), open_command()
|
||||
*/
|
||||
int close();
|
||||
|
||||
/** @brief Opens a subprocess using the given arguments.
|
||||
*
|
||||
* The `path` is an actual absolute or relative path to an executable
|
||||
* file (as in POSIX `execv` or Windows `CreateProcess`) and `args`
|
||||
* is a range of string-like types (any works as long as a string can
|
||||
* be constructed from it). The first element of `args` is `argv[0]`.
|
||||
*
|
||||
* If `path` is empty, the first element of `args` is used.
|
||||
*
|
||||
* If this fails, ostd::process_error will be thrown.
|
||||
*
|
||||
* On success, a new subprocess will be created and this will return
|
||||
* without waiting for it to finish. Use close() to wait and get the
|
||||
* return code.
|
||||
*
|
||||
* @throws ostd::process_error on failure of any kind.
|
||||
*
|
||||
* @see open_command(), close()
|
||||
*/
|
||||
template<typename InputRange>
|
||||
void open_path(string_range path, InputRange &&args) {
|
||||
open_full(path, std::forward<InputRange>(args), false);
|
||||
}
|
||||
|
||||
/** @brief Like open_path() with an empty first argument. */
|
||||
template<typename InputRange>
|
||||
void open_path(InputRange &&args) {
|
||||
open_path(nullptr, std::forward<InputRange>(args));
|
||||
}
|
||||
|
||||
/** @brief Opens a subprocess using the given arguments.
|
||||
*
|
||||
* The `cmd` is a command name looked up in the `PATH` environment
|
||||
* variable when it contains no slash and an ordinary executable
|
||||
* path when it contains one (as in POSIX `execvp` or Windows
|
||||
* `CreateProcess`) and `args` is a range of string-like types
|
||||
* (any works as long as a string can be constructed from it).
|
||||
* The first element of `args` is `argv[0]`.
|
||||
*
|
||||
* If `cmd` is empty, the first element of `args` is used.
|
||||
*
|
||||
* If this fails, ostd::process_error will be thrown.
|
||||
*
|
||||
* On success, a new subprocess will be created and this will return
|
||||
* without waiting for it to finish. Use close() to wait and get the
|
||||
* return code.
|
||||
*
|
||||
* @throws ostd::process_error on failure of any kind.
|
||||
*
|
||||
* @see open_path(), close()
|
||||
*/
|
||||
template<typename InputRange>
|
||||
void open_command(string_range cmd, InputRange &&args) {
|
||||
open_full(cmd, std::forward<InputRange>(args), true);
|
||||
}
|
||||
|
||||
/** @brief Like open_command() with an empty first argument. */
|
||||
template<typename InputRange>
|
||||
void open_command(InputRange &&args) {
|
||||
open_command(nullptr, std::forward<InputRange>(args));
|
||||
|
|
Loading…
Reference in a new issue