The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

XS::Framework::CallbackDispatcher - callback dispatcher for C++ events

SYNOPSIS

    my $dispatcher = ...;
    # add simple callbacks
    $dispatcher->add(sub {
        my @args = @_;
        ...
    });
    $dispatcher->prepend(sub {
        my @args = @_;
        ...
    });

    $dispatcher->add_event_listener(sub {
        my ($event, @args) = @_;
        ...
        return $event->(@args) + 10;
    });
    $dispatcher->prepend_event_listener(sub { ... });

    # remove callbacks
    $dispatcher->remove($cb);
    $dispatcher->remove_all();

    say "I have no listeners" if !$dispatcher->has_listeners();
    $dispatcher->call('Hi, there!');

DESCRIPTION

The idea is pretty simple, there is a event source, and possible multiple event listeners. When an event occur it is delivered to the listeners (aka callbacks). Schematically it is something like:

    my $event_source = ...; # construct it somehow
    $event_source->add(sub { my $arg = shift; say "Hey, I have ", $arg; });
    $event_source->add(sub { my $arg = shift; say "Me too have ", $arg; });
    ...
    $event_source->generate_event('boom!'); # somewhere in C++ code

In the addition to delivering event to a callback, the callback might optionally return a value, if the event source asked for that.

In the C++ world objects are strictly typed, hence the type of event as well as the type of the optional result must be declared. Something like:

    CallbackDispatcher<void(string)>;

Here the void is the return type (i.e. nothing is expected), and the event type is string.

By the reasons, described above, the pure perl event source aka CallbackDispatcher cannot be instantiated, it is always typed and instantiated in C++. If you need pure perl implementation without C++ backend, you can easily create your own or search in CPAN.

When an event occur, callbacks are invoked in the order they were added.

SIMPLE and NON-SIMPLE HANDLERS

Lets assume there is a C++ interface like:

    CallbackDispatcher<bool(string)>;

i.e. string is delivered to the callback and a bool value in the return is expected. How would it manage that, if there are multiple callbacks? The answer is: all returned results are ignored, if simple callback interface is used, like:

    $event_source->add(sub { ... });
    $event_source->add(sub { ... });

To deal with result, a little bit more complex result the complex callback interface should be used. It takes the special event object and all parameters taken during event generation, i.e.:

    $event_source->add_event_listener(sub {
        my ($event, @args) = @_;
        return 1 if ($some_condition);
        return $event->(@args);
    });

If some condition is met, then result is returned and no further callbacks are polled. Otherwise, args are forwarded further to the next complex event listener and the result is taken from it, and so on, recursively. The arguments can be modified/changed/substituted on demand, of course.

METHODS

add($code)

Appends list of callbacks by additing a $code into the back.

The return result of the $code will be ignored, as it is considered simple callback interface. The arguments used on event creation are forwarded to the simple callback(s).

add_weak($ref, $code)

Helper method for capturing data by weak reference. Behaviour is the same as for add(). $ref is any object or reference and it must be captured by the closure. This method changes subroutine in a way that it captures the specified $ref by a weak reference instead of strong reference. Additionally, if $ref becomes undef will no longer call the callback (other callbacks still may be called).

So that this code:

    Scalar::Util::weaken(my $weak_obj = $obj);
    $dispatcher->add(sub {
        return unless $weak_obj;
        ...use $weak_obj
    });

can be simplified to:

    $dispatcher->add_weak($obj, sub {
        ...use $obj
    });

This is useful if you want to avoid memory leaks caused by capturing an object which contains dispacher directly or indirectly (cyclic reference).

    my $obj = ...;
    $obj->some_event_dispatcher->add_weak($obj, sub {
        # use $obj
    });

prepend($code)

Same as add() but adds callback to the front (will be called first).

add_event_listener($code);

Appends list of callbacks by additing a $code into the back.

The return result of the $code will be converted into the required C++ type by calling xs::in(). This is considered complex callback interface, so, the $code is invoked with the special event object and arguments, i..e

    $code->($event, @args);

The $event is an anonynous subroutine, which allows to poll further callbacks. It should be noted, that $event SHOULD NOT outlive callback dispatcher instance, as it leads to core dump.

prepend_event_listener($code)

Same as add_event_listener() but adds callback to the front (will be called first).

remove($code)

Removes the $code from the list of callbacks.

remove_all()

Clears list of callbacks.

has_listeners()

Returns true if there is any callback listeners.

call([$arg1 [, $arg2 ...]]);

Generates the C++ event, converting arguments into proper C++ type via xs::in and passing it to callbacks.