build: remove entt library

This commit is contained in:
2026-07-27 09:33:51 +08:00
parent 7b530968af
commit 6d0e60e241
115 changed files with 0 additions and 24797 deletions

View File

@@ -1,314 +0,0 @@
#ifndef ENTT_SIGNAL_DELEGATE_HPP
#define ENTT_SIGNAL_DELEGATE_HPP
#include "../config/config.h"
#include "../core/type_traits.hpp"
#include "../stl/cstddef.hpp"
#include "../stl/functional.hpp"
#include "../stl/tuple.hpp"
#include "../stl/type_traits.hpp"
#include "../stl/utility.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Ret, typename... Args>
auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...);
template<typename Ret, typename Type, typename... Args, typename Other>
auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...);
template<typename Class, typename Ret, typename... Args, typename... Other>
auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...);
template<typename Class, typename Ret, typename... Args, typename... Other>
auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...);
template<typename Class, typename Type, typename... Other>
requires stl::is_member_object_pointer_v<Type Class::*>
auto function_pointer(Type Class::*, Other &&...) -> Type (*)();
template<typename... Type>
using function_pointer_t = decltype(function_pointer(stl::declval<Type>()...));
template<typename... Class, typename Ret, typename... Args>
[[nodiscard]] ENTT_CONSTEVAL auto index_sequence_for(Ret (*)(Args...)) {
return stl::index_sequence_for<Class..., Args...>{};
}
} // namespace internal
/*! @endcond */
/**
* @brief Basic delegate implementation.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error unless the template parameter is a function type.
*/
template<typename>
class delegate;
/**
* @brief Utility class to use to send around functions and members.
*
* Unmanaged delegate for function pointers and members. Users of this class are
* in charge of disconnecting instances before deleting them.
*
* A delegate can be used as a general purpose invoker without memory overhead
* for free functions possibly with payloads and bound or unbound members.
*
* @tparam Ret Return type of a function type.
* @tparam Args Types of arguments of a function type.
*/
template<typename Ret, typename... Args>
class delegate<Ret(Args...)> {
using return_type = stl::remove_const_t<Ret>;
using delegate_type = return_type(const void *, Args...);
template<auto Candidate, stl::size_t... Index>
[[nodiscard]] auto wrap(stl::index_sequence<Index...>) noexcept {
return [](const void *, Args... args) -> return_type {
[[maybe_unused]] const auto arguments = stl::forward_as_tuple(stl::forward<Args>(args)...);
[[maybe_unused]] constexpr auto offset = !stl::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index));
return static_cast<Ret>(stl::invoke(Candidate, stl::forward<type_list_element_t<Index + offset, type_list<Args...>>>(stl::get<Index + offset>(arguments))...));
};
}
template<auto Candidate, typename Type, stl::size_t... Index>
[[nodiscard]] auto wrap(Type &, stl::index_sequence<Index...>) noexcept {
return [](const void *payload, Args... args) -> return_type {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
[[maybe_unused]] const auto arguments = stl::forward_as_tuple(stl::forward<Args>(args)...);
[[maybe_unused]] constexpr auto offset = !stl::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index));
return static_cast<Ret>(stl::invoke(Candidate, *curr, stl::forward<type_list_element_t<Index + offset, type_list<Args...>>>(stl::get<Index + offset>(arguments))...));
};
}
template<auto Candidate, typename Type, stl::size_t... Index>
[[nodiscard]] auto wrap(Type *, stl::index_sequence<Index...>) noexcept {
return [](const void *payload, Args... args) -> return_type {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
[[maybe_unused]] const auto arguments = stl::forward_as_tuple(stl::forward<Args>(args)...);
[[maybe_unused]] constexpr auto offset = !stl::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...> * (sizeof...(Args) - sizeof...(Index));
return static_cast<Ret>(stl::invoke(Candidate, curr, stl::forward<type_list_element_t<Index + offset, type_list<Args...>>>(stl::get<Index + offset>(arguments))...));
};
}
public:
/*! @brief Function type of the contained target. */
using function_type = Ret(const void *, Args...);
/*! @brief Function type of the delegate. */
using type = Ret(Args...);
/*! @brief Return type of the delegate. */
using result_type = Ret;
/*! @brief Default constructor. */
delegate() noexcept = default;
/**
* @brief Constructs a delegate with a given object or payload, if any.
* @tparam Candidate Function or member to connect to the delegate.
* @tparam Type Type of class or type of payload, if any.
* @param value_or_instance Optional valid object that fits the purpose.
*/
template<auto Candidate, typename... Type>
delegate(connect_arg_t<Candidate>, Type &&...value_or_instance) noexcept {
connect<Candidate>(stl::forward<Type>(value_or_instance)...);
}
/**
* @brief Constructs a delegate and connects an user defined function with
* optional payload.
* @param function Function to connect to the delegate.
* @param payload User defined arbitrary data.
*/
delegate(function_type *function, const void *payload = nullptr) noexcept {
connect(function, payload);
}
/**
* @brief Connects a free function or an unbound member to a delegate.
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Candidate>
void connect() noexcept {
instance = nullptr;
if constexpr(stl::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
fn = [](const void *, Args... args) -> return_type {
return Ret(stl::invoke(Candidate, stl::forward<Args>(args)...));
};
} else if constexpr(stl::is_member_pointer_v<decltype(Candidate)>) {
fn = wrap<Candidate>(internal::index_sequence_for<type_list_element_t<0, type_list<Args...>>>(internal::function_pointer_t<decltype(Candidate)>{}));
} else {
fn = wrap<Candidate>(internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate)>{}));
}
}
/**
* @brief Connects a free function with payload or a bound member to a
* delegate.
*
* The delegate isn't responsible for the connected object or the payload.
* Users must always guarantee that the lifetime of the instance overcomes
* the one of the delegate.<br/>
* When used to connect a free function with payload, its signature must be
* such that the instance is the first argument before the ones used to
* define the delegate itself.
*
* @tparam Candidate Function or member to connect to the delegate.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid reference that fits the purpose.
*/
template<auto Candidate, typename Type>
void connect(Type &value_or_instance) noexcept {
instance = &value_or_instance;
if constexpr(stl::is_invocable_r_v<Ret, decltype(Candidate), Type &, Args...>) {
fn = [](const void *payload, Args... args) -> return_type {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
return Ret(stl::invoke(Candidate, *curr, stl::forward<Args>(args)...));
};
} else {
fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
}
}
/**
* @brief Connects a free function with payload or a bound member to a
* delegate.
*
* @sa connect(Type &)
*
* @tparam Candidate Function or member 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.
*/
template<auto Candidate, typename Type>
void connect(Type *value_or_instance) noexcept {
instance = value_or_instance;
if constexpr(stl::is_invocable_r_v<Ret, decltype(Candidate), Type *, Args...>) {
fn = [](const void *payload, Args... args) -> return_type {
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
return Ret(stl::invoke(Candidate, curr, stl::forward<Args>(args)...));
};
} else {
fn = wrap<Candidate>(value_or_instance, internal::index_sequence_for(internal::function_pointer_t<decltype(Candidate), Type>{}));
}
}
/**
* @brief Connects an user defined function with optional payload to a
* delegate.
*
* The delegate isn't responsible for the connected object or the payload.
* Users must always guarantee that the lifetime of an instance overcomes
* the one of the delegate.<br/>
* The payload is returned as the first argument to the target function in
* all cases.
*
* @param function Function to connect to the delegate.
* @param payload User defined arbitrary data.
*/
void connect(function_type *function, const void *payload = nullptr) noexcept {
ENTT_ASSERT(function != nullptr, "Uninitialized function pointer");
instance = payload;
fn = function;
}
/**
* @brief Resets a delegate.
*
* After a reset, a delegate cannot be invoked anymore.
*/
void reset() noexcept {
instance = nullptr;
fn = nullptr;
}
/**
* @brief Returns a pointer to the stored callable function target, if any.
* @return An opaque pointer to the stored callable function target.
*/
[[nodiscard]] function_type *target() const noexcept {
return fn;
}
/**
* @brief Returns the instance or the payload linked to a delegate, if any.
* @return An opaque pointer to the underlying data.
*/
[[nodiscard]] const void *data() const noexcept {
return instance;
}
/**
* @brief Triggers a delegate.
*
* The delegate invokes the underlying function and returns the result.
*
* @warning
* Attempting to trigger an invalid delegate results in undefined
* behavior.
*
* @param args Arguments to use to invoke the underlying function.
* @return The value returned by the underlying function.
*/
Ret operator()(Args... args) const {
ENTT_ASSERT(static_cast<bool>(*this), "Uninitialized delegate");
return fn(instance, stl::forward<Args>(args)...);
}
/**
* @brief Checks whether a delegate actually stores a listener.
* @return False if the delegate is empty, true otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
// no need to also test instance
return !(fn == nullptr);
}
/**
* @brief Compares the contents of two delegates.
* @param other Delegate with which to compare.
* @return False if the two contents differ, true otherwise.
*/
[[nodiscard]] bool operator==(const delegate<Ret(Args...)> &other) const noexcept {
return fn == other.fn && instance == other.instance;
}
private:
const void *instance{};
delegate_type *fn{};
};
/**
* @brief Deduction guide.
* @tparam Candidate Function or member to connect to the delegate.
*/
template<auto Candidate>
delegate(connect_arg_t<Candidate>) -> delegate<stl::remove_pointer_t<internal::function_pointer_t<decltype(Candidate)>>>;
/**
* @brief Deduction guide.
* @tparam Candidate Function or member 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 &&) -> delegate<stl::remove_pointer_t<internal::function_pointer_t<decltype(Candidate), Type>>>;
/**
* @brief Deduction guide.
* @tparam Ret Return type of a function type.
* @tparam Args Types of arguments of a function type.
*/
template<typename Ret, typename... Args>
delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate<Ret(Args...)>;
} // namespace entt
#endif

