signals meet references (close #289)

This commit is contained in:
Michele Caini
2019-08-18 15:21:29 +02:00
parent 97dd928ca4
commit 1c7d9e3d75
13 changed files with 173 additions and 179 deletions

1
TODO
View File

@@ -18,6 +18,7 @@
* built-in support for dual (or N-) buffering
* allow to "merge" registries easily
* allow for custom stomp functions
* deprecate/replace snapshot
* remove prototype
TODO

View File

@@ -348,13 +348,13 @@ notified on the creation of a component, use the `on_construct` member function:
registry.on_construct<position>().connect<&my_free_function>();
// connects a member function
registry.on_construct<position>().connect<&my_class::member>(&instance);
registry.on_construct<position>().connect<&my_class::member>(instance);
// disconnects a free function
registry.on_construct<position>().disconnect<&my_free_function>();
// disconnects a member function
registry.on_construct<position>().disconnect<&my_class::member>(&instance);
registry.on_construct<position>().disconnect<&my_class::member>(instance);
```
To be notified when components are destroyed, use the `on_destroy` member
@@ -656,9 +656,8 @@ brief, yet incomplete list of some of them:
entity.<br/>
Put aside the fact that having the prototypes separated from the simulation is
useful in many cases, they make also the codebase easier to maintain, since
updating a template is much less error prone than jumping in the code to
update all the snippets copied and pasted around to initialize entities and
components.
updating a template is less error prone than jumping in the code to update all
the snippets copied and pasted around to initialize entities and components.
* Literally _move_ entities from one registry to another (that is a copy
followed by a destroy), which can be useful for solving problems such as the

View File

@@ -71,27 +71,27 @@ delegate.connect<&f>();
// bind a member function to the delegate
my_struct instance;
delegate.connect<&my_struct::f>(&instance);
delegate.connect<&my_struct::f>(instance);
```
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.
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
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
void g(const char *c, int i) { /* ... */ }
void g(const char &c, int i) { /* ... */ }
const char c = 'c';
delegate.connect<&g>(&c);
delegate.connect<&g>(c);
delegate(42);
```
The function `g` will be invoked with a pointer to `c` and `42`. However, the
The function `g` will be invoked with a reference to `c` and `42`. However, the
function type of the delegate is still `void(int)`. This is also the signature
of its function call operator.
@@ -144,8 +144,8 @@ fine.
# Signals
Signal handlers work with naked pointers, function pointers and pointers to
members. Listeners can be any kind of objects and users are in charge of
Signal handlers work with references to classes, function pointers and pointers
to 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/>
@@ -190,7 +190,7 @@ entt::sink sink{signal};
listener instance;
sink.connect<&foo>();
sink.connect<&listener::bar>(&instance);
sink.connect<&listener::bar>(instance);
// ...
@@ -198,10 +198,10 @@ sink.connect<&listener::bar>(&instance);
sink.disconnect<&foo>();
// disconnect a member function of an instance
sink.disconnect<&listener::bar>(&instance);
sink.disconnect<&listener::bar>(instance);
// disconnect all the member functions of an instance, if any
sink.disconnect(&instance);
sink.disconnect(instance);
// discards all the listeners at once
sink.disconnect();
@@ -280,7 +280,7 @@ This class shares part of its API with the one of the signal handler, but it
doesn't require that all the types of events are specified when declared:
```cpp
// define a general purpose dispatcher that works with naked pointers
// define a general purpose dispatcher
entt::dispatcher dispatcher{};
```
@@ -303,16 +303,16 @@ struct listener {
// ...
listener listener;
dispatcher.sink<an_event>().connect<&listener::receive>(&listener);
dispatcher.sink<another_event>().connect<&listener::method>(&listener);
dispatcher.sink<an_event>().connect<&listener::receive>(listener);
dispatcher.sink<another_event>().connect<&listener::method>(listener);
```
The `disconnect` member function follows the same pattern and can be used to
remove one listener at a time or all of them at once:
```cpp
dispatcher.sink<an_event>().disconnect<&listener::receive>(&listener);
dispatcher.sink<another_event>().disconnect(&listener);
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
dispatcher.sink<another_event>().disconnect(listener);
```
The `trigger` member function serves the purpose of sending an immediate event

View File

@@ -173,72 +173,72 @@ class basic_observer {
template<typename... Reject, typename... Require, typename AnyOf>
struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, AnyOf>> {
template<std::size_t Index>
static void maybe_valid_if(basic_observer *obs, const basic_registry<Entity> &reg, const Entity entt) {
static void maybe_valid_if(basic_observer &obs, const basic_registry<Entity> &reg, const Entity entt) {
if(reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...)) {
auto *comp = obs->view.try_get(entt);
(comp ? *comp : obs->view.construct(entt)) |= (1 << Index);
auto *comp = obs.view.try_get(entt);
(comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
}
}
template<std::size_t Index>
static void discard_if(basic_observer *obs, const basic_registry<Entity> &, const Entity entt) {
if(auto *value = obs->view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
obs->view.destroy(entt);
static void discard_if(basic_observer &obs, const basic_registry<Entity> &, const Entity entt) {
if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
obs.view.destroy(entt);
}
}
template<std::size_t Index>
static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(&obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(&obs), ...);
reg.template on_replace<AnyOf>().template connect<&maybe_valid_if<Index>>(&obs);
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(&obs);
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
reg.template on_replace<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
}
static void disconnect(basic_observer &obs, basic_registry<Entity> &reg) {
(reg.template on_destroy<Require>().disconnect(&obs), ...);
(reg.template on_construct<Reject>().disconnect(&obs), ...);
reg.template on_replace<AnyOf>().disconnect(&obs);
reg.template on_destroy<AnyOf>().disconnect(&obs);
(reg.template on_destroy<Require>().disconnect(obs), ...);
(reg.template on_construct<Reject>().disconnect(obs), ...);
reg.template on_replace<AnyOf>().disconnect(obs);
reg.template on_destroy<AnyOf>().disconnect(obs);
}
};
template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
struct matcher_handler<matcher<matcher<type_list<Reject...>, type_list<Require...>>, type_list<NoneOf...>, type_list<AllOf...>>> {
template<std::size_t Index>
static void maybe_valid_if(basic_observer *obs, const basic_registry<Entity> &reg, const Entity entt) {
static void maybe_valid_if(basic_observer &obs, const basic_registry<Entity> &reg, const Entity entt) {
if(reg.template has<AllOf...>(entt) && !(reg.template has<NoneOf>(entt) || ...)
&& reg.template has<Require...>(entt) && !(reg.template has<Reject>(entt) || ...))
{
auto *comp = obs->view.try_get(entt);
(comp ? *comp : obs->view.construct(entt)) |= (1 << Index);
auto *comp = obs.view.try_get(entt);
(comp ? *comp : obs.view.construct(entt)) |= (1 << Index);
}
}
template<std::size_t Index>
static void discard_if(basic_observer *obs, const basic_registry<Entity> &, const Entity entt) {
if(auto *value = obs->view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
obs->view.destroy(entt);
static void discard_if(basic_observer &obs, const basic_registry<Entity> &, const Entity entt) {
if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) {
obs.view.destroy(entt);
}
}
template<std::size_t Index>
static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(&obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(&obs), ...);
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(&obs), ...);
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index>>(&obs), ...);
(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(&obs), ...);
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(&obs), ...);
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
}
static void disconnect(basic_observer &obs, basic_registry<Entity> &reg) {
(reg.template on_destroy<Require>().disconnect(&obs), ...);
(reg.template on_construct<Reject>().disconnect(&obs), ...);
(reg.template on_construct<AllOf>().disconnect(&obs), ...);
(reg.template on_destroy<NoneOf>().disconnect(&obs), ...);
(reg.template on_destroy<AllOf>().disconnect(&obs), ...);
(reg.template on_construct<NoneOf>().disconnect(&obs), ...);
(reg.template on_destroy<Require>().disconnect(obs), ...);
(reg.template on_construct<Reject>().disconnect(obs), ...);
(reg.template on_construct<AllOf>().disconnect(obs), ...);
(reg.template on_destroy<NoneOf>().disconnect(obs), ...);
(reg.template on_destroy<AllOf>().disconnect(obs), ...);
(reg.template on_construct<NoneOf>().disconnect(obs), ...);
}
};

View File

@@ -1347,14 +1347,14 @@ public:
ENTT_ASSERT((!std::get<pool_type<Owned> *>(curr->cpools)->group && ...));
((std::get<pool_type<Owned> *>(curr->cpools)->group = curr), ...);
(std::get<pool_type<Owned> *>(curr->cpools)->on_construct().template connect<&handler_type::template maybe_valid_if<Owned>>(curr), ...);
(std::get<pool_type<Owned> *>(curr->cpools)->on_destroy().template connect<&handler_type::discard_if>(curr), ...);
(std::get<pool_type<Owned> *>(curr->cpools)->on_construct().template connect<&handler_type::template maybe_valid_if<Owned>>(*curr), ...);
(std::get<pool_type<Owned> *>(curr->cpools)->on_destroy().template connect<&handler_type::discard_if>(*curr), ...);
(std::get<pool_type<Get> *>(curr->cpools)->on_construct().template connect<&handler_type::template maybe_valid_if<Get>>(curr), ...);
(std::get<pool_type<Get> *>(curr->cpools)->on_destroy().template connect<&handler_type::discard_if>(curr), ...);
(std::get<pool_type<Get> *>(curr->cpools)->on_construct().template connect<&handler_type::template maybe_valid_if<Get>>(*curr), ...);
(std::get<pool_type<Get> *>(curr->cpools)->on_destroy().template connect<&handler_type::discard_if>(*curr), ...);
(std::get<pool_type<Exclude> *>(curr->cpools)->on_destroy().template connect<&handler_type::template maybe_valid_if<Exclude>>(curr), ...);
(std::get<pool_type<Exclude> *>(curr->cpools)->on_construct().template connect<&handler_type::discard_if>(curr), ...);
(std::get<pool_type<Exclude> *>(curr->cpools)->on_destroy().template connect<&handler_type::template maybe_valid_if<Exclude>>(*curr), ...);
(std::get<pool_type<Exclude> *>(curr->cpools)->on_construct().template connect<&handler_type::discard_if>(*curr), ...);
const auto *cpool = std::min({
static_cast<sparse_set<Entity> *>(std::get<pool_type<Owned> *>(curr->cpools))...,

View File

@@ -27,20 +27,20 @@ template<typename Ret, typename... Args>
auto to_function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...);
template<typename Ret, typename... Args, typename Type, typename Payload, typename = std::enable_if_t<std::is_convertible_v<Payload *, Type *>>>
auto to_function_pointer(Ret(*)(Type *, Args...), Payload *) -> Ret(*)(Args...);
template<typename Ret, typename... Args, typename Type, typename Payload, typename = std::enable_if_t<std::is_convertible_v<Payload &, Type &>>>
auto to_function_pointer(Ret(*)(Type &, Args...), Payload &) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... Args>
auto to_function_pointer(Ret(Class:: *)(Args...), const Class *) -> Ret(*)(Args...);
auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...);
template<typename Class, typename Ret, typename... Args>
auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class *) -> Ret(*)(Args...);
auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class &) -> Ret(*)(Args...);
template<typename Class, typename Type>
auto to_function_pointer(Type Class:: *, const Class *) -> Type(*)();
auto to_function_pointer(Type Class:: *, const Class &) -> Type(*)();
template<typename>
@@ -115,9 +115,9 @@ class delegate<Ret(Args...)> {
}
template<auto Candidate, typename Type, std::size_t... Index>
void connect(Type *value_or_instance, std::index_sequence<Index...>) ENTT_NOEXCEPT {
static_assert(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, std::tuple_element_t<Index, std::tuple<Args...>>...>);
data = value_or_instance;
void connect(Type &value_or_instance, std::index_sequence<Index...>) ENTT_NOEXCEPT {
static_assert(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, std::tuple_element_t<Index, std::tuple<Args...>>...>);
data = &value_or_instance;
fn = [](const void *payload, std::tuple<Args &&...> args) -> Ret {
Type *curr = nullptr;
@@ -129,7 +129,7 @@ class delegate<Ret(Args...)> {
}
// Ret(...) makes void(...) eat the return values to avoid errors
return Ret(std::invoke(Candidate, curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
return Ret(std::invoke(Candidate, *curr, std::forward<std::tuple_element_t<Index, std::tuple<Args...>>>(std::get<Index>(args))...));
};
}
@@ -158,10 +158,10 @@ public:
* or a free function with payload.
* @tparam Candidate Member or free function to connect to the delegate.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @param value_or_instance A valid reference that fits the purpose.
*/
template<auto Candidate, typename Type>
delegate(connect_arg_t<Candidate>, Type *value_or_instance) ENTT_NOEXCEPT
delegate(connect_arg_t<Candidate>, Type &value_or_instance) ENTT_NOEXCEPT
: delegate{}
{
connect<Candidate>(value_or_instance);
@@ -190,11 +190,11 @@ public:
*
* @tparam Candidate Member or free function to connect to the delegate.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @param value_or_instance A valid reference that fits the purpose.
*/
template<auto Candidate, typename Type>
void connect(Type *value_or_instance) ENTT_NOEXCEPT {
constexpr auto extent = internal::function_extent_v<decltype(internal::to_function_pointer(std::declval<decltype(Candidate)>(), std::declval<Type *>()))>;
void connect(Type &value_or_instance) ENTT_NOEXCEPT {
constexpr auto extent = internal::function_extent_v<decltype(internal::to_function_pointer(std::declval<decltype(Candidate)>(), value_or_instance))>;
connect<Candidate>(value_or_instance, std::make_index_sequence<extent>{});
}
@@ -292,12 +292,13 @@ delegate(connect_arg_t<Function>) ENTT_NOEXCEPT
* It allows to deduce the function type of the delegate directly from a member
* or a free function with payload provided to the constructor.
*
* @param value_or_instance A valid reference that fits the purpose.
* @tparam Candidate Member or free function to connect to the delegate.
* @tparam Type Type of class or type of payload.
*/
template<auto Candidate, typename Type>
delegate(connect_arg_t<Candidate>, Type *) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<decltype(internal::to_function_pointer(Candidate, std::declval<Type *>()))>>;
delegate(connect_arg_t<Candidate>, Type &value_or_instance) ENTT_NOEXCEPT
-> delegate<std::remove_pointer_t<decltype(internal::to_function_pointer(Candidate, value_or_instance))>>;
}

View File

@@ -24,9 +24,9 @@ namespace entt {
* type `Event`, listeners are such that they can be invoked with an argument of
* type `const Event &`, no matter what the return type is.
*
* The type of the instances is `Class *` (a naked pointer). It means that users
* must guarantee that the lifetimes of the instances overcome the one of the
* dispatcher itself to avoid crashes.
* The types of the instances are `Class &`. Users must guarantee that the
* lifetimes of the objects overcome the one of the dispatcher itself to avoid
* crashes.
*/
class dispatcher {
using event_family = family<struct internal_dispatcher_event_family>;

View File

@@ -42,9 +42,9 @@ class sigh;
/**
* @brief Unmanaged signal handler.
*
* It works directly with naked pointers to classes and pointers to member
* functions as well as pointers to free functions. Users of this class are in
* charge of disconnecting instances before deleting them.
* It works directly with references to classes and pointers to member functions
* as well as pointers to free functions. Users of this class are in charge of
* disconnecting instances before deleting them.
*
* This class serves mainly two purposes:
*
@@ -305,7 +305,7 @@ class sink<Ret(Args...)> {
using signal_type = sigh<Ret(Args...)>;
template<auto Candidate, typename Type>
static void release(Type *value_or_instance, void *signal) {
static void release(Type &value_or_instance, void *signal) {
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance);
}
@@ -363,11 +363,11 @@ public:
*
* @tparam Candidate Member or free function to connect to the signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @param value_or_instance A valid reference that fits the purpose.
* @return A properly initialized connection object.
*/
template<auto Candidate, typename Type>
connection connect(Type *value_or_instance) {
connection connect(Type &value_or_instance) {
disconnect<Candidate>(value_or_instance);
delegate<void(void *)> conn{};
conn.template connect<&sink::release<Candidate, Type>>(value_or_instance);
@@ -392,10 +392,10 @@ public:
* a signal.
* @tparam Candidate Member or free function to disconnect from the signal.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @param value_or_instance A valid reference that fits the purpose.
*/
template<auto Candidate, typename Type>
void disconnect(Type *value_or_instance) {
void disconnect(Type &value_or_instance) {
auto &calls = signal->calls;
delegate<Ret(Args...)> delegate{};
delegate.template connect<Candidate>(value_or_instance);
@@ -405,12 +405,14 @@ public:
/**
* @brief Disconnects member functions or free functions based on an
* instance or specific payload.
* @param value_or_instance A valid pointer that fits the purpose.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid reference that fits the purpose.
*/
void disconnect(const void *value_or_instance) {
template<typename Type>
void disconnect(const Type &value_or_instance) {
auto &calls = signal->calls;
calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto &delegate) {
return value_or_instance == delegate.instance();
calls.erase(std::remove_if(calls.begin(), calls.end(), [&value_or_instance](const auto &delegate) {
return delegate.instance() == &value_or_instance;
}), calls.end());
}

View File

@@ -925,10 +925,10 @@ TEST(Registry, Signals) {
entt::registry registry;
listener listener;
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(&listener);
registry.on_destroy<empty_type>().connect<&listener::decr<empty_type>>(&listener);
registry.on_construct<int>().connect<&listener::incr<int>>(&listener);
registry.on_destroy<int>().connect<&listener::decr<int>>(&listener);
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener);
registry.on_destroy<empty_type>().connect<&listener::decr<empty_type>>(listener);
registry.on_construct<int>().connect<&listener::incr<int>>(listener);
registry.on_destroy<int>().connect<&listener::decr<int>>(listener);
auto e0 = registry.create();
auto e1 = registry.create();
@@ -951,8 +951,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e0);
registry.on_destroy<empty_type>().disconnect<&listener::decr<empty_type>>(&listener);
registry.on_destroy<int>().disconnect<&listener::decr<int>>(&listener);
registry.on_destroy<empty_type>().disconnect<&listener::decr<empty_type>>(listener);
registry.on_destroy<int>().disconnect<&listener::decr<int>>(listener);
registry.remove<empty_type>(e1);
registry.remove<int>(e1);
@@ -960,8 +960,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e0);
registry.on_construct<empty_type>().disconnect<&listener::incr<empty_type>>(&listener);
registry.on_construct<int>().disconnect<&listener::incr<int>>(&listener);
registry.on_construct<empty_type>().disconnect<&listener::incr<empty_type>>(listener);
registry.on_construct<int>().disconnect<&listener::incr<int>>(listener);
registry.assign<empty_type>(e1);
registry.assign<int>(e1);
@@ -969,8 +969,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e0);
registry.on_construct<int>().connect<&listener::incr<int>>(&listener);
registry.on_destroy<int>().connect<&listener::decr<int>>(&listener);
registry.on_construct<int>().connect<&listener::incr<int>>(listener);
registry.on_destroy<int>().connect<&listener::decr<int>>(listener);
registry.assign<int>(e0);
registry.reset<int>(e1);
@@ -978,8 +978,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e1);
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(&listener);
registry.on_destroy<empty_type>().connect<&listener::decr<empty_type>>(&listener);
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener);
registry.on_destroy<empty_type>().connect<&listener::decr<empty_type>>(listener);
registry.reset<empty_type>(e1);
registry.assign<empty_type>(e0);
@@ -1011,8 +1011,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e0);
registry.on_destroy<empty_type>().disconnect<&listener::decr<empty_type>>(&listener);
registry.on_destroy<int>().disconnect<&listener::decr<int>>(&listener);
registry.on_destroy<empty_type>().disconnect<&listener::decr<empty_type>>(listener);
registry.on_destroy<int>().disconnect<&listener::decr<int>>(listener);
registry.assign_or_replace<empty_type>(e0);
registry.assign_or_replace<int>(e0);
@@ -1020,8 +1020,8 @@ TEST(Registry, Signals) {
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e0);
registry.on_replace<empty_type>().connect<&listener::incr<empty_type>>(&listener);
registry.on_replace<int>().connect<&listener::incr<int>>(&listener);
registry.on_replace<empty_type>().connect<&listener::incr<empty_type>>(listener);
registry.on_replace<int>().connect<&listener::incr<int>>(listener);
registry.assign_or_replace<empty_type>(e0);
registry.assign_or_replace<int>(e0);
@@ -1179,13 +1179,13 @@ TEST(Registry, CreateManyEntitiesWithComponentsAtOnceWithListener) {
entt::entity entities[3];
listener listener;
registry.on_construct<int>().connect<&listener::incr<int>>(&listener);
registry.on_construct<int>().connect<&listener::incr<int>>(listener);
registry.create<int, char>(std::begin(entities), std::end(entities));
ASSERT_EQ(listener.counter, 3);
registry.on_construct<int>().disconnect<&listener::incr<int>>(&listener);
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(&listener);
registry.on_construct<int>().disconnect<&listener::incr<int>>(listener);
registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener);
registry.create<char, empty_type>(std::begin(entities), std::end(entities));
ASSERT_EQ(listener.counter, 6);

View File

@@ -6,8 +6,8 @@ int delegate_function(const int &i) {
return i*i;
}
int curried_function(const int *i, int j) {
return *i+j;
int curried_function(const int &i, int j) {
return i+j;
}
int non_const_reference(int &i) {
@@ -51,7 +51,7 @@ TEST(Delegate, Functionalities) {
ASSERT_EQ(ff_del, mf_del);
ff_del.connect<&delegate_function>();
mf_del.connect<&delegate_functor::operator()>(&functor);
mf_del.connect<&delegate_functor::operator()>(functor);
ASSERT_TRUE(ff_del);
ASSERT_TRUE(mf_del);
@@ -82,7 +82,7 @@ TEST(Delegate, DataMembers) {
entt::delegate<double()> delegate;
delegate_functor functor;
delegate.connect<&delegate_functor::data_member>(&functor);
delegate.connect<&delegate_functor::data_member>(functor);
ASSERT_EQ(delegate(), 42);
}
@@ -113,37 +113,37 @@ TEST(Delegate, Comparison) {
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&curried_function>(&value);
lhs.connect<&curried_function>(value);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_function>, &value}));
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_function>, value}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&curried_function>(&value);
rhs.connect<&curried_function>(value);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_function>, &value}));
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&curried_function>, value}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&delegate_functor::operator()>(&functor);
lhs.connect<&delegate_functor::operator()>(functor);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, &functor}));
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, functor}));
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
ASSERT_NE(lhs, rhs);
rhs.connect<&delegate_functor::operator()>(&functor);
rhs.connect<&delegate_functor::operator()>(functor);
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, &functor}));
ASSERT_EQ(rhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, functor}));
ASSERT_FALSE(lhs != rhs);
ASSERT_TRUE(lhs == rhs);
ASSERT_EQ(lhs, rhs);
lhs.connect<&delegate_functor::operator()>(&other);
lhs.connect<&delegate_functor::operator()>(other);
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, &other}));
ASSERT_EQ(lhs, (entt::delegate<int(int)>{entt::connect_arg<&delegate_functor::operator()>, other}));
ASSERT_NE(lhs.instance(), rhs.instance());
ASSERT_TRUE(lhs != rhs);
ASSERT_FALSE(lhs == rhs);
@@ -168,16 +168,16 @@ TEST(Delegate, ConstNonConstNoExcept) {
entt::delegate<void()> delegate;
const_nonconst_noexcept functor;
delegate.connect<&const_nonconst_noexcept::f>(&functor);
delegate.connect<&const_nonconst_noexcept::f>(functor);
delegate();
delegate.connect<&const_nonconst_noexcept::g>(&functor);
delegate.connect<&const_nonconst_noexcept::g>(functor);
delegate();
delegate.connect<&const_nonconst_noexcept::h>(&functor);
delegate.connect<&const_nonconst_noexcept::h>(functor);
delegate();
delegate.connect<&const_nonconst_noexcept::i>(&functor);
delegate.connect<&const_nonconst_noexcept::i>(functor);
delegate();
ASSERT_EQ(functor.cnt, 4);
@@ -188,17 +188,17 @@ TEST(Delegate, DeductionGuide) {
int value = 0;
entt::delegate func{entt::connect_arg<&delegate_function>};
entt::delegate curried_func{entt::connect_arg<&curried_function>, &value};
entt::delegate curried_func_const{entt::connect_arg<&curried_function>, &std::as_const(value)};
entt::delegate member_func_f{entt::connect_arg<&const_nonconst_noexcept::f>, &functor};
entt::delegate member_func_g{entt::connect_arg<&const_nonconst_noexcept::g>, &functor};
entt::delegate member_func_h{entt::connect_arg<&const_nonconst_noexcept::h>, &functor};
entt::delegate member_func_h_const{entt::connect_arg<&const_nonconst_noexcept::h>, &std::as_const(functor)};
entt::delegate member_func_i{entt::connect_arg<&const_nonconst_noexcept::i>, &functor};
entt::delegate member_func_i_const{entt::connect_arg<&const_nonconst_noexcept::i>, &std::as_const(functor)};
entt::delegate data_member_u{entt::connect_arg<&const_nonconst_noexcept::u>, &functor};
entt::delegate data_member_v{entt::connect_arg<&const_nonconst_noexcept::v>, &functor};
entt::delegate data_member_v_const{entt::connect_arg<&const_nonconst_noexcept::v>, &std::as_const(functor)};
entt::delegate curried_func{entt::connect_arg<&curried_function>, value};
entt::delegate curried_func_const{entt::connect_arg<&curried_function>, std::as_const(value)};
entt::delegate member_func_f{entt::connect_arg<&const_nonconst_noexcept::f>, functor};
entt::delegate member_func_g{entt::connect_arg<&const_nonconst_noexcept::g>, functor};
entt::delegate member_func_h{entt::connect_arg<&const_nonconst_noexcept::h>, functor};
entt::delegate member_func_h_const{entt::connect_arg<&const_nonconst_noexcept::h>, std::as_const(functor)};
entt::delegate member_func_i{entt::connect_arg<&const_nonconst_noexcept::i>, functor};
entt::delegate member_func_i_const{entt::connect_arg<&const_nonconst_noexcept::i>, std::as_const(functor)};
entt::delegate data_member_u{entt::connect_arg<&const_nonconst_noexcept::u>, functor};
entt::delegate data_member_v{entt::connect_arg<&const_nonconst_noexcept::v>, functor};
entt::delegate data_member_v_const{entt::connect_arg<&const_nonconst_noexcept::v>, std::as_const(functor)};
static_assert(std::is_same_v<typename decltype(func)::function_type, int(const int &)>);
static_assert(std::is_same_v<typename decltype(curried_func)::function_type, int(int)>);
@@ -233,7 +233,7 @@ TEST(Delegate, ConstInstance) {
ASSERT_FALSE(delegate);
delegate.connect<&delegate_functor::identity>(&functor);
delegate.connect<&delegate_functor::identity>(functor);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(3), 3);
@@ -266,7 +266,7 @@ TEST(Delegate, CurriedFunction) {
entt::delegate<int(int)> delegate;
const auto value = 3;
delegate.connect<&curried_function>(&value);
delegate.connect<&curried_function>(value);
ASSERT_TRUE(delegate);
ASSERT_EQ(delegate(1), 4);
@@ -278,8 +278,8 @@ TEST(Delegate, Constructors) {
entt::delegate<int(int)> empty{};
entt::delegate<int(int)> func{entt::connect_arg<&delegate_function>};
entt::delegate<int(int)> curr{entt::connect_arg<&curried_function>, &value};
entt::delegate<int(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};
entt::delegate<int(int)> curr{entt::connect_arg<&curried_function>, value};
entt::delegate<int(int)> member{entt::connect_arg<&delegate_functor::operator()>, functor};
ASSERT_FALSE(empty);
@@ -297,8 +297,8 @@ TEST(Delegate, VoidVsNonVoidReturnType) {
delegate_functor functor;
entt::delegate<void(int)> func{entt::connect_arg<&delegate_function>};
entt::delegate<void(int)> member{entt::connect_arg<&delegate_functor::operator()>, &functor};
entt::delegate<void(int)> cmember{entt::connect_arg<&delegate_functor::identity>, &std::as_const(functor)};
entt::delegate<void(int)> member{entt::connect_arg<&delegate_functor::operator()>, functor};
entt::delegate<void(int)> cmember{entt::connect_arg<&delegate_functor::identity>, std::as_const(functor)};
ASSERT_TRUE(func);
ASSERT_TRUE(member);
@@ -315,7 +315,7 @@ TEST(Delegate, TheLessTheBetter) {
ASSERT_EQ(delegate(3, 'c'), 9);
// int delegate_functor::operator()(int);
delegate.connect<&delegate_functor::operator()>(&functor);
delegate.connect<&delegate_functor::operator()>(functor);
ASSERT_EQ(delegate(3, 'c'), 6);
}

View File

@@ -23,7 +23,7 @@ TEST(Dispatcher, Functionalities) {
dispatcher.enqueue<one_more_event>();
dispatcher.update<one_more_event>();
dispatcher.sink<an_event>().connect<&receiver::receive>(&receiver);
dispatcher.sink<an_event>().connect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
dispatcher.enqueue<an_event>();
@@ -43,7 +43,7 @@ TEST(Dispatcher, Functionalities) {
an_event event{};
dispatcher.sink<an_event>().disconnect<&receiver::receive>(&receiver);
dispatcher.sink<an_event>().disconnect<&receiver::receive>(receiver);
dispatcher.trigger<an_event>();
dispatcher.enqueue(event);
dispatcher.update();

View File

@@ -96,43 +96,34 @@ TEST(SigH, Functions) {
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(static_cast<entt::sigh<void(int &)>::size_type>(0), sigh.size());
ASSERT_EQ(v, 0);
sink.connect<&sigh_listener::f>();
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(static_cast<entt::sigh<void(int &)>::size_type>(1), sigh.size());
sink.disconnect(nullptr);
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(static_cast<entt::sigh<void(int &)>::size_type>(0), sigh.size());}
}
TEST(SigH, Members) {
sigh_listener l1, l2;
entt::sigh<bool(int)> sigh;
entt::sink sink{sigh};
sink.connect<&sigh_listener::g>(&l1);
sink.connect<&sigh_listener::g>(l1);
sigh.publish(42);
ASSERT_TRUE(l1.k);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(static_cast<entt::sigh<bool(int)>::size_type>(1), sigh.size());
sink.disconnect<&sigh_listener::g>(&l1);
sink.disconnect<&sigh_listener::g>(l1);
sigh.publish(42);
ASSERT_TRUE(l1.k);
ASSERT_TRUE(sigh.empty());
ASSERT_EQ(static_cast<entt::sigh<bool(int)>::size_type>(0), sigh.size());
sink.connect<&sigh_listener::g>(&l1);
sink.connect<&sigh_listener::h>(&l2);
sink.connect<&sigh_listener::g>(l1);
sink.connect<&sigh_listener::h>(l2);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(static_cast<entt::sigh<bool(int)>::size_type>(2), sigh.size());
sink.disconnect(&l1);
sink.disconnect(l1);
ASSERT_FALSE(sigh.empty());
ASSERT_EQ(static_cast<entt::sigh<bool(int)>::size_type>(1), sigh.size());
@@ -144,8 +135,8 @@ TEST(SigH, Collector) {
entt::sink sink{sigh};
int cnt = 0;
sink.connect<&sigh_listener::g>(&listener);
sink.connect<&sigh_listener::h>(&listener);
sink.connect<&sigh_listener::g>(listener);
sink.connect<&sigh_listener::h>(listener);
listener.k = true;
sigh.collect([&listener, &cnt](bool value) {
@@ -174,8 +165,8 @@ TEST(SigH, CollectorVoid) {
entt::sink sink{sigh};
int cnt = 0;
sink.connect<&sigh_listener::g>(&listener);
sink.connect<&sigh_listener::h>(&listener);
sink.connect<&sigh_listener::g>(listener);
sink.connect<&sigh_listener::h>(listener);
sigh.collect([&cnt]() { ++cnt; }, 42);
ASSERT_FALSE(sigh.empty());
@@ -219,7 +210,7 @@ TEST(SigH, ScopedConnection) {
{
ASSERT_FALSE(listener.k);
entt::scoped_connection conn = sink.connect<&sigh_listener::g>(&listener);
entt::scoped_connection conn = sink.connect<&sigh_listener::g>(listener);
sigh.publish(42);
ASSERT_FALSE(sigh.empty());
@@ -244,7 +235,7 @@ TEST(SigH, ScopedConnectionConstructorsAndOperators) {
ASSERT_FALSE(conn);
entt::scoped_connection inner{};
inner = sink.connect<&sigh_listener::g>(&listener);
inner = sink.connect<&sigh_listener::g>(listener);
sigh.publish(42);
ASSERT_FALSE(sigh.empty());
@@ -256,7 +247,7 @@ TEST(SigH, ScopedConnectionConstructorsAndOperators) {
ASSERT_TRUE(sigh.empty());
ASSERT_FALSE(inner);
auto basic = sink.connect<&sigh_listener::g>(&listener);
auto basic = sink.connect<&sigh_listener::g>(listener);
inner = std::as_const(basic);
sigh.publish(42);
@@ -285,19 +276,19 @@ TEST(SigH, ConstNonConstNoExcept) {
const_nonconst_noexcept functor;
const const_nonconst_noexcept cfunctor;
sink.connect<&const_nonconst_noexcept::f>(&functor);
sink.connect<&const_nonconst_noexcept::g>(&functor);
sink.connect<&const_nonconst_noexcept::h>(&cfunctor);
sink.connect<&const_nonconst_noexcept::i>(&cfunctor);
sink.connect<&const_nonconst_noexcept::f>(functor);
sink.connect<&const_nonconst_noexcept::g>(functor);
sink.connect<&const_nonconst_noexcept::h>(cfunctor);
sink.connect<&const_nonconst_noexcept::i>(cfunctor);
sigh.publish();
ASSERT_EQ(functor.cnt, 2);
ASSERT_EQ(cfunctor.cnt, 2);
sink.disconnect<&const_nonconst_noexcept::f>(&functor);
sink.disconnect<&const_nonconst_noexcept::g>(&functor);
sink.disconnect<&const_nonconst_noexcept::h>(&cfunctor);
sink.disconnect<&const_nonconst_noexcept::i>(&cfunctor);
sink.disconnect<&const_nonconst_noexcept::f>(functor);
sink.disconnect<&const_nonconst_noexcept::g>(functor);
sink.disconnect<&const_nonconst_noexcept::h>(cfunctor);
sink.disconnect<&const_nonconst_noexcept::i>(cfunctor);
sigh.publish();
ASSERT_EQ(functor.cnt, 2);

View File

@@ -68,8 +68,8 @@ TEST(Lib, Dispatcher) {
entt::dispatcher dispatcher;
listener listener;
dispatcher.sink<an_event>().connect<&listener::on_an_event>(&listener);
dispatcher.sink<another_event>().connect<&listener::on_another_event>(&listener);
dispatcher.sink<an_event>().connect<&listener::on_an_event>(listener);
dispatcher.sink<another_event>().connect<&listener::on_another_event>(listener);
listener.value = 0;