diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 934d107e4..a21135a88 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -27,7 +27,7 @@ jobs: working-directory: build run: | sudo apt install lcov - lcov -c -d . -o coverage.info + lcov -c -d . -o coverage.info --ignore-errors gcov,gcov,mismatch,mismatch lcov -l coverage.info - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e9e73d42..14adc8915 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,76 +222,82 @@ if(ENTT_HAS_LIBCPP) target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++) endif() -# Install pkg-config file +# Install EnTT and all related files -include(JoinPaths) +option(ENTT_INSTALL "Install EnTT and all related files." OFF) -set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc) +if(ENTT_INSTALL) + # Install pkg-config file -join_paths(EnTT_PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") + include(JoinPaths) -configure_file( - ${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in - ${EnTT_PKGCONFIG} - @ONLY -) + set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc) -install( - FILES ${EnTT_PKGCONFIG} - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig -) + join_paths(EnTT_PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") -# Install EnTT + configure_file( + ${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in + ${EnTT_PKGCONFIG} + @ONLY + ) -include(CMakePackageConfigHelpers) + install( + FILES ${EnTT_PKGCONFIG} + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + ) -install( - TARGETS EnTT - EXPORT EnTTTargets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) + # Install EnTT -write_basic_package_version_file( - EnTTConfigVersion.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY AnyNewerVersion -) + include(CMakePackageConfigHelpers) -configure_package_config_file( - ${EnTT_SOURCE_DIR}/cmake/in/EnTTConfig.cmake.in - EnTTConfig.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake -) + install( + TARGETS EnTT + EXPORT EnTTTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) -export( - EXPORT EnTTTargets - FILE ${CMAKE_CURRENT_BINARY_DIR}/EnTTTargets.cmake - NAMESPACE EnTT:: -) + write_basic_package_version_file( + EnTTConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion + ) -install( - EXPORT EnTTTargets - FILE EnTTTargets.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake - NAMESPACE EnTT:: -) + configure_package_config_file( + ${EnTT_SOURCE_DIR}/cmake/in/EnTTConfig.cmake.in + EnTTConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake + ) -install( - FILES - ${PROJECT_BINARY_DIR}/EnTTConfig.cmake - ${PROJECT_BINARY_DIR}/EnTTConfigVersion.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake -) + export( + EXPORT EnTTTargets + FILE ${CMAKE_CURRENT_BINARY_DIR}/EnTTTargets.cmake + NAMESPACE EnTT:: + ) -install( - DIRECTORY src/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING - PATTERN "*.h" - PATTERN "*.hpp" -) + install( + EXPORT EnTTTargets + FILE EnTTTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake + NAMESPACE EnTT:: + ) -export(PACKAGE EnTT) + install( + FILES + ${PROJECT_BINARY_DIR}/EnTTConfig.cmake + ${PROJECT_BINARY_DIR}/EnTTConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake + ) + + install( + DIRECTORY src/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.hpp" + ) + + export(PACKAGE EnTT) +endif() # Tests diff --git a/TODO b/TODO index 94315f168..3315f90c6 100644 --- a/TODO +++ b/TODO @@ -42,4 +42,3 @@ TODO: * sparse_set shrink_to_fit argument for sparse array shrink policy (none, empty, deep, whatever) * any cdynamic to support const ownership construction * track meta context on meta elements -* safer meta_data/meta_func (no blind indirections) diff --git a/docs/md/entity.md b/docs/md/entity.md index 9fb5419ff..047f5c04b 100644 --- a/docs/md/entity.md +++ b/docs/md/entity.md @@ -1833,23 +1833,26 @@ the `my_type` component, regardless of what other components they have. ### View pack -Views are combined with each other to create new and more specific queries.
-The type returned when combining multiple views together is itself a view, more -in general a multi component one. +Views are combined with storage objects and with each other to create new, more +specific _queries_.
+The type returned when combining multiple elements together is itself a view, +more in general a multi component one. -Combining different views tries to mimic C++20 ranges: +Combining different elements tries to mimic C++20 ranges: ```cpp auto view = registry.view(); auto other = registry.view(); +const auto &storage = registry.storage(); -auto pack = view | other; +auto pack = view | other | renderable; ``` The constness of the types is preserved and their order depends on the order in -which the views are combined. For example, the pack above returns an instance of -`position` first and then one of `velocity`.
-Since combining views generates views, a chain can be of arbitrary length and +which the views are combined. For example, the above _pack_ first returns an +instance of `position`, then one of `velocity`, and finally one of +`renderable`.
+Since combining elements generates views, a chain can be of arbitrary length and the above type order rules apply sequentially. ### Iteration order diff --git a/src/entt/core/any.hpp b/src/entt/core/any.hpp index 2c1923160..f28f16b4c 100644 --- a/src/entt/core/any.hpp +++ b/src/entt/core/any.hpp @@ -249,12 +249,14 @@ class basic_any { /** * @brief Move assignment operator. + * + * @warning + * Self-moving puts objects in a safe but unspecified state. + * * @param other The instance to move from. * @return This any object. */ basic_any &operator=(basic_any &&other) noexcept { - ENTT_ASSERT(this != &other, "Self move assignment"); - reset(); if(other.mode == any_policy::embedded) { diff --git a/src/entt/core/tuple.hpp b/src/entt/core/tuple.hpp index 1a2dc4e1b..4f33b8862 100644 --- a/src/entt/core/tuple.hpp +++ b/src/entt/core/tuple.hpp @@ -7,25 +7,20 @@ namespace entt { -/*! @cond TURN_OFF_DOXYGEN */ -namespace internal { - -template -struct is_tuple_impl: std::false_type {}; - -template -struct is_tuple_impl>: std::true_type {}; - -} // namespace internal -/*! @endcond */ - /** * @brief Provides the member constant `value` to true if a given type is a * tuple, false otherwise. * @tparam Type The type to test. */ template -struct is_tuple: internal::is_tuple_impl> {}; +struct is_tuple: std::false_type {}; + +/** + * @copybrief is_tuple + * @tparam Args Tuple template arguments. + */ +template +struct is_tuple>: std::true_type {}; /** * @brief Helper variable template. diff --git a/src/entt/core/type_traits.hpp b/src/entt/core/type_traits.hpp index f6a9ea9de..104062977 100644 --- a/src/entt/core/type_traits.hpp +++ b/src/entt/core/type_traits.hpp @@ -772,18 +772,18 @@ template // NOLINTBEGIN(modernize-use-transparent-functors) if constexpr(std::is_array_v) { return false; - } else if constexpr(!is_iterator_v && has_value_type::value) { - if constexpr(std::is_same_v || dispatch_is_equality_comparable()) { - return maybe_equality_comparable(0); - } else { - return false; - } } else if constexpr(is_complete_v>>) { if constexpr(has_tuple_size_value::value) { return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(0); } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } } else { return maybe_equality_comparable(0); } diff --git a/src/entt/entity/storage.hpp b/src/entt/entity/storage.hpp index 608ce793f..1f0f6ff7d 100644 --- a/src/entt/entity/storage.hpp +++ b/src/entt/entity/storage.hpp @@ -298,6 +298,18 @@ class basic_storage: public basic_sparse_set, exclude_t, std::enable_if_t<(sizeof. return iterable{base_type::begin(), base_type::end()}; } + /** + * @brief Combines a view and a storage in _more specific_ view. + * @tparam OGet Type of storage to combine the view with. + * @param other The storage for the type to combine the view with. + * @return A more specific view. + */ + template + [[nodiscard]] std::enable_if_t, basic_view, exclude_t>> operator|(OGet &other) const noexcept { + return *this | basic_view, exclude_t<>>{other}; + } + /** * @brief Combines two views in a _more specific_ one. * @tparam OGet Element list of the view to combine with. @@ -1072,6 +1083,17 @@ class basic_view, exclude_t<>> } } + /** + * @brief Combines a view and a storage in _more specific_ view. + * @tparam OGet Type of storage to combine the view with. + * @param other The storage for the type to combine the view with. + * @return A more specific view. + */ + template + [[nodiscard]] std::enable_if_t, basic_view, exclude_t<>>> operator|(OGet &other) const noexcept { + return *this | basic_view, exclude_t<>>{other}; + } + /** * @brief Combines two views in a _more specific_ one. * @tparam OGet Element list of the view to combine with. diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index 26979bf35..016800861 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -218,8 +218,7 @@ class meta_any { public: /*! Default constructor. */ - meta_any() - : meta_any{meta_ctx_arg, locator::value_or()} {} + meta_any() = default; /** * @brief Context aware constructor. @@ -358,13 +357,15 @@ class meta_any { /** * @brief Move assignment operator. + * + * @warning + * Self-moving puts objects in a safe but unspecified state. + * * @param other The instance to move from. * @return This meta any object. */ meta_any &operator=(meta_any &&other) noexcept { - ENTT_ASSERT(this != &other, "Self move assignment"); - - release(); + reset(); storage = std::move(other.storage); ctx = other.ctx; node = std::exchange(other.node, internal::meta_type_node{}); @@ -640,7 +641,7 @@ class meta_any { private: any storage{}; - const meta_ctx *ctx{}; + const meta_ctx *ctx{&locator::value_or()}; internal::meta_type_node node{}; vtable_type *vtable{}; }; @@ -874,7 +875,7 @@ struct meta_data { template // NOLINTNEXTLINE(modernize-use-nodiscard) bool set(meta_handle instance, Type &&value) const { - return node.set && node.set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); + return (node.set != nullptr) && node.set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); } /** @@ -883,7 +884,7 @@ struct meta_data { * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(meta_handle instance) const { - return node.get(*ctx, meta_handle{*ctx, std::move(instance)}); + return (node.get != nullptr) ? node.get(*ctx, meta_handle{*ctx, std::move(instance)}) : meta_any{meta_ctx_arg, *ctx}; } /** @@ -916,7 +917,7 @@ struct meta_data { * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { - return (ctx != nullptr); + return (node.get != nullptr); } /** @@ -930,7 +931,7 @@ struct meta_data { private: internal::meta_data_node node{}; - const meta_ctx *ctx{}; + const meta_ctx *ctx{&locator::value_or()}; }; /** @@ -1005,7 +1006,7 @@ struct meta_func { * @return A wrapper containing the returned value, if any. */ meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { - return sz == arity() ? node.invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; + return ((node.invoke != nullptr) && (sz == arity())) ? node.invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; } /** @@ -1018,8 +1019,7 @@ struct meta_func { template // NOLINTNEXTLINE(modernize-use-nodiscard) meta_any invoke(meta_handle instance, Args &&...args) const { - std::array arguments{meta_any{*ctx, std::forward(args)}...}; - return invoke(std::move(instance), arguments.data(), sizeof...(Args)); + return invoke(std::move(instance), std::array{meta_any{*ctx, std::forward(args)}...}.data(), sizeof...(Args)); } /*! @copydoc meta_data::traits */ @@ -1038,7 +1038,7 @@ struct meta_func { * @return The next overload of the given function, if any. */ [[nodiscard]] meta_func next() const { - return node.next ? meta_func{*ctx, *node.next} : meta_func{}; + return (node.next != nullptr) ? meta_func{*ctx, *node.next} : meta_func{}; } /** @@ -1046,7 +1046,7 @@ struct meta_func { * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { - return (ctx != nullptr); + return (node.invoke != nullptr); } /*! @copydoc meta_data::operator== */ @@ -1056,7 +1056,7 @@ struct meta_func { private: internal::meta_func_node node{}; - const meta_ctx *ctx{}; + const meta_ctx *ctx{&locator::value_or()}; }; /** @@ -1153,7 +1153,7 @@ class meta_type { * @return The type info object of the underlying type. */ [[nodiscard]] const type_info &info() const noexcept { - return *node.info; + return node.info ? *node.info : type_id(); } /** @@ -1235,7 +1235,7 @@ class meta_type { * doesn't refer to a pointer type. */ [[nodiscard]] meta_type remove_pointer() const noexcept { - return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; + return (node.remove_pointer != nullptr) ? meta_type{*ctx, node.remove_pointer(internal::meta_context::from(*ctx))} : *this; } /** @@ -1391,8 +1391,7 @@ class meta_type { */ template [[nodiscard]] meta_any construct(Args &&...args) const { - std::array arguments{meta_any{*ctx, std::forward(args)}...}; - return construct(arguments.data(), sizeof...(Args)); + return construct(std::array{meta_any{*ctx, std::forward(args)}...}.data(), sizeof...(Args)); } /** @@ -1452,8 +1451,7 @@ class meta_type { template // NOLINTNEXTLINE(modernize-use-nodiscard) meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { - std::array arguments{meta_any{*ctx, std::forward(args)}...}; - return invoke(id, std::move(instance), arguments.data(), sizeof...(Args)); + return invoke(id, std::move(instance), std::array{meta_any{*ctx, std::forward(args)}...}.data(), sizeof...(Args)); } /** @@ -1498,7 +1496,7 @@ class meta_type { * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { - return (ctx != nullptr); + return (node.info != nullptr); } /*! @copydoc meta_data::operator== */ @@ -1508,7 +1506,7 @@ class meta_type { private: internal::meta_type_node node{}; - const meta_ctx *ctx{}; + const meta_ctx *ctx{&locator::value_or()}; }; /** @@ -1550,7 +1548,7 @@ bool meta_any::set(const id_type id, Type &&value) { } [[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { - return internal::try_convert(internal::meta_context::from(*ctx), node, type.info(), type.is_arithmetic() || type.is_enum(), storage.data(), [this, &type]([[maybe_unused]] const void *instance, auto &&...args) { + return internal::try_convert(internal::meta_context::from(*ctx), node, type.info(), type.is_arithmetic() || type.is_enum(), storage.data(), [this, &type]([[maybe_unused]] const void *instance, [[maybe_unused]] auto &&...args) { if constexpr((std::is_same_v>, internal::meta_type_node> || ...)) { return (args.from_void(*ctx, nullptr, instance), ...); } else if constexpr((std::is_same_v>, internal::meta_conv_node> || ...)) { @@ -1582,7 +1580,7 @@ inline bool meta_any::assign(meta_any &&other) { } [[nodiscard]] inline meta_type meta_data::type() const noexcept { - return meta_type{*ctx, node.type(internal::meta_context::from(*ctx))}; + return (node.type != nullptr) ? meta_type{*ctx, node.type(internal::meta_context::from(*ctx))} : meta_type{}; } [[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { @@ -1590,7 +1588,7 @@ inline bool meta_any::assign(meta_any &&other) { } [[nodiscard]] inline meta_type meta_func::ret() const noexcept { - return meta_type{*ctx, node.ret(internal::meta_context::from(*ctx))}; + return (node.ret != nullptr) ? meta_type{*ctx, node.ret(internal::meta_context::from(*ctx))} : meta_type{}; } [[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { diff --git a/test/entt/core/any.cpp b/test/entt/core/any.cpp index f2d3434d1..81660d1c4 100644 --- a/test/entt/core/any.cpp +++ b/test/entt/core/any.cpp @@ -282,11 +282,17 @@ TEST(Any, SBOMoveAssignment) { ASSERT_EQ(entt::any_cast(other), 2); } -ENTT_DEBUG_TEST(AnyDeathTest, SBOSelfMoveAssignment) { +TEST(AnyDeathTest, SBOSelfMoveAssignment) { entt::any any{2}; // avoid warnings due to self-assignment - ASSERT_DEATH(any = std::move(*&any), ""); + any = std::move(*&any); + + ASSERT_FALSE(any); + ASSERT_FALSE(any.owner()); + ASSERT_EQ(any.policy(), entt::any_policy::empty); + ASSERT_EQ(any.type(), entt::type_id()); + ASSERT_EQ(any.data(), nullptr); } TEST(Any, SBODirectAssignment) { @@ -600,12 +606,18 @@ TEST(Any, NoSBOMoveAssignment) { ASSERT_EQ(entt::any_cast(other), instance); } -ENTT_DEBUG_TEST(AnyDeathTest, NoSBOSelfMoveAssignment) { +TEST(AnyDeathTest, NoSBOSelfMoveAssignment) { const fat instance{.1, .2, .3, .4}; entt::any any{instance}; // avoid warnings due to self-assignment - ASSERT_DEATH(any = std::move(*&any), ""); + any = std::move(*&any); + + ASSERT_FALSE(any); + ASSERT_FALSE(any.owner()); + ASSERT_EQ(any.policy(), entt::any_policy::empty); + ASSERT_EQ(any.type(), entt::type_id()); + ASSERT_EQ(any.data(), nullptr); } TEST(Any, NoSBODirectAssignment) { @@ -808,11 +820,17 @@ TEST(Any, VoidMoveAssignment) { ASSERT_EQ(entt::any_cast(&other), nullptr); } -ENTT_DEBUG_TEST(AnyDeathTest, VoidSelfMoveAssignment) { +TEST(AnyDeathTest, VoidSelfMoveAssignment) { entt::any any{std::in_place_type}; // avoid warnings due to self-assignment - ASSERT_DEATH(any = std::move(*&any), ""); + any = std::move(*&any); + + ASSERT_FALSE(any); + ASSERT_FALSE(any.owner()); + ASSERT_EQ(any.policy(), entt::any_policy::empty); + ASSERT_EQ(any.type(), entt::type_id()); + ASSERT_EQ(any.data(), nullptr); } TEST(Any, SBOMoveValidButUnspecifiedState) { diff --git a/test/entt/entity/view.cpp b/test/entt/entity/view.cpp index 93e4049e9..6f03dea83 100644 --- a/test/entt/entity/view.cpp +++ b/test/entt/entity/view.cpp @@ -1539,7 +1539,11 @@ TEST(View, Pipe) { entt::basic_view view1{std::forward_as_tuple(std::get<0>(storage)), std::forward_as_tuple(std::as_const(std::get<1>(storage)))}; entt::basic_view view2{std::forward_as_tuple(std::as_const(std::get<0>(storage))), std::forward_as_tuple(std::get<4>(storage))}; entt::basic_view view3{std::get<2>(storage)}; - entt::basic_view view4{std::get<3>(storage)}; + entt::basic_view view4{std::as_const(std::get<3>(storage))}; + + testing::StaticAssertTypeEq(storage)), decltype(view1 | view3)>(); + testing::StaticAssertTypeEq(std::as_const(storage))), decltype(view1 | view4)>(); + testing::StaticAssertTypeEq(storage) | view4), decltype(view1 | view3 | view4)>(); testing::StaticAssertTypeEq, const entt::storage>, entt::exclude_t, entt::storage>>, decltype(view1 | view2)>(); testing::StaticAssertTypeEq, entt::storage>, entt::exclude_t, const entt::storage>>, decltype(view2 | view1)>(); diff --git a/test/entt/meta/meta_any.cpp b/test/entt/meta/meta_any.cpp index 38e3d9cb1..8d6aca2de 100644 --- a/test/entt/meta/meta_any.cpp +++ b/test/entt/meta/meta_any.cpp @@ -356,11 +356,15 @@ TEST_F(MetaAny, SBOMoveAssignment) { ASSERT_NE(other, entt::meta_any{0}); } -ENTT_DEBUG_TEST_F(MetaAnyDeathTest, SBOSelfMoveAssignment) { +TEST_F(MetaAnyDeathTest, SBOSelfMoveAssignment) { entt::meta_any any{3}; // avoid warnings due to self-assignment - ASSERT_DEATH(any = std::move(*&any), ""); + any = std::move(*&any); + + ASSERT_FALSE(any); + ASSERT_FALSE(any.type()); + ASSERT_EQ(any.base().data(), nullptr); } TEST_F(MetaAny, SBODirectAssignment) { @@ -685,12 +689,16 @@ TEST_F(MetaAny, NoSBOMoveAssignment) { ASSERT_NE(other, fat{}); } -ENTT_DEBUG_TEST_F(MetaAnyDeathTest, NoSBOSelfMoveAssignment) { +TEST_F(MetaAnyDeathTest, NoSBOSelfMoveAssignment) { const fat instance{.1, .2, .3, .4}; entt::meta_any any{instance}; // avoid warnings due to self-assignment - ASSERT_DEATH(any = std::move(*&any), ""); + any = std::move(*&any); + + ASSERT_FALSE(any); + ASSERT_FALSE(any.type()); + ASSERT_EQ(any.base().data(), nullptr); } TEST_F(MetaAny, NoSBODirectAssignment) { @@ -908,11 +916,15 @@ TEST_F(MetaAny, VoidMoveAssignment) { ASSERT_EQ(other, entt::meta_any{std::in_place_type}); } -ENTT_DEBUG_TEST_F(MetaAnyDeathTest, VoidSelfMoveAssignment) { +TEST_F(MetaAnyDeathTest, VoidSelfMoveAssignment) { entt::meta_any any{std::in_place_type}; // avoid warnings due to self-assignment - ASSERT_DEATH(any = std::move(*&any), ""); + any = std::move(*&any); + + ASSERT_FALSE(any); + ASSERT_FALSE(any.type()); + ASSERT_EQ(any.base().data(), nullptr); } TEST_F(MetaAny, SBOMoveInvalidate) { diff --git a/test/entt/meta/meta_data.cpp b/test/entt/meta/meta_data.cpp index 3a5ec4040..3bd3fa016 100644 --- a/test/entt/meta/meta_data.cpp +++ b/test/entt/meta/meta_data.cpp @@ -146,6 +146,22 @@ struct MetaData: ::testing::Test { using MetaDataDeathTest = MetaData; +TEST_F(MetaData, SafeWhenEmpty) { + entt::meta_data data{}; + + ASSERT_FALSE(data); + ASSERT_EQ(data, entt::meta_data{}); + ASSERT_EQ(data.arity(), 0u); + ASSERT_FALSE(data.is_const()); + ASSERT_FALSE(data.is_static()); + ASSERT_EQ(data.type(), entt::meta_type{}); + ASSERT_FALSE(data.set({}, 0)); + ASSERT_FALSE(data.get({})); + ASSERT_EQ(data.arg(0u), entt::meta_type{}); + ASSERT_EQ(data.traits(), test::meta_traits::none); + ASSERT_EQ(static_cast(data.custom()), nullptr); +} + TEST_F(MetaData, UserTraits) { using namespace entt::literals; diff --git a/test/entt/meta/meta_func.cpp b/test/entt/meta/meta_func.cpp index 5bce15fc0..6a84b1780 100644 --- a/test/entt/meta/meta_func.cpp +++ b/test/entt/meta/meta_func.cpp @@ -158,6 +158,27 @@ struct MetaFunc: ::testing::Test { using MetaFuncDeathTest = MetaFunc; +TEST_F(MetaFunc, SafeWhenEmpty) { + entt::meta_func func{}; + entt::meta_any *args = nullptr; + + ASSERT_FALSE(func); + ASSERT_EQ(func, entt::meta_func{}); + ASSERT_EQ(func.arity(), 0u); + ASSERT_FALSE(func.is_const()); + ASSERT_FALSE(func.is_static()); + ASSERT_EQ(func.ret(), entt::meta_type{}); + ASSERT_EQ(func.arg(0u), entt::meta_type{}); + ASSERT_EQ(func.arg(1u), entt::meta_type{}); + ASSERT_FALSE(func.invoke({}, args, 0u)); + ASSERT_FALSE(func.invoke({}, args, 1u)); + ASSERT_FALSE(func.invoke({})); + ASSERT_FALSE(func.invoke({}, 'c')); + ASSERT_EQ(func.traits(), test::meta_traits::none); + ASSERT_EQ(static_cast(func.custom()), nullptr); + ASSERT_EQ(func.next(), func); +} + TEST_F(MetaFunc, UserTraits) { using namespace entt::literals; diff --git a/test/entt/meta/meta_type.cpp b/test/entt/meta/meta_type.cpp index d1b6515b0..9defb4011 100644 --- a/test/entt/meta/meta_type.cpp +++ b/test/entt/meta/meta_type.cpp @@ -204,6 +204,62 @@ TEST_F(MetaType, Resolve) { ASSERT_TRUE(found); } +TEST_F(MetaType, SafeWhenEmpty) { + using namespace entt::literals; + + entt::meta_type type{}; + entt::meta_any *args = nullptr; + + ASSERT_FALSE(type); + ASSERT_EQ(type, entt::meta_type{}); + ASSERT_EQ(type.info(), entt::type_id()); + ASSERT_EQ(type.id(), entt::id_type{}); + ASSERT_EQ(type.size_of(), 0u); + ASSERT_FALSE(type.is_arithmetic()); + ASSERT_FALSE(type.is_integral()); + ASSERT_FALSE(type.is_signed()); + ASSERT_FALSE(type.is_array()); + ASSERT_FALSE(type.is_enum()); + ASSERT_FALSE(type.is_class()); + ASSERT_FALSE(type.is_pointer()); + ASSERT_EQ(type.remove_pointer(), type); + ASSERT_FALSE(type.is_pointer_like()); + ASSERT_FALSE(type.is_sequence_container()); + ASSERT_FALSE(type.is_associative_container()); + ASSERT_FALSE(type.is_template_specialization()); + ASSERT_EQ(type.template_arity(), 0u); + ASSERT_EQ(type.template_type(), type); + ASSERT_EQ(type.template_arg(0u), type); + ASSERT_EQ(type.template_arg(1u), type); + ASSERT_FALSE(type.can_cast(type)); + ASSERT_FALSE(type.can_cast(entt::resolve())); + ASSERT_FALSE(type.can_convert(type)); + ASSERT_FALSE(type.can_convert(entt::resolve())); + ASSERT_EQ(type.base().begin(), type.base().end()); + ASSERT_EQ(type.data().begin(), type.data().end()); + ASSERT_EQ(type.data("data"_hs), entt::meta_data{}); + ASSERT_EQ(type.func().begin(), type.func().end()); + ASSERT_EQ(type.func("func"_hs), entt::meta_func{}); + ASSERT_FALSE(type.construct(args, 0u)); + ASSERT_FALSE(type.construct(args, 1u)); + ASSERT_FALSE(type.construct()); + ASSERT_FALSE(type.construct(0.0)); + ASSERT_FALSE(type.from_void(static_cast(nullptr))); + ASSERT_FALSE(type.from_void(static_cast(nullptr), true)); + ASSERT_FALSE(type.from_void(static_cast(&type))); + ASSERT_FALSE(type.from_void(static_cast(&type), true)); + ASSERT_FALSE(type.from_void(static_cast(nullptr))); + ASSERT_FALSE(type.from_void(static_cast(&type))); + ASSERT_FALSE(type.invoke("func"_hs, {}, args, 0u)); + ASSERT_FALSE(type.invoke("func"_hs, {}, args, 1u)); + ASSERT_FALSE(type.invoke("func"_hs, {})); + ASSERT_FALSE(type.invoke("func"_hs, {}, 'c')); + ASSERT_FALSE(type.set("data"_hs, {}, 0)); + ASSERT_FALSE(type.get("data"_hs, {})); + ASSERT_EQ(type.traits(), test::meta_traits::none); + ASSERT_EQ(static_cast(type.custom()), nullptr); +} + TEST_F(MetaType, UserTraits) { ASSERT_EQ(entt::resolve().traits(), test::meta_traits::none); ASSERT_EQ(entt::resolve().traits(), test::meta_traits::none);