View File

@@ -1,391 +0,0 @@
#ifndef ENTT_SIGNAL_DISPATCHER_HPP
#define ENTT_SIGNAL_DISPATCHER_HPP
#include "../container/dense_map.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/concepts.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../stl/cstddef.hpp"
#include "../stl/functional.hpp"
#include "../stl/memory.hpp"
#include "../stl/type_traits.hpp"
#include "../stl/utility.hpp"
#include "../stl/vector.hpp"
#include "fwd.hpp"
#include "sigh.hpp"
namespace entt {
/*! @cond ENTT_INTERNAL */
namespace internal {
struct basic_dispatcher_handler {
virtual ~basic_dispatcher_handler() = default;
virtual void publish() = 0;
virtual void disconnect(void *) = 0;
virtual void clear() noexcept = 0;
[[nodiscard]] virtual stl::size_t size() const noexcept = 0;
};
template<cvref_unqualified Type, typename Allocator>
class dispatcher_handler final: public basic_dispatcher_handler {
using alloc_traits = stl::allocator_traits<Allocator>;
using signal_type = sigh<void(Type &), Allocator>;
using container_type = stl::vector<Type, typename alloc_traits::template rebind_alloc<Type>>;
public:
using allocator_type = Allocator;
dispatcher_handler(const allocator_type &allocator)
: signal{allocator},
events{allocator} {}
void publish() override {
container_type other{};
other.swap(events);
for(auto &&elem: other) {
signal.publish(elem);
}
}
void disconnect(void *instance) override {
bucket().disconnect(instance);
}
void clear() noexcept override {
events.clear();
}
[[nodiscard]] auto bucket() noexcept {
return typename signal_type::sink_type{signal};
}
void trigger(Type &event) {
signal.publish(event);
}
template<typename... Args>
void enqueue(Args &&...args) {
if constexpr(stl::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !stl::is_default_constructible_v<Type>)) {
events.push_back(Type{stl::forward<Args>(args)...});
} else {
events.emplace_back(stl::forward<Args>(args)...);
}
}
[[nodiscard]] stl::size_t size() const noexcept override {
return events.size();
}
private:
signal_type signal;
container_type events;
};
} // namespace internal
/*! @endcond */
/**
* @brief Basic dispatcher implementation.
*
* A dispatcher can be used either to trigger an immediate event or to enqueue
* events to be published all together once per tick.<br/>
* Listeners are provided in the form of member functions. For each event of
* type `Type`, listeners are such that they can be invoked with an argument of
* type `Type &`, no matter what the return type is.
*
* The dispatcher creates instances of the `sigh` class internally. Refer to the
* documentation of the latter for more details.
*
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Allocator>
class basic_dispatcher {
template<typename Type>
using handler_type = internal::dispatcher_handler<Type, Allocator>;
using key_type = id_type;
// stl::shared_ptr because of its type erased allocator which is useful here
using mapped_type = stl::shared_ptr<internal::basic_dispatcher_handler>;
using alloc_traits = stl::allocator_traits<Allocator>;
using container_allocator = alloc_traits::template rebind_alloc<stl::pair<const key_type, mapped_type>>;
using container_type = dense_map<key_type, mapped_type, stl::identity, stl::equal_to<>, container_allocator>;
template<cvref_unqualified Type>
[[nodiscard]] handler_type<Type> &assure(const id_type id) {
auto &&ptr = pools.first()[id];
if(!ptr) {
const auto &allocator = get_allocator();
ptr = stl::allocate_shared<handler_type<Type>>(allocator, allocator);
}
return static_cast<handler_type<Type> &>(*ptr);
}
template<cvref_unqualified Type>
[[nodiscard]] const handler_type<Type> *assure(const id_type id) const {
if(auto it = pools.first().find(id); it != pools.first().cend()) {
return static_cast<const handler_type<Type> *>(it->second.get());
}
return nullptr;
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Unsigned integer type. */
using size_type = stl::size_t;
/*! @brief Default constructor. */
basic_dispatcher()
: basic_dispatcher{allocator_type{}} {}
/**
* @brief Constructs a dispatcher with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_dispatcher(const allocator_type &allocator)
: pools{allocator, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_dispatcher(const basic_dispatcher &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_dispatcher(basic_dispatcher &&other) noexcept
: pools{stl::move(other.pools)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator)
: pools{container_type{stl::move(other.pools.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
}
/*! @brief Default destructor. */
~basic_dispatcher() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This dispatcher.
*/
basic_dispatcher &operator=(const basic_dispatcher &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This dispatcher.
*/
basic_dispatcher &operator=(basic_dispatcher &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed");
swap(other);
return *this;
}
/**
* @brief Exchanges the contents with those of a given dispatcher.
* @param other Dispatcher to exchange the content with.
*/
void swap(basic_dispatcher &other) noexcept {
using stl::swap;
swap(pools, other.pools);
}
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return pools.second();
}
/**
* @brief Returns the number of pending events for a given type.
* @tparam Type Type of event for which to return the count.
* @param id Name used to map the event queue within the dispatcher.
* @return The number of pending events for the given type.
*/
template<typename Type>
[[nodiscard]] size_type size(const id_type id = type_hash<Type>::value()) const noexcept {
const auto *cpool = assure<stl::decay_t<Type>>(id);
return cpool ? cpool->size() : 0u;
}
/**
* @brief Returns the total number of pending events.
* @return The total number of pending events.
*/
[[nodiscard]] size_type size() const noexcept {
size_type count{};
for(auto &&cpool: pools.first()) {
count += cpool.second->size();
}
return count;
}
/**
* @brief Returns a sink object for the given event and queue.
*
* A sink is an opaque object used to connect listeners to events.
*
* The function type for a listener is _compatible_ with:
*
* @code{.cpp}
* void(Type &);
* @endcode
*
* The order of invocation of the listeners isn't guaranteed.
*
* @sa sink
*
* @tparam Type Type of event of which to get the sink.
* @param id Name used to map the event queue within the dispatcher.
* @return A temporary sink object.
*/
template<typename Type>
[[nodiscard]] auto sink(const id_type id = type_hash<Type>::value()) {
return assure<Type>(id).bucket();
}
/**
* @brief Triggers an immediate event of a given type.
* @tparam Type Type of event to trigger.
* @param value An instance of the given type of event.
*/
template<typename Type>
void trigger(Type value) {
trigger(type_hash<stl::decay_t<Type>>::value(), value);
}
/**
* @brief Triggers an immediate event on a queue of a given type.
* @tparam Type Type of event to trigger.
* @param value An instance of the given type of event.
* @param id Name used to map the event queue within the dispatcher.
*/
template<typename Type>
void trigger(const id_type id, Type value) {
assure<stl::decay_t<Type>>(id).trigger(value);
}
/**
* @brief Enqueues an event of the given type.
* @tparam Type Type of event to enqueue.
* @tparam Args Types of arguments to use to construct the event.
* @param args Arguments to use to construct the event.
*/
template<typename Type, typename... Args>
void enqueue(Args &&...args) {
enqueue_hint<Type>(type_hash<Type>::value(), stl::forward<Args>(args)...);
}
/**
* @brief Enqueues an event of the given type.
* @tparam Type Type of event to enqueue.
* @param value An instance of the given type of event.
*/
template<typename Type>
void enqueue(Type &&value) {
enqueue_hint(type_hash<stl::decay_t<Type>>::value(), stl::forward<Type>(value));
}
/**
* @brief Enqueues an event of the given type.
* @tparam Type Type of event to enqueue.
* @tparam Args Types of arguments to use to construct the event.
* @param id Name used to map the event queue within the dispatcher.
* @param args Arguments to use to construct the event.
*/
template<typename Type, typename... Args>
void enqueue_hint(const id_type id, Args &&...args) {
assure<Type>(id).enqueue(stl::forward<Args>(args)...);
}
/**
* @brief Enqueues an event of the given type.
* @tparam Type Type of event to enqueue.
* @param id Name used to map the event queue within the dispatcher.
* @param value An instance of the given type of event.
*/
template<typename Type>
void enqueue_hint(const id_type id, Type &&value) {
assure<stl::decay_t<Type>>(id).enqueue(stl::forward<Type>(value));
}
/**
* @brief Utility function to disconnect everything related to a given value
* or instance from a dispatcher.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
*/
template<typename Type>
void disconnect(Type &value_or_instance) {
disconnect(&value_or_instance);
}
/**
* @brief Utility function to disconnect everything related to a given value
* or instance from a dispatcher.
* @tparam Type Type of class or type of payload.
* @param value_or_instance A valid object that fits the purpose.
*/
template<typename Type>
void disconnect(Type *value_or_instance) {
for(auto &&cpool: pools.first()) {
cpool.second->disconnect(value_or_instance);
}
}
/**
* @brief Discards all the events stored so far in a given queue.
* @tparam Type Type of event to discard.
* @param id Name used to map the event queue within the dispatcher.
*/
template<typename Type>
void clear(const id_type id = type_hash<Type>::value()) {
assure<Type>(id).clear();
}
/*! @brief Discards all the events queued so far. */
void clear() noexcept {
for(auto &&cpool: pools.first()) {
cpool.second->clear();
}
}
/**
* @brief Delivers all the pending events of a given queue.
* @tparam Type Type of event to send.
* @param id Name used to map the event queue within the dispatcher.
*/
template<typename Type>
void update(const id_type id = type_hash<Type>::value()) {
assure<Type>(id).publish();
}
/*! @brief Delivers all the pending events. */
void update() const {
for(auto &&cpool: pools.first()) {
cpool.second->publish();
}
}
private:
compressed_pair<container_type, allocator_type> pools;
};
} // namespace entt
#endif

