123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- [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 to which can be passed to one of the launching functions.
- If a class inherits [classref boost::process::extend::handler] it will be regarded as a 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 is 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 lambda 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 dafaults 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__
- {
- tempalte<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 handles directly. This is because
- the behaviour can be changed through arguments passed to the launching function. Hence the 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 cofiguration 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 exectur are still a template so that they will not be evaluated if not used. Hence this provides a
- way to implement systems-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]
|