updated doc for delegate/sigh (close #267)
This commit is contained in:
1
TODO
1
TODO
@@ -28,4 +28,3 @@
|
||||
|
||||
TODO
|
||||
* registry::sort and registry::respect also for types that are part of a group (untracked items only)
|
||||
* update documentation for delegate and sigh
|
||||
|
||||
@@ -417,6 +417,12 @@ should be kept to a minimum, if possible. Note also that the greater the number
|
||||
of listeners, the greater the performance hit when components are created or
|
||||
destroyed.
|
||||
|
||||
Please, refer to the documentation of the signal class to know all the features
|
||||
it offers.<br/>
|
||||
There are many useful but less known functionalities that aren't described here,
|
||||
such as the connection objects or the possibility to attach listeners with a
|
||||
list of parameters that is shorter than that of the signal itself.
|
||||
|
||||
### They call me Reactive System
|
||||
|
||||
As mentioned above, signals are the basic tools to construct reactive systems,
|
||||
|
||||
@@ -28,9 +28,9 @@ allocations under the hood and this could be problematic sometimes. Furthermore,
|
||||
it solves a problem but may not adapt well to other requirements that may arise
|
||||
from time to time.
|
||||
|
||||
In case that the flexibility and potential of an `std::function` are not
|
||||
required or where you are looking for something different, `EnTT` offers a full
|
||||
set of classes to solve completely different problems.
|
||||
In case that the flexibility and power of an `std::function` isn't required or
|
||||
if the price to pay for them is too high,` EnTT` offers a complete set of
|
||||
lightweight classes to solve the same and many other problems.
|
||||
|
||||
# Delegate
|
||||
|
||||
@@ -78,9 +78,9 @@ The delegate class accepts also data members, if needed. In this case, the
|
||||
function type of the delegate is such that the parameter list is empty and the
|
||||
value of the data member is at least convertible to the return type.
|
||||
|
||||
Functions having type equivalent to `void(T *, args...)` are accepted as well.
|
||||
In this case, `T *` is considered a payload and the function will receive it
|
||||
back every time it's invoked. In other terms, this works just fine with the
|
||||
Free functions having type equivalent to `void(T *, args...)` are accepted as
|
||||
well. In this case, `T *` is considered a payload and the function will receive
|
||||
it back every time it's invoked. In other terms, this works just fine with the
|
||||
above definition:
|
||||
|
||||
```cpp
|
||||
@@ -98,8 +98,7 @@ of its function call operator.
|
||||
Another interesting aspect of the delegate class is that it accepts also
|
||||
functions with a list of parameters that is shorter than that of the function
|
||||
type used to specialize the delegate itself.<br/>
|
||||
This is a nice-to-have feature in a lot of cases. The following code is
|
||||
therefore perfectly valid:
|
||||
The following code is therefore perfectly valid:
|
||||
|
||||
```cpp
|
||||
void g() { /* ... */ }
|
||||
@@ -108,7 +107,9 @@ delegate(42);
|
||||
```
|
||||
|
||||
Where the function type of the delegate is `void(int)` as above. It goes without
|
||||
saying that the extra arguments are silently discarded internally.
|
||||
saying that the extra arguments are silently discarded internally.<br/>v
|
||||
This is a nice-to-have feature in a lot of cases, as an example when the
|
||||
`delegate` class is used as a building block of a signal-slot system.
|
||||
|
||||
To create and initialize a delegate at once, there are a few specialized
|
||||
constructors. Because of the rules of the language, the listener is provided by
|
||||
@@ -144,37 +145,34 @@ fine.
|
||||
# Signals
|
||||
|
||||
Signal handlers work with naked pointers, function pointers and pointers to
|
||||
member. Listeners can be any kind of objects and users are in charge of
|
||||
members. Listeners can be any kind of objects and users are in charge of
|
||||
connecting and disconnecting them from a signal to avoid crashes due to
|
||||
different lifetimes. On the other side, performance shouldn't be affected that
|
||||
much by the presence of such a signal handler.<br/>
|
||||
Signals make use of delegates internally and therefore they undergo the same
|
||||
rules and offer similar functionalities. It may be a good idea to consult the
|
||||
documentation of the `delegate` class for further information.
|
||||
|
||||
A signal handler can be used as a private data member without exposing any
|
||||
_publish_ functionality to the clients of a class. The basic idea is to impose a
|
||||
clear separation between the signal itself and its _sink_ class, that is a tool
|
||||
clear separation between the signal itself and the `sink` class, that is a tool
|
||||
to be used to connect and disconnect listeners on the fly.
|
||||
|
||||
The API of a signal handler is straightforward. The most important thing is that
|
||||
it comes in two forms: with and without a collector. In case a signal is
|
||||
associated with a collector, all the values returned by the listeners can be
|
||||
provided with a collector, all the values returned by the listeners can be
|
||||
literally _collected_ and used later by the caller. Otherwise it works just like
|
||||
a plain signal that emits events from time to time.<br/>
|
||||
|
||||
**Note**: collectors are allowed only in case of function types whose the return
|
||||
type isn't `void` for obvious reasons.
|
||||
|
||||
To create instances of signal handlers there exist mainly two ways:
|
||||
To create instances of signal handlers it is sufficient to provide the type of
|
||||
function to which they refer:
|
||||
|
||||
```cpp
|
||||
// no collector type
|
||||
entt::sigh<void(int, char)> signal;
|
||||
|
||||
// explicit collector type
|
||||
entt::sigh<void(int, char), my_collector<bool>> collector;
|
||||
```
|
||||
|
||||
As expected, they offer all the basic functionalities required to know how many
|
||||
listeners they contain (`size`) or if they contain at least a listener (`empty`)
|
||||
and even to swap two signal handlers (`swap`).
|
||||
Signals offer all the basic functionalities required to know how many listeners
|
||||
they contain (`size`) or if they contain at least a listener (`empty`), as well
|
||||
as a function to use to swap handlers (`swap`).
|
||||
|
||||
Besides them, there are member functions to use both to connect and disconnect
|
||||
listeners in all their forms by means of a sink:
|
||||
@@ -188,29 +186,35 @@ struct listener {
|
||||
|
||||
// ...
|
||||
|
||||
entt::sink sink{signal};
|
||||
listener instance;
|
||||
|
||||
signal.sink().connect<&foo>();
|
||||
signal.sink().connect<&listener::bar>(&instance);
|
||||
sink.connect<&foo>();
|
||||
sink.connect<&listener::bar>(&instance);
|
||||
|
||||
// ...
|
||||
|
||||
// disconnects a free function
|
||||
signal.sink().disconnect<&foo>();
|
||||
sink.disconnect<&foo>();
|
||||
|
||||
// disconnect a member function of an instance
|
||||
signal.sink().disconnect<&listener::bar>(&instance);
|
||||
sink.disconnect<&listener::bar>(&instance);
|
||||
|
||||
// disconnect all the member functions of an instance, if any
|
||||
signal.sink().disconnect(&instance);
|
||||
sink.disconnect(&instance);
|
||||
|
||||
// discards all the listeners at once
|
||||
signal.sink().disconnect();
|
||||
sink.disconnect();
|
||||
```
|
||||
|
||||
As shown above, the listeners don't have to strictly follow the signature of the
|
||||
signal. As long as a listener can be invoked with the given arguments to yield a
|
||||
result that is convertible to the given result type, everything works just fine.
|
||||
result that is convertible to the given return type, everything works just
|
||||
fine.<br/>
|
||||
The `connect` member function returns by default a `connection` object to be
|
||||
used as an alternative to break a connection by means of its `release` member
|
||||
function. A `scoped_connection` can also be created from a connection. In this
|
||||
case, the link is broken automatically as soon as the object goes out of scope.
|
||||
|
||||
Once listeners are attached (or even if there are no listeners at all), events
|
||||
and data in general can be published through a signal by means of the `publish`
|
||||
@@ -239,20 +243,23 @@ int g() { return 1; }
|
||||
// ...
|
||||
|
||||
entt::sigh<int(), my_collector<int>> signal;
|
||||
entt::sink sink{sigh};
|
||||
|
||||
signal.sink().connect<&f>();
|
||||
signal.sink().connect<&g>();
|
||||
sink.connect<&f>();
|
||||
sink.connect<&g>();
|
||||
|
||||
my_collector collector = signal.collect();
|
||||
std::vector<int> vec{};
|
||||
my_collector collector = signal.collect([&vec](int value) { vec.push_back(value); });
|
||||
|
||||
assert(collector.vec[0] == 0);
|
||||
assert(collector.vec[1] == 1);
|
||||
```
|
||||
|
||||
A collector must expose a function operator that accepts as an argument a type
|
||||
to which the return type of the listeners can be converted. Moreover, it has to
|
||||
return a boolean value that is false to stop collecting data, true otherwise.
|
||||
This way one can avoid calling all the listeners in case it isn't necessary.
|
||||
to which the return type of the listeners can be converted. Moreover, it can
|
||||
optionally return a boolean value that is true to stop collecting data, false
|
||||
otherwise. This way one can avoid calling all the listeners in case it isn't
|
||||
necessary.
|
||||
|
||||
# Event dispatcher
|
||||
|
||||
@@ -278,8 +285,7 @@ the `connect` member function of the sink in charge for the specific event:
|
||||
struct an_event { int value; };
|
||||
struct another_event {};
|
||||
|
||||
struct listener
|
||||
{
|
||||
struct listener {
|
||||
void receive(const an_event &) { /* ... */ }
|
||||
void method(const another_event &) { /* ... */ }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user