| [def __on_exit__ [globalref boost::process::on_exit on_exit]] |
| [def __on_success__ [globalref boost::process::extend::on_success ex::on_success]] |
| [def __child__ [classref boost::process::child child]] |
| [def __handler__ [classref boost::process::extend::handler handler]] |
| [def __on_success__ [memberref boost::process::extend::handler::on_success on_success]] |
| [def __posix_executor__ [classref boost::process::extend::posix_executor ex::posix_executor]] |
| [def __windows_executor__ [classref boost::process::extend::windows_executor ex::windows_executor]] |
| [def __io_context__ [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_context.html boost::asio::io_context]] |
| [def __require_io_context__ [classref boost::process::extend::require_io_context ex::require_io_context]] |
| [def __async_handler__ [classref boost::process::extend::async_handler ex::async_handler]] |
| [def __get_io_context__ [funcref boost::process::extend::get_io_context ex::get_io_context]] |
| |
| [section:extend Extensions] |
| To extend the library, the header [headerref boost/process/extend.hpp extend] is provided. |
| |
| It only provides the explicit style for custom properties, but no implicit style. |
| |
| What this means is, that a custom initializer can be implemented, a reference which can be passed to one of the launching functions. |
| If a class inherits [classref boost::process::extend::handler] it will be regarded as an initializer and thus directly put into the sequence |
| the executor gets passed. |
| |
| [section:structure Structure] |
| |
| The executor calls different handlers of the initializers during the process launch. |
| The basic structure consists of three functions, as given below: |
| |
| * [globalref boost::process::extend::on_setup on_setup] |
| * [globalref boost::process::extend::on_error on_error] |
| * [globalref boost::process::extend::on_success on_success] |
| |
| ''' |
| <imagedata fileref="boost_process/windows_exec.svg"/> |
| ''' |
| |
| Additionally posix provides three more handlers, listed below: |
| |
| * [globalref boost::process::extend::on_fork_error on_fork_error] |
| * [globalref boost::process::extend::on_exec_setup on_exec_setup] |
| * [globalref boost::process::extend::on_exec_error on_exec_error] |
| |
| For more information see the reference of [classref boost::process::extend::posix_executor posix_executor]. |
| |
| [endsect] |
| [section:simple Simple extensions] |
| |
| The simplest extension just takes a single handler, which can be done in a functional style. |
| So let's start with a simple hello-world example, while we use a C++14 generic lambda. |
| |
| ``` |
| using namespace boost::process; |
| namespace ex = bp::extend; |
| |
| __child__ c("foo", __on_success__=[](auto & exec) {std::cout << "hello world" << std::endl;}); |
| ``` |
| |
| Considering that lambdas can also capture values, data can easily be shared between handlers. |
| |
| To see which members the executor has, refer to [classref boost::process::extend::windows_executor windows_executor] |
| and [classref boost::process::extend::posix_executor posix_executor]. |
| |
| [note Combined with __on_exit__ this can also handle the process exit.] |
| |
| [caution The posix handler symbols are not defined on windows.] |
| |
| [endsect] |
| |
| [section:handler Handler Types] |
| |
| Since the previous example is in a functional style, it is not very reusable. |
| To solve that problem, the [classref boost::process::extend::handler handler] has an alias in the `boost::process::extend` namespace, to be inherited. |
| So let's implement the hello world example in a class. |
| |
| ``` |
| struct hello_world : __handler__ |
| { |
| template<typename Executor> |
| void __on_success__(Executor & exec) const |
| { |
| std::cout << "hello world" << std::endl; |
| } |
| }; |
| |
| //in our function |
| __child__ c("foo", hello_world()); |
| ``` |
| |
| [note The implementation is done via overloading, not overriding.] |
| |
| Every handler not implemented defaults to [classref boost::process::extend::handler handler], where an empty handler is defined for each event. |
| |
| [endsect] |
| |
| |
| |
| [section:async Asynchronous Functionality] |
| |
| Since `boost.process` provides an interface for [@http://www.boost.org/doc/libs/release/libs/asio/ boost.asio], |
| this functionality is also available for extensions. If the class needs the __io_context__ for some reason, the following code will do that. |
| |
| ``` |
| struct async_foo : __handler__, __require_io_context__ |
| { |
| template<typename Executor> |
| void on_setup(Executor & exec) |
| { |
| __io_context__ & ios = __get_io_context__(exec.seq); //gives us a reference and a compiler error if not present. |
| //do something with ios |
| } |
| }; |
| ``` |
| [note Inheriting [globalref boost::process::extend::require_io_context require_io_context] is necessary, so [funcref boost::process::system system] provides one.] |
| |
| Additionally the handler can provide a function that is invoked when the child process exits. This is done through __async_handler__. |
| |
| [note [globalref boost::process::extend::async_handler async_handler] implies [globalref boost::process::extend::require_io_context require_io_context] .] |
| |
| ``` |
| struct async_bar : __handler, __async_handler__ |
| { |
| template<typename Executor> |
| std::function<void(int, const std::error_code&)> on_exit_handler(Executor & exec) |
| { |
| auto handler_ = this->handler; |
| return [handler_](int exit_code, const std::error_code & ec) |
| { |
| std::cout << "hello world, I exited with " << exit_code << std::endl; |
| }; |
| |
| } |
| }; |
| ``` |
| |
| |
| [caution `on_exit_handler` does not default and is always required when [classref boost::process::extend::async_handler async_handler] is inherited. ] |
| |
| [caution `on_exit_handler` uses `boost::asio::signal_set` to listen for SIGCHLD on posix. The application must not also register a signal handler for SIGCHLD using functions such as `signal()` or `sigaction()` (but using `boost::asio::signal_set` is fine). ] |
| |
| [endsect] |
| |
| [section:error Error handling] |
| |
| If an error occurs in the initializers it shall be told to the executor and not handled directly. This is because |
| the behaviour can be changed through arguments passed to the launching function. Hence the executor |
| has the function `set_error`, which takes an [@http://en.cppreference.com/w/cpp/error/error_code std::error_code] and a string. |
| Depending on the configuration of the executor, this may either throw, set an internal `error_code`, or do nothing. |
| |
| So let's take a simple example, where we set a randomly chosen `error_code`. |
| |
| ``` |
| auto set_error = [](auto & exec) |
| { |
| std::error_code ec{42, std::system_category()}; |
| exec.set_error(ec, "a fake error"); |
| |
| }; |
| __child__ c("foo", on_setup=set_error); |
| ``` |
| |
| Since we do not specify the error-handling mode in this example, this will throw [classref boost::process::process_error process_error]. |
| |
| [endsect] |
| |
| [section:exec_over Executor Overloading] |
| |
| Now that we have a custom initializer, let's consider how we can handle differences between different executors. |
| The distinction is between posix and windows and `char` and `wchar_t` on windows. |
| One solution is to use the [@http://www.boost.org/doc/libs/master/boost/system/api_config.hpp BOOST_WINDOWS_API and BOOST_POSIX_API] macros, |
| which are automatically available as soon as any process-header is included. |
| |
| Another variant are the type aliases __posix_executor__ and __windows_executor__, where the executor, not on the current system is a forward-declaration. |
| This works fine, because the function will never get invoked. So let's implement another example, which prints the executable name __on_success__. |
| |
| ``` |
| struct hello_exe : __handler__ |
| { |
| template<typename Sequence> |
| void __on_success__(__posix_executor__<Sequence> & exec) |
| { |
| std::cout << "posix-exe: " << exec.exe << std::endl; |
| } |
| |
| template<typename Sequence> |
| void __on_success__(__windows_executor__<char, Sequence> & exec) |
| { |
| //note: exe might be a nullptr on windows. |
| if (exec.exe != nullptr) |
| std::cout << "windows-exe: " << exec.exe << std::endl; |
| else |
| std::cout << "windows didn't use exe" << std::endl; |
| } |
| |
| template<typename Sequence> |
| void __on_success__(__windows_executor__<wchar_t, Sequence> & exec) |
| { |
| //note: exe might be a nullptr on windows. |
| if (exec.exe != nullptr) |
| std::wcout << L"windows-exe: " << exec.exe << std::endl; |
| else |
| std::cout << "windows didn't use exe" << std::endl; |
| } |
| |
| }; |
| ``` |
| |
| So given our example, the definitions with the non-native executor are still a template so that they will not be evaluated if not used. Hence this provides a |
| way to implement system-specific code without using the preprocessor. |
| |
| [note If you only write a partial implementation, e.g. only for __posix_executor__, the other variants will default to __handler__]. |
| |
| [endsect] |
| |
| [endsect] |