View File

@@ -1,181 +0,0 @@
#ifndef ENTT_SIGNAL_EMITTER_HPP
#define ENTT_SIGNAL_EMITTER_HPP
#include "../container/dense_map.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../stl/functional.hpp"
#include "../stl/type_traits.hpp"
#include "../stl/utility.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief General purpose event emitter.
*
* To create an emitter type, derived classes must inherit from the base as:
*
* @code{.cpp}
* struct my_emitter: emitter<my_emitter> {
* // ...
* }
* @endcode
*
* Handlers for the different events are created internally on the fly. It's not
* required to specify in advance the full list of accepted events.<br/>
* Moreover, whenever an event is published, an emitter also passes a reference
* to itself to its listeners.
*
* @tparam Derived Emitter type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Derived, typename Allocator>
class emitter {
using key_type = id_type;
using mapped_type = stl::function<void(void *)>;
using alloc_traits = stl::allocator_traits<Allocator>;
using container_allocator = alloc_traits::template rebind_alloc<stl::pair<const key_type, mapped_type>>;
using container_type = dense_map<key_type, mapped_type, stl::identity, stl::equal_to<>, container_allocator>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Unsigned integer type. */
using size_type = stl::size_t;
/*! @brief Default constructor. */
emitter()
: emitter{allocator_type{}} {}
/**
* @brief Constructs an emitter with a given allocator.
* @param allocator The allocator to use.
*/
explicit emitter(const allocator_type &allocator)
: handlers{allocator, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
emitter(const emitter &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
emitter(emitter &&other) noexcept
: handlers{stl::move(other.handlers)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
emitter(emitter &&other, const allocator_type &allocator)
: handlers{container_type{stl::move(other.handlers.first()), allocator}, allocator} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed");
}
/*! @brief Default destructor. */
virtual ~emitter() {
static_assert(stl::is_base_of_v<emitter<Derived, Allocator>, Derived>, "Invalid emitter type");
}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This emitter.
*/
emitter &operator=(const emitter &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This emitter.
*/
emitter &operator=(emitter &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed");
swap(other);
return *this;
}
/**
* @brief Exchanges the contents with those of a given emitter.
* @param other Emitter to exchange the content with.
*/
void swap(emitter &other) noexcept {
using stl::swap;
swap(handlers, other.handlers);
}
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return handlers.second();
}
/**
* @brief Publishes a given event.
* @tparam Type Type of event to trigger.
* @param value An instance of the given type of event.
*/
template<typename Type>
void publish(Type value) {
if(const auto id = type_id<Type>().hash(); handlers.first().contains(id)) {
handlers.first()[id](&value);
}
}
/**
* @brief Registers a listener with the event emitter.
* @tparam Type Type of event to which to connect the listener.
* @param func The listener to register.
*/
template<typename Type>
void on(stl::function<void(Type &, Derived &)> func) {
handlers.first().insert_or_assign(type_id<Type>().hash(), [func = stl::move(func), this](void *value) {
func(*static_cast<Type *>(value), static_cast<Derived &>(*this));
});
}
/**
* @brief Disconnects a listener from the event emitter.
* @tparam Type Type of event of the listener.
*/
template<typename Type>
void erase() {
handlers.first().erase(type_hash<stl::remove_cvref_t<Type>>::value());
}
/*! @brief Disconnects all the listeners. */
void clear() noexcept {
handlers.first().clear();
}
/**
* @brief Checks if there are listeners registered for the specific event.
* @tparam Type Type of event to test.
* @return True if there are no listeners registered, false otherwise.
*/
template<typename Type>
[[nodiscard]] bool contains() const {
return handlers.first().contains(type_hash<stl::remove_cvref_t<Type>>::value());
}
/**
* @brief Checks if there are listeners registered with the event emitter.
* @return True if there are no listeners registered, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return handlers.first().empty();
}
private:
compressed_pair<container_type, allocator_type> handlers;
};
} // namespace entt
#endif

View File

@@ -1,46 +0,0 @@
#ifndef ENTT_SIGNAL_FWD_HPP
#define ENTT_SIGNAL_FWD_HPP
#include "../stl/memory.hpp"
namespace entt {
template<typename>
class delegate;
template<typename = stl::allocator<void>>
class basic_dispatcher;
template<typename, typename = stl::allocator<void>>
class emitter;
class connection;
struct scoped_connection;
template<typename>
class sink;
template<typename Type, typename = stl::allocator<void>>
class sigh;
/*! @brief Alias declaration for the most common use case. */
using dispatcher = basic_dispatcher<>;
/*! @brief Disambiguation tag for constructors and the like. */
template<auto>
struct connect_arg_t {
/*! @brief Default constructor. */
explicit connect_arg_t() = default;
};
/**
* @brief Constant of type connect_arg_t used to disambiguate calls.
* @tparam Candidate Element to connect (likely a free or member function).
*/
template<auto Candidate>
inline constexpr connect_arg_t<Candidate> connect_arg{};
} // namespace entt
#endif

View File

@@ -1,573 +0,0 @@
#ifndef ENTT_SIGNAL_SIGH_HPP
#define ENTT_SIGNAL_SIGH_HPP
#include "../stl/cstddef.hpp"
#include "../stl/memory.hpp"
#include "../stl/type_traits.hpp"
#include "../stl/utility.hpp"
#include "../stl/vector.hpp"
#include "delegate.hpp"
#include "fwd.hpp"
namespace entt {
/**
* @brief Sink class.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error unless the template parameter is a function type.
*
* @tparam Type A valid signal handler type.
*/
template<typename Type>
class sink;
/**
* @brief Unmanaged signal handler.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error unless the template parameter is a function type.
*
* @tparam Type A valid function type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Allocator>
class sigh;
/**
* @brief Unmanaged signal handler.
*
* 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:
*
* * Creating signals to use later to notify a bunch of listeners.
* * Collecting results from a set of functions like in a voting system.
*
* @tparam Ret Return type of a function type.
* @tparam Args Types of arguments of a function type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Ret, typename... Args, typename Allocator>
class sigh<Ret(Args...), Allocator> {
friend class sink<sigh<Ret(Args...), Allocator>>;
using alloc_traits = stl::allocator_traits<Allocator>;
using delegate_type = delegate<Ret(Args...)>;
using container_type = stl::vector<delegate_type, typename alloc_traits::template rebind_alloc<delegate_type>>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Unsigned integer type. */
using size_type = stl::size_t;
/*! @brief Sink type. */
using sink_type = sink<sigh<Ret(Args...), Allocator>>;
/*! @brief Default constructor. */
sigh() noexcept(noexcept(allocator_type{}))
: sigh{allocator_type{}} {}
/**
* @brief Constructs a signal handler with a given allocator.
* @param allocator The allocator to use.
*/
explicit sigh(const allocator_type &allocator) noexcept
: calls{allocator} {}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
sigh(const sigh &other)
: calls{other.calls} {}
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
sigh(const sigh &other, const allocator_type &allocator)
: calls{other.calls, allocator} {}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
sigh(sigh &&other) noexcept
: calls{stl::move(other.calls)} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
sigh(sigh &&other, const allocator_type &allocator)
: calls{stl::move(other.calls), allocator} {}
/*! @brief Default destructor. */
~sigh() = default;
/**
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This signal handler.
*/
sigh &operator=(const sigh &other) {
calls = other.calls;
return *this;
}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This signal handler.
*/
sigh &operator=(sigh &&other) noexcept {
swap(other);
return *this;
}
/**
* @brief Exchanges the contents with those of a given signal handler.
* @param other Signal handler to exchange the content with.
*/
void swap(sigh &other) noexcept {
using stl::swap;
swap(calls, other.calls);
}
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return calls.get_allocator();
}
/**
* @brief Number of listeners connected to the signal.
* @return Number of listeners currently connected.
*/
[[nodiscard]] size_type size() const noexcept {
return calls.size();
}
/**
* @brief Returns false if at least a listener is connected to the signal.
* @return True if the signal has no listeners connected, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return calls.empty();
}
/**
* @brief Triggers a signal.
*
* All the listeners are notified. Order isn't guaranteed.
*
* @param args Arguments to use to invoke listeners.
*/
void publish(Args... args) const {
for(auto pos = calls.size(); pos; --pos) {
calls[pos - 1u](args...);
}
}
/**
* @brief Collects return values from the listeners.
*
* The collector must expose a call operator with the following properties:
*
* * The return type is either `void` or such that it's convertible to
* `bool`. In the second case, a true value will stop the iteration.
* * The list of parameters is empty if `Ret` is `void`, otherwise it
* contains a single element such that `Ret` is convertible to it.
*
* @tparam Func Type of collector to use, if any.
* @param func A valid function object.
* @param args Arguments to use to invoke listeners.
*/
template<typename Func>
void collect(Func func, Args... args) const {
for(auto pos = calls.size(); pos; --pos) {
if constexpr(stl::is_void_v<Ret> || !stl::is_invocable_v<Func, Ret>) {
calls[pos - 1u](args...);
if constexpr(stl::is_invocable_r_v<bool, Func>) {
if(func()) {
break;
}
} else {
func();
}
} else if constexpr(stl::is_invocable_r_v<bool, Func, Ret>) {
if(func(calls[pos - 1u](args...))) {
break;
}
} else {
func(calls[pos - 1u](args...));
}
}
}
private:
container_type calls;
};
/**
* @brief Connection class.
*
* Opaque object the aim of which is to allow users to release an already
* established connection without having to keep a reference to the signal or
* the sink that generated it.
*/
class connection {
template<typename>
friend class sink;
connection(delegate<void(void *)> fn, void *ref)
: disconnect{fn}, signal{ref} {}
public:
/*! @brief Default constructor. */
connection()
: signal{} {}
/**
* @brief Checks whether a connection is properly initialized.
* @return True if the connection is properly initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return static_cast<bool>(disconnect);
}
/*! @brief Breaks the connection. */
void release() {
if(disconnect) {
disconnect(signal);
disconnect.reset();
}
}
private:
delegate<void(void *)> disconnect;
void *signal;
};
/**
* @brief Scoped connection class.
*
* Opaque object the aim of which is to allow users to release an already
* established connection without having to keep a reference to the signal or
* the sink that generated it.<br/>
* A scoped connection automatically breaks the link between the two objects
* when it goes out of scope.
*/
struct scoped_connection {
/*! @brief Default constructor. */
scoped_connection() = default;
/**
* @brief Constructs a scoped connection from a basic connection.
* @param other A valid connection object.
*/
scoped_connection(const connection &other)
: conn{other} {}
/*! @brief Default copy constructor, deleted on purpose. */
scoped_connection(const scoped_connection &) = delete;
/**
* @brief Move constructor.
* @param other The scoped connection to move from.
*/
scoped_connection(scoped_connection &&other) noexcept
: conn{stl::exchange(other.conn, {})} {}
/*! @brief Automatically breaks the link on destruction. */
~scoped_connection() {
conn.release();
}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This scoped connection.
*/
scoped_connection &operator=(const scoped_connection &) = delete;
/**
* @brief Move assignment operator.
* @param other The scoped connection to move from.
* @return This scoped connection.
*/
scoped_connection &operator=(scoped_connection &&other) noexcept {
conn = stl::exchange(other.conn, {});
return *this;
}
/**
* @brief Acquires a connection.
* @param other The connection object to acquire.
* @return This scoped connection.
*/
scoped_connection &operator=(connection other) {
conn = other;
return *this;
}
/**
* @brief Checks whether a scoped connection is properly initialized.
* @return True if the connection is properly initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return static_cast<bool>(conn);
}
/*! @brief Breaks the connection. */
void release() {
conn.release();
}
private:
connection conn;
};
/**
* @brief Sink class.
*
* A sink is used to connect listeners to signals and to disconnect them.<br/>
* The function type for a listener is the one of the signal to which it
* belongs.
*
* The clear separation between a signal and a sink permits to store the former
* as private data member without exposing the publish functionality to the
* users of the class.
*
* @warning
* Lifetime of a sink must not overcome that of the signal to which it refers.
* In any other case, attempting to use a sink results in undefined behavior.
*
* @tparam Ret Return type of a function type.
* @tparam Args Types of arguments of a function type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Ret, typename... Args, typename Allocator>
class sink<sigh<Ret(Args...), Allocator>> {
using signal_type = sigh<Ret(Args...), Allocator>;
using delegate_type = signal_type::delegate_type;
using difference_type = signal_type::container_type::difference_type;
template<auto Candidate, typename Type>
static void release(Type value_or_instance, void *signal) {
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(value_or_instance);
}
template<auto Candidate>
static void release(void *signal) {
sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>();
}
template<typename Func>
void disconnect_if(Func callback) {
auto &ref = signal_or_assert();
for(auto pos = ref.calls.size(); pos; --pos) {
if(auto &elem = ref.calls[pos - 1u]; callback(elem)) {
elem = stl::move(ref.calls.back());
ref.calls.pop_back();
}
}
}
[[nodiscard]] auto &signal_or_assert() const noexcept {
ENTT_ASSERT(signal != nullptr, "Invalid pointer to signal");
return *signal;
}
public:
/*! @brief Constructs an invalid sink. */
sink() noexcept
: signal{} {}
/**
* @brief Constructs a sink that is allowed to modify a given signal.
* @param ref A valid reference to a signal object.
*/
sink(sigh<Ret(Args...), Allocator> &ref) noexcept
: signal{&ref} {}
/**
* @brief Returns false if at least a listener is connected to the sink.
* @return True if the sink has no listeners connected, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return signal_or_assert().calls.empty();
}
/**
* @brief Connects a free function or an unbound member to a signal.
* @tparam Candidate Function or member to connect to the signal.
* @return A properly initialized connection object.
*/
template<auto Candidate>
connection connect() {
disconnect<Candidate>();
delegate_type call{};
call.template connect<Candidate>();
signal_or_assert().calls.push_back(stl::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<Candidate>>();
return {conn, signal};
}
/**
* @brief Connects a free function with payload or a bound member to a
* signal.
*
* The signal isn't responsible for the connected object or the payload.
* Users must always guarantee that the lifetime of the instance overcomes
* the one of the signal.<br/>
* When used to connect a free function with payload, its signature must be
* such that the instance is the first argument before the ones used to
* define the signal itself.
*
* @tparam Candidate Function or member to connect to the signal.
* @tparam Type Type of class or type of payload.
* @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) {
disconnect<Candidate>(value_or_instance);
delegate_type call{};
call.template connect<Candidate>(value_or_instance);
signal_or_assert().calls.push_back(stl::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<Candidate, Type &>>(value_or_instance);
return {conn, signal};
}
/**
* @brief Connects a free function with payload or a bound member to a
* signal.
*
* @sa connect(Type &)
*
* @tparam Candidate Function or member 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.
* @return A properly initialized connection object.
*/
template<auto Candidate, typename Type>
connection connect(Type *value_or_instance) {
disconnect<Candidate>(value_or_instance);
delegate_type call{};
call.template connect<Candidate>(value_or_instance);
signal_or_assert().calls.push_back(stl::move(call));
delegate<void(void *)> conn{};
conn.template connect<&release<Candidate, Type *>>(value_or_instance);
return {conn, signal};
}
/**
* @brief Disconnects a free function or an unbound member from a signal.
* @tparam Candidate Function or member to disconnect from the signal.
*/
template<auto Candidate>
void disconnect() {
delegate_type call{};
call.template connect<Candidate>();
disconnect_if([&call](const auto &elem) { return elem == call; });
}
/**
* @brief Disconnects a free function with payload or a bound member from a
* signal.
*
* The signal isn't responsible for the connected object or the payload.
* Users must always guarantee that the lifetime of the instance overcomes
* the one of the signal.<br/>
* When used to connect a free function with payload, its signature must be
* such that the instance is the first argument before the ones used to
* define the signal itself.
*
* @tparam Candidate Function or member to disconnect from the signal.
* @tparam Type Type of class or type of payload, if any.
* @param value_or_instance A valid reference that fits the purpose.
*/
template<auto Candidate, typename Type>
void disconnect(Type &value_or_instance) {
delegate_type call{};
call.template connect<Candidate>(value_or_instance);
disconnect_if([&call](const auto &elem) { return elem == call; });
}
/**
* @brief Disconnects a free function with payload or a bound member from a
* signal.
*
* @sa disconnect(Type &)
*
* @tparam Candidate Function or member to disconnect from the signal.
* @tparam Type Type of class or type of payload, if any.
* @param value_or_instance A valid pointer that fits the purpose.
*/
template<auto Candidate, typename Type>
void disconnect(Type *value_or_instance) {
delegate_type call{};
call.template connect<Candidate>(value_or_instance);
disconnect_if([&call](const auto &elem) { return elem == call; });
}
/**
* @brief Disconnects free functions with payload or bound members from a
* signal.
* @param value_or_instance A valid object that fits the purpose.
*/
void disconnect(const void *value_or_instance) {
ENTT_ASSERT(value_or_instance != nullptr, "Invalid value or instance");
disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; });
}
/*! @brief Disconnects all the listeners from a signal. */
void disconnect() {
signal_or_assert().calls.clear();
}
/**
* @brief Returns true if a sink is correctly initialized, false otherwise.
* @return True if a sink is correctly initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return signal != nullptr;
}
private:
signal_type *signal;
};
/**
* @brief Deduction guide.
*
* It allows to deduce the signal handler type of a sink directly from the
* signal it refers to.
*
* @tparam Ret Return type of a function type.
* @tparam Args Types of arguments of a function type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Ret, typename... Args, typename Allocator>
sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>;
} // namespace entt
#endif