| Home | Libraries | People | FAQ | More

## Extensions

To extend the library, the header `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 `boost::process::extend::handler` it will be regarded as a initializer and thus directly put into the sequence the executor gets passed.

### Structure

The executor calls different handlers of the initializers during the process launch. The basic structure is consists of three functions, as given below:

- `on_setup`
- `on_error`
- `on_success`

Additionally posix provides three more handlers, listed below:

- `on_fork_error`
- `on_exec_setup`
- `on_exec_error`

For more information see the reference of `posix_executor`.

### 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", ex::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 `windows_executor` and `posix_executor`.

| Note
Combined with `on_exit` this can also handle the process exit.

| Caution
The posix handler symbols are not defined on windows.

### Handler Types

Since the previous example is in a functional style, it is not very reusable. To solve that problem, the `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 ex::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 `handler`, where an empty handler is defined for each event.

### Asynchronous Functionality

Since `boost.process` provides an interface for boost.asio, this functionality is also available for extensions. If the class needs the boost::asio::io_context for some reason, the following code will do that.

    struct async_foo : handler, ex::require_io_context
    {
        tempalte<typename Executor>
        void on_setup(Executor & exec)
        {
            boost::asio::io_context & ios = ex::get_io_context(exec.seq); //gives us a reference and a compiler error if not present.
            //do something with ios
        }
    };

| Note
Inheriting `require_io_context` is necessary, so `system` provides one.

Additionally the handler can provide a function that is invoked when the child process exits. This is done through `ex::async_handler`.

| Note
`async_handler` implies `require_io_context` .

    struct async_bar : __handler, ex::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 `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).

### 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 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 `process_error`.

### 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 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 `ex::posix_executor` and `ex::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 `ex::on_success`.

    struct hello_exe : handler
    {
        template<typename Sequence>
        void ex::on_success(ex::posix_executor<Sequence> & exec)
        {
            std::cout << "posix-exe: " << exec.exe << std::endl;
        }

        template<typename Sequence>
        void ex::on_success(ex::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 ex::on_success(ex::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 `ex::posix_executor`, the other variants will default to `handler`

.