1[def __on_exit__ [globalref boost::process::on_exit on_exit]] 2[def __on_success__ [globalref boost::process::extend::on_success ex::on_success]] 3[def __child__ [classref boost::process::child child]] 4[def __handler__ [classref boost::process::extend::handler handler]] 5[def __on_success__ [memberref boost::process::extend::handler::on_success on_success]] 6[def __posix_executor__ [classref boost::process::extend::posix_executor ex::posix_executor]] 7[def __windows_executor__ [classref boost::process::extend::windows_executor ex::windows_executor]] 8[def __io_context__ [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/io_context.html boost::asio::io_context]] 9[def __require_io_context__ [classref boost::process::extend::require_io_context ex::require_io_context]] 10[def __async_handler__ [classref boost::process::extend::async_handler ex::async_handler]] 11[def __get_io_context__ [funcref boost::process::extend::get_io_context ex::get_io_context]] 12 13[section:extend Extensions] 14To extend the library, the header [headerref boost/process/extend.hpp extend] is provided. 15 16It only provides the explicit style for custom properties, but no implicit style. 17 18What this means is, that a custom initializer can be implemented, a reference which can be passed to one of the launching functions. 19If a class inherits [classref boost::process::extend::handler] it will be regarded as an initializer and thus directly put into the sequence 20the executor gets passed. 21 22[section:structure Structure] 23 24The executor calls different handlers of the initializers during the process launch. 25The basic structure consists of three functions, as given below: 26 27* [globalref boost::process::extend::on_setup on_setup] 28* [globalref boost::process::extend::on_error on_error] 29* [globalref boost::process::extend::on_success on_success] 30 31''' 32<imagedata fileref="boost_process/windows_exec.svg"/> 33''' 34 35Additionally posix provides three more handlers, listed below: 36 37* [globalref boost::process::extend::on_fork_error on_fork_error] 38* [globalref boost::process::extend::on_exec_setup on_exec_setup] 39* [globalref boost::process::extend::on_exec_error on_exec_error] 40 41For more information see the reference of [classref boost::process::extend::posix_executor posix_executor]. 42 43[endsect] 44[section:simple Simple extensions] 45 46The simplest extension just takes a single handler, which can be done in a functional style. 47So let's start with a simple hello-world example, while we use a C++14 generic lambda. 48 49``` 50using namespace boost::process; 51namespace ex = bp::extend; 52 53__child__ c("foo", __on_success__=[](auto & exec) {std::cout << "hello world" << std::endl;}); 54``` 55 56Considering that lambdas can also capture values, data can easily be shared between handlers. 57 58To see which members the executor has, refer to [classref boost::process::extend::windows_executor windows_executor] 59and [classref boost::process::extend::posix_executor posix_executor]. 60 61[note Combined with __on_exit__ this can also handle the process exit.] 62 63[caution The posix handler symbols are not defined on windows.] 64 65[endsect] 66 67[section:handler Handler Types] 68 69Since the previous example is in a functional style, it is not very reusable. 70To solve that problem, the [classref boost::process::extend::handler handler] has an alias in the `boost::process::extend` namespace, to be inherited. 71So let's implement the hello world example in a class. 72 73``` 74struct hello_world : __handler__ 75{ 76 template<typename Executor> 77 void __on_success__(Executor & exec) const 78 { 79 std::cout << "hello world" << std::endl; 80 } 81}; 82 83//in our function 84__child__ c("foo", hello_world()); 85``` 86 87[note The implementation is done via overloading, not overriding.] 88 89Every handler not implemented defaults to [classref boost::process::extend::handler handler], where an empty handler is defined for each event. 90 91[endsect] 92 93 94 95[section:async Asynchronous Functionality] 96 97Since `boost.process` provides an interface for [@http://www.boost.org/doc/libs/release/libs/asio/ boost.asio], 98this functionality is also available for extensions. If the class needs the __io_context__ for some reason, the following code will do that. 99 100``` 101struct async_foo : __handler__, __require_io_context__ 102{ 103 template<typename Executor> 104 void on_setup(Executor & exec) 105 { 106 __io_context__ & ios = __get_io_context__(exec.seq); //gives us a reference and a compiler error if not present. 107 //do something with ios 108 } 109}; 110``` 111[note Inheriting [globalref boost::process::extend::require_io_context require_io_context] is necessary, so [funcref boost::process::system system] provides one.] 112 113Additionally the handler can provide a function that is invoked when the child process exits. This is done through __async_handler__. 114 115[note [globalref boost::process::extend::async_handler async_handler] implies [globalref boost::process::extend::require_io_context require_io_context] .] 116 117``` 118struct async_bar : __handler, __async_handler__ 119{ 120 template<typename Executor> 121 std::function<void(int, const std::error_code&)> on_exit_handler(Executor & exec) 122 { 123 auto handler_ = this->handler; 124 return [handler_](int exit_code, const std::error_code & ec) 125 { 126 std::cout << "hello world, I exited with " << exit_code << std::endl; 127 }; 128 129 } 130}; 131``` 132 133 134[caution `on_exit_handler` does not default and is always required when [classref boost::process::extend::async_handler async_handler] is inherited. ] 135 136[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). ] 137 138[endsect] 139 140[section:error Error handling] 141 142If an error occurs in the initializers it shall be told to the executor and not handled directly. This is because 143the behaviour can be changed through arguments passed to the launching function. Hence the executor 144has the function `set_error`, which takes an [@http://en.cppreference.com/w/cpp/error/error_code std::error_code] and a string. 145Depending on the configuration of the executor, this may either throw, set an internal `error_code`, or do nothing. 146 147So let's take a simple example, where we set a randomly chosen `error_code`. 148 149``` 150auto set_error = [](auto & exec) 151 { 152 std::error_code ec{42, std::system_category()}; 153 exec.set_error(ec, "a fake error"); 154 155 }; 156__child__ c("foo", on_setup=set_error); 157``` 158 159Since we do not specify the error-handling mode in this example, this will throw [classref boost::process::process_error process_error]. 160 161[endsect] 162 163[section:exec_over Executor Overloading] 164 165Now that we have a custom initializer, let's consider how we can handle differences between different executors. 166The distinction is between posix and windows and `char` and `wchar_t` on windows. 167One 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, 168which are automatically available as soon as any process-header is included. 169 170Another variant are the type aliases __posix_executor__ and __windows_executor__, where the executor, not on the current system is a forward-declaration. 171This works fine, because the function will never get invoked. So let's implement another example, which prints the executable name __on_success__. 172 173``` 174struct hello_exe : __handler__ 175{ 176 template<typename Sequence> 177 void __on_success__(__posix_executor__<Sequence> & exec) 178 { 179 std::cout << "posix-exe: " << exec.exe << std::endl; 180 } 181 182 template<typename Sequence> 183 void __on_success__(__windows_executor__<char, Sequence> & exec) 184 { 185 //note: exe might be a nullptr on windows. 186 if (exec.exe != nullptr) 187 std::cout << "windows-exe: " << exec.exe << std::endl; 188 else 189 std::cout << "windows didn't use exe" << std::endl; 190 } 191 192 template<typename Sequence> 193 void __on_success__(__windows_executor__<wchar_t, Sequence> & exec) 194 { 195 //note: exe might be a nullptr on windows. 196 if (exec.exe != nullptr) 197 std::wcout << L"windows-exe: " << exec.exe << std::endl; 198 else 199 std::cout << "windows didn't use exe" << std::endl; 200 } 201 202}; 203``` 204 205So 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 206way to implement system-specific code without using the preprocessor. 207 208[note If you only write a partial implementation, e.g. only for __posix_executor__, the other variants will default to __handler__]. 209 210[endsect] 211 212[endsect] 213