sigh: allocator support
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
#ifndef ENTT_SIGNAL_FWD_HPP
|
||||
#define ENTT_SIGNAL_FWD_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<typename>
|
||||
@@ -18,7 +20,7 @@ struct scoped_connection;
|
||||
template<typename>
|
||||
class sink;
|
||||
|
||||
template<typename>
|
||||
template<typename Type, typename = std::allocator<delegate<Type>>>
|
||||
class sigh;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -19,9 +19,9 @@ namespace entt {
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error unless the template parameter is a function type.
|
||||
*
|
||||
* @tparam Function A valid function type.
|
||||
* @tparam Type A valid signal handler type.
|
||||
*/
|
||||
template<typename Function>
|
||||
template<typename Type>
|
||||
class sink;
|
||||
|
||||
/**
|
||||
@@ -30,9 +30,10 @@ class sink;
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error unless the template parameter is a function type.
|
||||
*
|
||||
* @tparam Function A valid function type.
|
||||
* @tparam Type A valid function type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Function>
|
||||
template<typename Type, typename Allocator>
|
||||
class sigh;
|
||||
|
||||
/**
|
||||
@@ -49,17 +50,104 @@ class sigh;
|
||||
*
|
||||
* @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>
|
||||
class sigh<Ret(Args...)> {
|
||||
template<typename Ret, typename... Args, typename Allocator>
|
||||
class sigh<Ret(Args...), Allocator> {
|
||||
/*! @brief A sink is allowed to modify a signal. */
|
||||
friend class sink<Ret(Args...)>;
|
||||
friend class sink<sigh<Ret(Args...), Allocator>>;
|
||||
|
||||
using allocator_traits = std::allocator_traits<Allocator>;
|
||||
using alloc = typename allocator_traits::template rebind_alloc<delegate<Ret(Args...)>>;
|
||||
using alloc_traits = typename std::allocator_traits<alloc>;
|
||||
|
||||
using container_type = std::vector<delegate<Ret(Args...)>, alloc>;
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Sink type. */
|
||||
using sink_type = sink<Ret(Args...)>;
|
||||
using sink_type = sink<sigh<Ret(Args...), Allocator>>;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
sigh()
|
||||
: sigh{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a signal handler with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit sigh(const allocator_type &allocator)
|
||||
: calls{allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Default 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 Default move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
sigh(sigh &&other) ENTT_NOEXCEPT
|
||||
: calls{std::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) ENTT_NOEXCEPT
|
||||
: calls{std::move(other.calls), allocator} {}
|
||||
|
||||
/**
|
||||
* @brief Default 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 Default move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This signal handler.
|
||||
*/
|
||||
sigh &operator=(sigh &&other) ENTT_NOEXCEPT {
|
||||
calls = std::move(other.calls);
|
||||
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) {
|
||||
using std::swap;
|
||||
swap(calls, other.calls);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
|
||||
return calls.get_allocator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Instance type when it comes to connecting member functions.
|
||||
@@ -133,7 +221,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<delegate<Ret(Args...)>> calls;
|
||||
container_type calls;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -271,10 +359,11 @@ private:
|
||||
*
|
||||
* @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>
|
||||
class sink<Ret(Args...)> {
|
||||
using signal_type = sigh<Ret(Args...)>;
|
||||
template<typename Ret, typename... Args, typename Allocator>
|
||||
class sink<sigh<Ret(Args...), Allocator>> {
|
||||
using signal_type = sigh<Ret(Args...), Allocator>;
|
||||
using difference_type = typename std::iterator_traits<typename decltype(signal_type::calls)::iterator>::difference_type;
|
||||
|
||||
template<auto Candidate, typename Type>
|
||||
@@ -292,7 +381,7 @@ public:
|
||||
* @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...)> &ref) ENTT_NOEXCEPT
|
||||
sink(sigh<Ret(Args...), Allocator> &ref) ENTT_NOEXCEPT
|
||||
: offset{},
|
||||
signal{&ref} {}
|
||||
|
||||
@@ -507,14 +596,15 @@ private:
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
*
|
||||
* It allows to deduce the function type of a sink directly from the signal it
|
||||
* refers to.
|
||||
* 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>
|
||||
sink(sigh<Ret(Args...)> &) -> sink<Ret(Args...)>;
|
||||
template<typename Ret, typename... Args, typename Allocator>
|
||||
sink(sigh<Ret(Args...), Allocator> &) -> sink<sigh<Ret(Args...), Allocator>>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef ENTT_ENTITY_THROWING_ALLOCATOR_HPP
|
||||
#define ENTT_ENTITY_THROWING_ALLOCATOR_HPP
|
||||
#ifndef ENTT_COMMON_THROWING_ALLOCATOR_HPP
|
||||
#define ENTT_COMMON_THROWING_ALLOCATOR_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
@@ -1,19 +1,19 @@
|
||||
#ifndef ENTT_ENTITY_THROWING_COMPONENT_HPP
|
||||
#define ENTT_ENTITY_THROWING_COMPONENT_HPP
|
||||
#ifndef ENTT_COMMON_THROWING_TYPE_HPP
|
||||
#define ENTT_COMMON_THROWING_TYPE_HPP
|
||||
|
||||
namespace test {
|
||||
|
||||
class throwing_component {
|
||||
class throwing_type {
|
||||
struct test_exception {};
|
||||
|
||||
public:
|
||||
using exception_type = test_exception;
|
||||
static constexpr auto moved_from_value = -1;
|
||||
|
||||
throwing_component(int value)
|
||||
throwing_type(int value)
|
||||
: data{value} {}
|
||||
|
||||
throwing_component(const throwing_component &other)
|
||||
throwing_type(const throwing_type &other)
|
||||
: data{other.data} {
|
||||
if(data == trigger_on_value) {
|
||||
data = moved_from_value;
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
throwing_component &operator=(const throwing_component &other) {
|
||||
throwing_type &operator=(const throwing_type &other) {
|
||||
if(other.data == trigger_on_value) {
|
||||
data = moved_from_value;
|
||||
throw exception_type{};
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/entity.hpp>
|
||||
#include <entt/entity/sparse_set.hpp>
|
||||
#include "throwing_allocator.hpp"
|
||||
#include "../common/throwing_allocator.hpp"
|
||||
|
||||
struct empty_type {};
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/component.hpp>
|
||||
#include <entt/entity/storage.hpp>
|
||||
#include "throwing_allocator.hpp"
|
||||
#include "throwing_component.hpp"
|
||||
#include "../common/throwing_allocator.hpp"
|
||||
#include "../common/throwing_type.hpp"
|
||||
|
||||
struct empty_stable_type {};
|
||||
|
||||
@@ -1480,28 +1480,28 @@ TEST(Storage, ThrowingAllocator) {
|
||||
}
|
||||
|
||||
TEST(Storage, ThrowingComponent) {
|
||||
entt::storage<test::throwing_component> pool;
|
||||
test::throwing_component::trigger_on_value = 42;
|
||||
entt::storage<test::throwing_type> pool;
|
||||
test::throwing_type::trigger_on_value = 42;
|
||||
|
||||
// strong exception safety
|
||||
ASSERT_THROW(pool.emplace(entt::entity{0}, test::throwing_component{42}), typename test::throwing_component::exception_type);
|
||||
ASSERT_THROW(pool.emplace(entt::entity{0}, test::throwing_type{42}), typename test::throwing_type::exception_type);
|
||||
ASSERT_TRUE(pool.empty());
|
||||
|
||||
const entt::entity entities[2u]{entt::entity{42}, entt::entity{1}};
|
||||
const test::throwing_component components[2u]{42, 1};
|
||||
const test::throwing_type components[2u]{42, 1};
|
||||
|
||||
// basic exception safety
|
||||
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), test::throwing_component{42}), typename test::throwing_component::exception_type);
|
||||
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), test::throwing_type{42}), typename test::throwing_type::exception_type);
|
||||
ASSERT_EQ(pool.size(), 0u);
|
||||
ASSERT_FALSE(pool.contains(entt::entity{1}));
|
||||
|
||||
// basic exception safety
|
||||
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), typename test::throwing_component::exception_type);
|
||||
ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), typename test::throwing_type::exception_type);
|
||||
ASSERT_EQ(pool.size(), 0u);
|
||||
ASSERT_FALSE(pool.contains(entt::entity{1}));
|
||||
|
||||
// basic exception safety
|
||||
ASSERT_THROW(pool.insert(std::rbegin(entities), std::rend(entities), std::rbegin(components)), typename test::throwing_component::exception_type);
|
||||
ASSERT_THROW(pool.insert(std::rbegin(entities), std::rend(entities), std::rbegin(components)), typename test::throwing_type::exception_type);
|
||||
ASSERT_EQ(pool.size(), 1u);
|
||||
ASSERT_TRUE(pool.contains(entt::entity{1}));
|
||||
ASSERT_EQ(pool.get(entt::entity{1}), 1);
|
||||
@@ -1511,7 +1511,7 @@ TEST(Storage, ThrowingComponent) {
|
||||
pool.emplace(entt::entity{42}, 42);
|
||||
|
||||
// basic exception safety
|
||||
ASSERT_THROW(pool.erase(entt::entity{1}), typename test::throwing_component::exception_type);
|
||||
ASSERT_THROW(pool.erase(entt::entity{1}), typename test::throwing_type::exception_type);
|
||||
ASSERT_EQ(pool.size(), 2u);
|
||||
ASSERT_TRUE(pool.contains(entt::entity{42}));
|
||||
ASSERT_TRUE(pool.contains(entt::entity{1}));
|
||||
@@ -1519,9 +1519,9 @@ TEST(Storage, ThrowingComponent) {
|
||||
ASSERT_EQ(pool.at(1u), entt::entity{42});
|
||||
ASSERT_EQ(pool.get(entt::entity{42}), 42);
|
||||
// the element may have been moved but it's still there
|
||||
ASSERT_EQ(pool.get(entt::entity{1}), test::throwing_component::moved_from_value);
|
||||
ASSERT_EQ(pool.get(entt::entity{1}), test::throwing_type::moved_from_value);
|
||||
|
||||
test::throwing_component::trigger_on_value = 99;
|
||||
test::throwing_type::trigger_on_value = 99;
|
||||
pool.erase(entt::entity{1});
|
||||
|
||||
ASSERT_EQ(pool.size(), 1u);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/signal/sigh.hpp>
|
||||
#include "../common/throwing_allocator.hpp"
|
||||
#include "../common/throwing_type.hpp"
|
||||
|
||||
struct sigh_listener {
|
||||
static void f(int &v) {
|
||||
@@ -122,7 +124,7 @@ TEST_F(SigH, Swap) {
|
||||
ASSERT_FALSE(sigh1.empty());
|
||||
ASSERT_TRUE(sigh2.empty());
|
||||
|
||||
std::swap(sigh1, sigh2);
|
||||
sigh1.swap(sigh2);
|
||||
|
||||
ASSERT_TRUE(sink1.empty());
|
||||
ASSERT_FALSE(sink2.empty());
|
||||
@@ -530,3 +532,55 @@ TEST_F(SigH, UnboundMemberFunction) {
|
||||
|
||||
ASSERT_TRUE(listener.k);
|
||||
}
|
||||
|
||||
TEST_F(SigH, CustomAllocator) {
|
||||
test::throwing_allocator<entt::delegate<void(int)>> allocator;
|
||||
|
||||
auto test = [&](auto curr) {
|
||||
ASSERT_EQ(curr.get_allocator(), allocator);
|
||||
ASSERT_TRUE(curr.empty());
|
||||
|
||||
entt::sink sink{curr};
|
||||
sigh_listener listener;
|
||||
sink.template connect<&sigh_listener::i>(listener);
|
||||
|
||||
decltype(curr) copy{curr, allocator};
|
||||
sink.disconnect(listener);
|
||||
|
||||
ASSERT_TRUE(curr.empty());
|
||||
ASSERT_FALSE(copy.empty());
|
||||
|
||||
curr = copy;
|
||||
|
||||
ASSERT_FALSE(curr.empty());
|
||||
ASSERT_FALSE(copy.empty());
|
||||
|
||||
decltype(curr) move{std::move(copy), allocator};
|
||||
|
||||
ASSERT_TRUE(copy.empty());
|
||||
ASSERT_FALSE(move.empty());
|
||||
|
||||
sink = entt::sink{move};
|
||||
sink.disconnect(&listener);
|
||||
|
||||
ASSERT_TRUE(copy.empty());
|
||||
ASSERT_TRUE(move.empty());
|
||||
|
||||
sink.template connect<&sigh_listener::i>(listener);
|
||||
copy.swap(move);
|
||||
|
||||
ASSERT_FALSE(copy.empty());
|
||||
ASSERT_TRUE(move.empty());
|
||||
|
||||
sink = entt::sink{copy};
|
||||
sink.disconnect();
|
||||
|
||||
ASSERT_TRUE(copy.empty());
|
||||
ASSERT_TRUE(move.empty());
|
||||
};
|
||||
|
||||
entt::sigh<void(int), decltype(allocator)> sigh{allocator};
|
||||
|
||||
test(sigh);
|
||||
test(std::move(sigh));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user