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);