diff --git a/src/entt/resource/cache.hpp b/src/entt/resource/cache.hpp index 3da2be9bd..031373cc6 100644 --- a/src/entt/resource/cache.hpp +++ b/src/entt/resource/cache.hpp @@ -91,8 +91,6 @@ struct resource_cache { */ template resource_handle load(const id_type id, Args &&... args) { - static_assert(std::is_base_of_v, Loader>, "Invalid loader type"); - if(auto it = resources.find(id); it == resources.cend()) { if(auto handle = temp(std::forward(args)...); handle) { return (resources[id] = std::move(handle)); diff --git a/src/entt/resource/fwd.hpp b/src/entt/resource/fwd.hpp index d3c794625..8a2c9e901 100644 --- a/src/entt/resource/fwd.hpp +++ b/src/entt/resource/fwd.hpp @@ -10,7 +10,7 @@ struct resource_cache; template -struct resource_handle; +class resource_handle; template diff --git a/src/entt/resource/handle.hpp b/src/entt/resource/handle.hpp index a82936b1d..58c5ffac1 100644 --- a/src/entt/resource/handle.hpp +++ b/src/entt/resource/handle.hpp @@ -3,6 +3,7 @@ #include +#include #include #include "../config/config.h" #include "fwd.hpp" @@ -24,7 +25,12 @@ namespace entt { * @tparam Resource Type of resource managed by a handle. */ template -struct resource_handle { +class resource_handle { + /*! @brief Resource handles are friends with each other. */ + template + friend class resource_handle; + +public: /*! @brief Default constructor. */ resource_handle() ENTT_NOEXCEPT = default; @@ -36,6 +42,74 @@ struct resource_handle { : resource{std::move(res)} {} + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + resource_handle(const resource_handle &other) ENTT_NOEXCEPT = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + resource_handle(resource_handle &&other) ENTT_NOEXCEPT = default; + + /** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ + template && std::is_base_of_v>> + resource_handle(const resource_handle &other) ENTT_NOEXCEPT + : resource{other.resource} + {} + + /** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ + template && std::is_base_of_v>> + resource_handle(resource_handle &&other) ENTT_NOEXCEPT + : resource{std::move(other.resource)} + {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + */ + resource_handle & operator=(const resource_handle &other) ENTT_NOEXCEPT = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + */ + resource_handle & operator=(resource_handle &&other) ENTT_NOEXCEPT = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ + template + std::enable_if_t && std::is_base_of_v, resource_handle &> + operator=(const resource_handle &other) ENTT_NOEXCEPT { + resource = other.resource; + return *this; + } + + /** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ + template + std::enable_if_t && std::is_base_of_v, resource_handle &> + operator=(resource_handle &&other) ENTT_NOEXCEPT { + resource = std::move(other.resource); + return *this; + } + /** * @brief Gets a reference to the managed resource. * diff --git a/src/entt/resource/loader.hpp b/src/entt/resource/loader.hpp index d6b4ead29..fa39d55bc 100644 --- a/src/entt/resource/loader.hpp +++ b/src/entt/resource/loader.hpp @@ -44,7 +44,8 @@ namespace entt { template class resource_loader { /*! @brief Resource loaders are friends of their caches. */ - friend struct resource_cache; + template + friend struct resource_cache; /** * @brief Loads the resource and returns it. diff --git a/test/entt/resource/resource.cpp b/test/entt/resource/resource.cpp index 57685712f..d7384c8a9 100644 --- a/test/entt/resource/resource.cpp +++ b/test/entt/resource/resource.cpp @@ -4,15 +4,20 @@ #include struct resource { int value; }; +struct derived_resource: resource {}; -struct loader: entt::resource_loader { - std::shared_ptr load(int value) const { - return std::shared_ptr(new resource{ value }); +template +struct loader: entt::resource_loader, Resource> { + std::shared_ptr load(int value) const { + auto res = std::shared_ptr(new Resource); + res->value = value; + return res; } }; -struct broken_loader: entt::resource_loader { - std::shared_ptr load(int) const { +template +struct broken_loader: entt::resource_loader, Resource> { + std::shared_ptr load(int) const { return nullptr; } }; @@ -28,16 +33,16 @@ TEST(Resource, Functionalities) { ASSERT_FALSE(cache.contains(hs1)); ASSERT_FALSE(cache.contains(hs2)); - ASSERT_FALSE(cache.load(hs1, 42)); - ASSERT_FALSE(cache.reload(hs1, 42)); + ASSERT_FALSE(cache.load>(hs1, 42)); + ASSERT_FALSE(cache.reload>(hs1, 42)); ASSERT_EQ(cache.size(), 0u); ASSERT_TRUE(cache.empty()); ASSERT_FALSE(cache.contains(hs1)); ASSERT_FALSE(cache.contains(hs2)); - ASSERT_TRUE(cache.load(hs1, 42)); - ASSERT_TRUE(cache.reload(hs1, 42)); + ASSERT_TRUE(cache.load>(hs1, 42)); + ASSERT_TRUE(cache.reload>(hs1, 42)); ASSERT_NE(cache.size(), 0u); ASSERT_FALSE(cache.empty()); @@ -45,8 +50,8 @@ TEST(Resource, Functionalities) { ASSERT_FALSE(cache.contains(hs2)); ASSERT_EQ((*cache.handle(hs1)).value, 42); - ASSERT_TRUE(cache.load(hs1, 42)); - ASSERT_TRUE(cache.load(hs2, 42)); + ASSERT_TRUE(cache.load>(hs1, 42)); + ASSERT_TRUE(cache.load>(hs2, 42)); ASSERT_NE(cache.size(), 0u); ASSERT_FALSE(cache.empty()); @@ -61,7 +66,7 @@ TEST(Resource, Functionalities) { ASSERT_TRUE(cache.contains(hs2)); ASSERT_EQ(cache.handle(hs2)->value, 42); - ASSERT_TRUE(cache.load(hs1, 42)); + ASSERT_TRUE(cache.load>(hs1, 42)); ASSERT_NO_FATAL_FAILURE(cache.clear()); ASSERT_EQ(cache.size(), 0u); @@ -69,7 +74,7 @@ TEST(Resource, Functionalities) { ASSERT_FALSE(cache.contains(hs1)); ASSERT_FALSE(cache.contains(hs2)); - ASSERT_TRUE(cache.load(hs1, 42)); + ASSERT_TRUE(cache.load>(hs1, 42)); ASSERT_NE(cache.size(), 0u); ASSERT_FALSE(cache.empty()); @@ -83,7 +88,7 @@ TEST(Resource, Functionalities) { ASSERT_EQ(cache.size(), 0u); ASSERT_TRUE(cache.empty()); - ASSERT_TRUE(cache.temp(42)); + ASSERT_TRUE(cache.temp>(42)); ASSERT_TRUE(cache.empty()); ASSERT_FALSE(entt::resource_handle{}); @@ -97,7 +102,7 @@ TEST(Resource, MutableHandle) { entt::resource_cache cache; constexpr auto hs = entt::hashed_string{"res"}; - auto handle = cache.load(hs, 0); + auto handle = cache.load>(hs, 0); ASSERT_TRUE(handle); @@ -109,11 +114,50 @@ TEST(Resource, MutableHandle) { ASSERT_EQ(cache.handle(hs)->value, 4); } +TEST(Resource, HandleCast) { + using namespace entt::literals; + + entt::resource_cache cache; + auto handle = cache.load>("resource"_hs, 0); + + auto resource = std::make_shared(); + entt::resource_handle other{resource}; + + ASSERT_TRUE(handle); + ASSERT_TRUE(other); + ASSERT_NE(&*handle, &*other); + ASSERT_EQ(resource.use_count(), 2u); + + auto temp = std::move(handle); + handle = other; + + ASSERT_TRUE(handle); + ASSERT_TRUE(other); + ASSERT_TRUE(temp); + ASSERT_EQ(&*handle, &*other); + ASSERT_EQ(resource.use_count(), 3u); + + temp = std::move(other); + + ASSERT_TRUE(handle); + ASSERT_FALSE(other); + ASSERT_TRUE(temp); + ASSERT_EQ(&*handle, &*temp); + ASSERT_EQ(resource.use_count(), 3u); + + temp = handle = {}; + + ASSERT_FALSE(handle); + ASSERT_FALSE(other); + ASSERT_FALSE(temp); + ASSERT_EQ(resource.use_count(), 1u); +} + TEST(Resource, Each) { using namespace entt::literals; entt::resource_cache cache; - cache.load("resource"_hs, 0); + cache.load>("resource"_hs, 0); cache.each([](entt::resource_handle res) { ++res->value;