#include #include #include #include #include #include #include #include #include #include #include #include "../common/config.h" struct base_t { virtual ~base_t() = default; static void destroy(base_t &) { ++counter; } inline static int counter = 0; int value{3}; }; struct derived_t: base_t { derived_t() {} }; struct clazz_t { clazz_t() : i{0}, j{1}, base{} {} operator int() const { return h; } int i{0}; const int j{1}; base_t base{}; inline static int h{2}; inline static const int k{3}; }; struct setter_getter_t { setter_getter_t() : value{0} {} int setter(double val) { return value = static_cast(val); } int getter() { return value; } int setter_with_ref(const int &val) { return value = val; } const int &getter_with_ref() { return value; } static int static_setter(setter_getter_t &type, int value) { return type.value = value; } static int static_getter(const setter_getter_t &type) { return type.value; } int value; }; struct multi_setter_t { multi_setter_t() : value{0} {} void from_double(double val) { value = static_cast(val); } void from_string(const char *val) { value = std::atoi(val); } int value; }; struct array_t { static inline int global[3]; int local[5]; }; enum class property_t : entt::id_type { random, value }; struct MetaData: ::testing::Test { void SetUp() override { using namespace entt::literals; entt::meta() .type("base"_hs) .dtor() .data<&base_t::value>("value"_hs); entt::meta() .type("derived"_hs) .base() .dtor() .data<&base_t::value>("value_from_base"_hs); entt::meta() .type("clazz"_hs) .data<&clazz_t::i, entt::as_ref_t>("i"_hs) .prop(3u, 0) .data<&clazz_t::i, entt::as_cref_t>("ci"_hs) .data<&clazz_t::j>("j"_hs) .prop("true"_hs, 1) .data<&clazz_t::h>("h"_hs) .prop(static_cast(property_t::random), 2) .data<&clazz_t::k>("k"_hs) .prop(static_cast(property_t::value), 3) .data<&clazz_t::base>("base"_hs) .data<&clazz_t::i, entt::as_void_t>("void"_hs) .conv(); entt::meta() .type("setter_getter"_hs) .data<&setter_getter_t::static_setter, &setter_getter_t::static_getter>("x"_hs) .data<&setter_getter_t::setter, &setter_getter_t::getter>("y"_hs) .data<&setter_getter_t::static_setter, &setter_getter_t::getter>("z"_hs) .data<&setter_getter_t::setter_with_ref, &setter_getter_t::getter_with_ref>("w"_hs) .data("z_ro"_hs) .data("value"_hs); entt::meta() .type("multi_setter"_hs) .data, &multi_setter_t::value>("value"_hs); entt::meta() .type("array"_hs) .data<&array_t::global>("global"_hs) .data<&array_t::local>("local"_hs); base_t::counter = 0; } void TearDown() override { entt::meta_reset(); } }; using MetaDataDeathTest = MetaData; TEST_F(MetaData, Functionalities) { using namespace entt::literals; auto data = entt::resolve().data("i"_hs); clazz_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data, data); ASSERT_NE(data, entt::meta_data{}); ASSERT_FALSE(data != data); ASSERT_TRUE(data == data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_FALSE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_TRUE(data.set(instance, 42)); ASSERT_EQ(data.get(instance).cast(), 42); for(auto curr: data.prop()) { ASSERT_EQ(curr.first, 3u); ASSERT_EQ(curr.second.value(), 0); } ASSERT_FALSE(data.prop(2)); ASSERT_FALSE(data.prop('c')); auto prop = data.prop(3u); ASSERT_TRUE(prop); ASSERT_EQ(prop.value(), 0); } TEST_F(MetaData, Const) { using namespace entt::literals; auto data = entt::resolve().data("j"_hs); clazz_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_TRUE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_EQ(data.get(instance).cast(), 1); ASSERT_FALSE(data.set(instance, 42)); ASSERT_EQ(data.get(instance).cast(), 1); for(auto curr: data.prop()) { ASSERT_EQ(curr.first, "true"_hs); ASSERT_EQ(curr.second.value(), 1); } ASSERT_FALSE(data.prop(false)); ASSERT_FALSE(data.prop('c')); auto prop = data.prop("true"_hs); ASSERT_TRUE(prop); ASSERT_EQ(prop.value(), 1); } TEST_F(MetaData, Static) { using namespace entt::literals; auto data = entt::resolve().data("h"_hs); ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_FALSE(data.is_const()); ASSERT_TRUE(data.is_static()); ASSERT_EQ(data.get({}).cast(), 2); ASSERT_TRUE(data.set({}, 42)); ASSERT_EQ(data.get({}).cast(), 42); for(auto curr: data.prop()) { ASSERT_EQ(curr.first, static_cast(property_t::random)); ASSERT_EQ(curr.second.value(), 2); } ASSERT_FALSE(data.prop(static_cast(property_t::value))); ASSERT_FALSE(data.prop('c')); auto prop = data.prop(static_cast(property_t::random)); ASSERT_TRUE(prop); ASSERT_EQ(prop.value(), 2); } TEST_F(MetaData, ConstStatic) { using namespace entt::literals; auto data = entt::resolve().data("k"_hs); ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_TRUE(data.is_const()); ASSERT_TRUE(data.is_static()); ASSERT_EQ(data.get({}).cast(), 3); ASSERT_FALSE(data.set({}, 42)); ASSERT_EQ(data.get({}).cast(), 3); for(auto curr: data.prop()) { ASSERT_EQ(curr.first, static_cast(property_t::value)); ASSERT_EQ(curr.second.value(), 3); } ASSERT_FALSE(data.prop(static_cast(property_t::random))); ASSERT_FALSE(data.prop('c')); auto prop = data.prop(static_cast(property_t::value)); ASSERT_TRUE(prop); ASSERT_EQ(prop.value(), 3); } TEST_F(MetaData, GetMetaAnyArg) { using namespace entt::literals; entt::meta_any any{clazz_t{}}; any.cast().i = 99; const auto value = entt::resolve().data("i"_hs).get(any); ASSERT_TRUE(value); ASSERT_TRUE(static_cast(value.cast())); ASSERT_EQ(value.cast(), 99); } TEST_F(MetaData, GetInvalidArg) { using namespace entt::literals; auto instance = 0; ASSERT_FALSE(entt::resolve().data("i"_hs).get(instance)); } TEST_F(MetaData, SetMetaAnyArg) { using namespace entt::literals; entt::meta_any any{clazz_t{}}; entt::meta_any value{42}; ASSERT_EQ(any.cast().i, 0); ASSERT_TRUE(entt::resolve().data("i"_hs).set(any, value)); ASSERT_EQ(any.cast().i, 42); } TEST_F(MetaData, SetInvalidArg) { using namespace entt::literals; ASSERT_FALSE(entt::resolve().data("i"_hs).set({}, 'c')); } TEST_F(MetaData, SetCast) { using namespace entt::literals; clazz_t instance{}; ASSERT_EQ(base_t::counter, 0); ASSERT_TRUE(entt::resolve().data("base"_hs).set(instance, derived_t{})); ASSERT_EQ(base_t::counter, 1); } TEST_F(MetaData, SetConvert) { using namespace entt::literals; clazz_t instance{}; instance.h = 42; ASSERT_EQ(instance.i, 0); ASSERT_TRUE(entt::resolve().data("i"_hs).set(instance, instance)); ASSERT_EQ(instance.i, 42); } TEST_F(MetaData, SetByRef) { using namespace entt::literals; entt::meta_any any{clazz_t{}}; int value{42}; ASSERT_EQ(any.cast().i, 0); ASSERT_TRUE(entt::resolve().data("i"_hs).set(any, entt::forward_as_meta(value))); ASSERT_EQ(any.cast().i, 42); value = 3; auto wrapper = entt::forward_as_meta(value); ASSERT_TRUE(entt::resolve().data("i"_hs).set(any, wrapper.as_ref())); ASSERT_EQ(any.cast().i, 3); } TEST_F(MetaData, SetByConstRef) { using namespace entt::literals; entt::meta_any any{clazz_t{}}; int value{42}; ASSERT_EQ(any.cast().i, 0); ASSERT_TRUE(entt::resolve().data("i"_hs).set(any, entt::forward_as_meta(std::as_const(value)))); ASSERT_EQ(any.cast().i, 42); value = 3; auto wrapper = entt::forward_as_meta(std::as_const(value)); ASSERT_TRUE(entt::resolve().data("i"_hs).set(any, wrapper.as_ref())); ASSERT_EQ(any.cast().i, 3); } TEST_F(MetaData, SetterGetterAsFreeFunctions) { using namespace entt::literals; auto data = entt::resolve().data("x"_hs); setter_getter_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_FALSE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_TRUE(data.set(instance, 42)); ASSERT_EQ(data.get(instance).cast(), 42); } TEST_F(MetaData, SetterGetterAsMemberFunctions) { using namespace entt::literals; auto data = entt::resolve().data("y"_hs); setter_getter_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_FALSE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_TRUE(data.set(instance, 42.)); ASSERT_EQ(data.get(instance).cast(), 42); ASSERT_TRUE(data.set(instance, 3)); ASSERT_EQ(data.get(instance).cast(), 3); } TEST_F(MetaData, SetterGetterWithRefAsMemberFunctions) { using namespace entt::literals; auto data = entt::resolve().data("w"_hs); setter_getter_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_FALSE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_TRUE(data.set(instance, 42)); ASSERT_EQ(data.get(instance).cast(), 42); } TEST_F(MetaData, SetterGetterMixed) { using namespace entt::literals; auto data = entt::resolve().data("z"_hs); setter_getter_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_FALSE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_TRUE(data.set(instance, 42)); ASSERT_EQ(data.get(instance).cast(), 42); } TEST_F(MetaData, SetterGetterReadOnly) { using namespace entt::literals; auto data = entt::resolve().data("z_ro"_hs); setter_getter_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 0u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::meta_type{}); ASSERT_TRUE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_FALSE(data.set(instance, 42)); ASSERT_EQ(data.get(instance).cast(), 0); } TEST_F(MetaData, SetterGetterReadOnlyDataMember) { using namespace entt::literals; auto data = entt::resolve().data("value"_hs); setter_getter_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 0u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::meta_type{}); ASSERT_TRUE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_FALSE(data.set(instance, 42)); ASSERT_EQ(data.get(instance).cast(), 0); } TEST_F(MetaData, MultiSetter) { using namespace entt::literals; auto data = entt::resolve().data("value"_hs); multi_setter_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 2u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_EQ(data.arg(1u), entt::resolve()); ASSERT_EQ(data.arg(2u), entt::meta_type{}); ASSERT_FALSE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_TRUE(data.set(instance, 42)); ASSERT_EQ(data.get(instance).cast(), 42); ASSERT_TRUE(data.set(instance, 3.)); ASSERT_EQ(data.get(instance).cast(), 3); ASSERT_FALSE(data.set(instance, std::string{"99"})); ASSERT_TRUE(data.set(instance, std::string{"99"}.c_str())); ASSERT_EQ(data.get(instance).cast(), 99); } TEST_F(MetaData, ConstInstance) { using namespace entt::literals; clazz_t instance{}; ASSERT_NE(entt::resolve().data("i"_hs).get(instance).try_cast(), nullptr); ASSERT_NE(entt::resolve().data("i"_hs).get(instance).try_cast(), nullptr); ASSERT_EQ(entt::resolve().data("i"_hs).get(std::as_const(instance)).try_cast(), nullptr); // as_ref_t adapts to the constness of the passed object and returns const references in case ASSERT_NE(entt::resolve().data("i"_hs).get(std::as_const(instance)).try_cast(), nullptr); ASSERT_TRUE(entt::resolve().data("i"_hs).get(instance)); ASSERT_TRUE(entt::resolve().data("i"_hs).set(instance, 3)); ASSERT_TRUE(entt::resolve().data("i"_hs).get(std::as_const(instance))); ASSERT_FALSE(entt::resolve().data("i"_hs).set(std::as_const(instance), 3)); ASSERT_TRUE(entt::resolve().data("ci"_hs).get(instance)); ASSERT_TRUE(entt::resolve().data("ci"_hs).set(instance, 3)); ASSERT_TRUE(entt::resolve().data("ci"_hs).get(std::as_const(instance))); ASSERT_FALSE(entt::resolve().data("ci"_hs).set(std::as_const(instance), 3)); ASSERT_TRUE(entt::resolve().data("j"_hs).get(instance)); ASSERT_FALSE(entt::resolve().data("j"_hs).set(instance, 3)); ASSERT_TRUE(entt::resolve().data("j"_hs).get(std::as_const(instance))); ASSERT_FALSE(entt::resolve().data("j"_hs).set(std::as_const(instance), 3)); } TEST_F(MetaData, ArrayStatic) { using namespace entt::literals; auto data = entt::resolve().data("global"_hs); ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_FALSE(data.is_const()); ASSERT_TRUE(data.is_static()); ASSERT_TRUE(data.type().is_array()); ASSERT_FALSE(data.get({})); } TEST_F(MetaData, Array) { using namespace entt::literals; auto data = entt::resolve().data("local"_hs); array_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_FALSE(data.is_const()); ASSERT_FALSE(data.is_static()); ASSERT_TRUE(data.type().is_array()); ASSERT_FALSE(data.get(instance)); } TEST_F(MetaData, AsVoid) { using namespace entt::literals; auto data = entt::resolve().data("void"_hs); clazz_t instance{}; ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_TRUE(data.set(instance, 42)); ASSERT_EQ(instance.i, 42); ASSERT_EQ(data.get(instance), entt::meta_any{std::in_place_type}); } TEST_F(MetaData, AsRef) { using namespace entt::literals; clazz_t instance{}; auto data = entt::resolve().data("i"_hs); ASSERT_TRUE(data); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_NE(data.prop().cbegin(), data.prop().cend()); ASSERT_EQ(instance.i, 0); data.get(instance).cast() = 3; ASSERT_EQ(instance.i, 3); } TEST_F(MetaData, AsConstRef) { using namespace entt::literals; clazz_t instance{}; auto data = entt::resolve().data("ci"_hs); ASSERT_EQ(instance.i, 0); ASSERT_EQ(data.arity(), 1u); ASSERT_EQ(data.type(), entt::resolve()); ASSERT_EQ(data.arg(0u), entt::resolve()); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_EQ(data.get(instance).cast(), 0); ASSERT_EQ(data.prop().cbegin(), data.prop().cend()); ASSERT_EQ(instance.i, 0); } ENTT_DEBUG_TEST_F(MetaDataDeathTest, AsConstRef) { using namespace entt::literals; clazz_t instance{}; auto data = entt::resolve().data("ci"_hs); ASSERT_DEATH(data.get(instance).cast() = 3, ""); } TEST_F(MetaData, SetGetBaseData) { using namespace entt::literals; auto type = entt::resolve(); derived_t instance{}; ASSERT_TRUE(type.data("value"_hs)); ASSERT_EQ(instance.value, 3); ASSERT_TRUE(type.data("value"_hs).set(instance, 42)); ASSERT_EQ(type.data("value"_hs).get(instance).cast(), 42); ASSERT_EQ(instance.value, 42); } TEST_F(MetaData, SetGetFromBase) { using namespace entt::literals; auto type = entt::resolve(); derived_t instance{}; ASSERT_TRUE(type.data("value_from_base"_hs)); ASSERT_EQ(instance.value, 3); ASSERT_TRUE(type.data("value_from_base"_hs).set(instance, 42)); ASSERT_EQ(type.data("value_from_base"_hs).get(instance).cast(), 42); ASSERT_EQ(instance.value, 42); } TEST_F(MetaData, ReRegistration) { using namespace entt::literals; SetUp(); auto &&node = entt::internal::resolve(entt::internal::meta_context::from(entt::locator::value_or())); auto type = entt::resolve(); ASSERT_TRUE(node.details); ASSERT_FALSE(node.details->data.empty()); ASSERT_EQ(node.details->data.size(), 1u); ASSERT_TRUE(type.data("value"_hs)); entt::meta().data<&base_t::value>("field"_hs); ASSERT_TRUE(node.details); ASSERT_EQ(node.details->data.size(), 2u); ASSERT_TRUE(type.data("value"_hs)); ASSERT_TRUE(type.data("field"_hs)); } TEST_F(MetaData, CollisionAndReuse) { using namespace entt::literals; ASSERT_TRUE(entt::resolve().data("j"_hs)); ASSERT_FALSE(entt::resolve().data("cj"_hs)); ASSERT_TRUE(entt::resolve().data("j"_hs).is_const()); ASSERT_NO_FATAL_FAILURE(entt::meta().data<&clazz_t::i>("j"_hs)); ASSERT_NO_FATAL_FAILURE(entt::meta().data<&clazz_t::j>("cj"_hs)); ASSERT_TRUE(entt::resolve().data("j"_hs)); ASSERT_TRUE(entt::resolve().data("cj"_hs)); ASSERT_FALSE(entt::resolve().data("j"_hs).is_const()); }