diff --git a/core/object/object.h b/core/object/object.h index 8951822700d6..c405a18a7fbb 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -166,9 +166,9 @@ struct StructMember { } }; -#define STRUCT_MEMBER(m_name, m_type) StructMember(SNAME(m_name), m_type) -#define STRUCT_CLASS_MEMBER(m_name, m_class) StructMember(SNAME(m_name), Variant::OBJECT, m_class) -#define STRUCT_STRUCT_MEMBER(m_name, m_struct) StructMember(SNAME(m_name), Variant::ARRAY, m_struct::get_name(), m_struct::get_members()) +#define STRUCT_MEMBER(m_name, m_type, m_default) StructMember(SNAME(m_name), m_type, StringName(), nullptr, m_default) +#define STRUCT_CLASS_MEMBER(m_name, m_class_name, m_default) StructMember(SNAME(m_name), Variant::OBJECT, m_class_name, nullptr, m_default) +#define STRUCT_STRUCT_MEMBER(m_name, m_struct, m_default) StructMember(SNAME(m_name), Variant::ARRAY, m_struct::get_name(), m_struct::get_members(), m_default) #define STRUCT_LAYOUT(m_struct, m_name, ...) \ struct m_struct { \ @@ -259,12 +259,12 @@ struct PropertyInfo { }; STRUCT_LAYOUT(PropertyInfoLayout, "PropertyInfo", - STRUCT_MEMBER("name", Variant::STRING), - STRUCT_MEMBER("class_name", Variant::STRING_NAME), - STRUCT_MEMBER("type", Variant::INT), - STRUCT_MEMBER("hint", Variant::INT), - STRUCT_MEMBER("hint_string", Variant::STRING), - STRUCT_MEMBER("usage", Variant::INT)); + STRUCT_MEMBER("name", Variant::STRING, String()), + STRUCT_MEMBER("class_name", Variant::STRING_NAME, StringName()), + STRUCT_MEMBER("type", Variant::INT, Variant::NIL), + STRUCT_MEMBER("hint", Variant::INT, PROPERTY_HINT_NONE), + STRUCT_MEMBER("hint_string", Variant::STRING, String()), + STRUCT_MEMBER("usage", Variant::INT, PROPERTY_USAGE_DEFAULT)); TypedArray convert_property_list(const List *p_list); diff --git a/core/variant/array.cpp b/core/variant/array.cpp index d028f1bca83d..5d94f5040cdf 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -225,9 +225,6 @@ void Array::assign(const Array &p_array) { const ContainerTypeValidate &typed = _p->typed; const ContainerTypeValidate &source_typed = p_array._p->typed; - // TODO: I will probably need to add some logic here for structs. - - // assign if we don't have a type yet, our type exactly matches the source, or our type can reference the source's if (typed.type == Variant::NIL || typed == source_typed || (source_typed.type == Variant::OBJECT && typed.can_reference(source_typed))) { // from same to same or // from anything to variants or @@ -236,17 +233,19 @@ void Array::assign(const Array &p_array) { return; } + if (typed.is_struct() && (typed != source_typed)) { + ERR_FAIL_MSG("A struct can only be assigned a struct of matching type."); + } + const Variant *source = p_array._p->array.ptr(); int size = p_array._p->array.size(); - // if (we are an object and the source is untyped) or (the source is an object, and it can reference us) if ((source_typed.type == Variant::NIL && typed.type == Variant::OBJECT) || (source_typed.type == Variant::OBJECT && source_typed.can_reference(typed))) { // from variants to objects or // from base classes to subclasses for (int i = 0; i < size; i++) { const Variant &element = source[i]; const Variant::Type type = source[i].get_type(); - // if source's ith element has a type but that type is an invalid object if (type != Variant::NIL && (type != Variant::OBJECT || !typed.validate_object(element, "assign"))) { ERR_FAIL_MSG(vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(type), Variant::get_type_name(typed.type))); } @@ -881,6 +880,30 @@ void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Var _p->typed.where = "TypedArray"; } +void Array::set_struct(uint32_t p_size, const StringName &p_struct_name, const StructMember &(*p_get_member)(uint32_t)) { + if (validate_set_type() != OK) { + return; + } + _p->array.resize(p_size); + _p->typed.type = Variant::ARRAY; + _p->typed.where = "Struct"; // TODO: is this right? + + _p->struct_name = p_struct_name; + _p->typed.class_name = p_struct_name; + + _p->array.resize(p_size); + _p->member_count = p_size; + _p->member_names.resize(p_size); + _p->typed.struct_members.resize(p_size); + + for (uint32_t i = 0; i < p_size; i++) { + StructMember member = p_get_member(i); + _p->typed.struct_members[i].type = member.type; + _p->typed.struct_members[i].class_name = member.class_name; + _p->member_names[i] = member.name; + } +} + bool Array::is_typed() const { return _p->typed.type != Variant::NIL; } @@ -920,77 +943,38 @@ Array::Array(const Array &p_from) { _ref(p_from); } -Array::Array(const Array &p_from, uint32_t p_size, const StringName &p_name, const StructMember &(*p_get_member)(uint32_t)) { +Array::Array(const Array &p_from, const StringName &p_name, const StructMember &(*p_get_member)(uint32_t)) { _p = memnew(ArrayPrivate); _p->refcount.init(); // TODO: should this be _ref(p_from)? - if (validate_set_type() != OK) { - return; - } - assign(p_from); - - _p->array.resize(p_size); - _p->typed.type = Variant::ARRAY; - _p->typed.where = "Struct"; // TODO: is this right? - - _p->struct_name = p_name; - _p->typed.class_name = p_name; - _p->member_count = p_size; - _p->member_names.resize(p_size); - _p->typed.struct_members.resize(p_size); - for (uint32_t i = 0; i < p_size; i++) { - StructMember source_member = p_get_member(i); - ContainerTypeValidate *member_type = &_p->typed.struct_members[i]; - _p->member_names[i] = source_member.name; - member_type->type = source_member.type; - member_type->class_name = source_member.class_name; - } -} -Array::Array(const Array &p_from, uint32_t p_size, const StringName &p_name, const Vector &p_member_names) { - _p = memnew(ArrayPrivate); - _p->refcount.init(); - if (validate_set_type() != OK) { - return; - } + set_struct(p_from.size(), p_name, p_get_member); assign(p_from); - - _p->array.resize(p_size); - _p->typed.where = "Struct"; // TODO: is this right? - - _p->struct_name = p_name; - _p->typed.class_name = p_name; - _p->member_count = p_size; - _p->member_names.resize(p_size); - _p->typed.struct_members.resize(p_size); - for (uint32_t i = 0; i < p_size; i++) { - StructMember source_member = p_member_names[i]; - ContainerTypeValidate *member_type = &_p->typed.struct_members[i]; - _p->member_names[i] = source_member.name; - member_type->type = source_member.type; - member_type->class_name = source_member.class_name; - } } +//Array::Array(const Array &p_from, const StringName &p_name, const Vector &p_member_names) { +// _p = memnew(ArrayPrivate); +// _p->refcount.init(); +// const uint32_t size = p_from.size(); +// +// set_struct(size, p_name); +// for (uint32_t i = 0; i < size; i++) { +// StructMember member = p_get_member(i); +// _p->typed.set_struct_member(i, member.type, member.class_name); +// _p->member_names[i] = member.name; +// _p->array.write[i, member.default_value]; +// } +// +// assign(p_from); +//} + Array::Array(uint32_t p_size, const StringName &p_name, const StructMember &(*p_get_member)(uint32_t)) { _p = memnew(ArrayPrivate); _p->refcount.init(); - if (validate_set_type() != OK) { - return; - } - _p->array.resize(p_size); - _p->typed.where = "Struct"; // TODO: is this right? - _p->struct_name = p_name; - _p->typed.class_name = p_name; - _p->member_count = p_size; - _p->member_names.resize(p_size); - _p->typed.struct_members.resize(p_size); + set_struct(p_size, p_name, p_get_member); + Variant *pw = _p->array.ptrw(); for (uint32_t i = 0; i < p_size; i++) { - StructMember source_member = p_get_member(i); - ContainerTypeValidate *member_type = &_p->typed.struct_members[i]; - _p->member_names[i] = source_member.name; - member_type->type = source_member.type; - member_type->class_name = source_member.class_name; + pw[i] = p_get_member(i).default_value; } } diff --git a/core/variant/array.h b/core/variant/array.h index 894316431aa8..bd28a87fdbe8 100644 --- a/core/variant/array.h +++ b/core/variant/array.h @@ -131,6 +131,7 @@ class Array { Error validate_set_type(); void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script); + void set_struct(uint32_t p_size, const StringName &p_class_name, const StructMember &(*p_get_member)(uint32_t)); bool is_typed() const; bool is_struct() const; bool is_same_typed(const Array &p_other) const; @@ -143,8 +144,7 @@ class Array { Array(const Array &p_base, uint32_t p_type, const StringName &p_class_name, const Variant &p_script); Array(const Array &p_from); - Array(const Array &p_from, uint32_t p_size, const StringName &p_name, const StructMember &(*p_get_member)(uint32_t)); - Array(const Array &p_from, uint32_t p_size, const StringName &p_name, const Vector &p_member_names); + Array(const Array &p_from, const StringName &p_name, const StructMember &(*p_get_member)(uint32_t)); Array(uint32_t p_size, const StringName &p_name, const StructMember &(*p_get_member)(uint32_t)); Array(); ~Array(); diff --git a/core/variant/struct.h b/core/variant/struct.h index 00d689f865cc..c564cb3fa2e5 100644 --- a/core/variant/struct.h +++ b/core/variant/struct.h @@ -35,48 +35,6 @@ class Array; -//// TODO: Another different version -//#define STRUCT_LAYOUT(m_name, m_member_count, ...) \ -// struct m_name { \ -// static const uint32_t member_count = m_member_count; \ -// _FORCE_INLINE_ static const StructMember *get_members() { \ -// static const StructMember members[member_count] = { __VA_ARGS__ }; \ -// return members; \ -// } \ -// _FORCE_INLINE_ static const StringName *get_member_names() { \ -// const StructMember members[member_count] = get_members(); \ -// StringName member_names[member_count]; \ -// for (uint32_t i = 0; i < member_count; i++) { \ -// member_names[i] = members[i].name; \ -// } \ -// return member_names; \ -// } \ -// _FORCE_INLINE_ static const uint32_t *get_member_types() { \ -// const StructMember members[member_count] = get_members(); \ -// uint32_t member_types[member_count]; \ -// for (uint32_t i = 0; i < member_count; i++) { \ -// member_types[i] = members[i].type; \ -// } \ -// return member_types; \ -// } \ -// _FORCE_INLINE_ static const StringName *get_member_class_names() { \ -// const StructMember members[member_count] = get_members(); \ -// StringName member_class_names[member_count]; \ -// for (uint32_t i = 0; i < member_count; i++) { \ -// member_class_names[i] = members[i].class_name; \ -// } \ -// return member_class_names; \ -// } \ -// _FORCE_INLINE_ static const Variant *get_member_default_values() { \ -// const StructMember members[member_count] = get_members(); \ -// Variant member_default_values[member_count]; \ -// for (uint32_t i = 0; i < member_count; i++) { \ -// member_default_values[i] = members[i].default_value; \ -// } \ -// return member_default_values; \ -// } \ -// }; - template class Struct : public Array { public: @@ -91,10 +49,10 @@ class Struct : public Array { return get_named(p_member); } _FORCE_INLINE_ Struct(const Variant &p_variant) : - Array(Array(p_variant), T::get_member_count(), T::get_name(), T::get_member) { + Array(Array(p_variant), T::get_name(), T::get_member) { } _FORCE_INLINE_ Struct(const Array &p_array) : - Array(p_array, T::get_member_count(), T::get_name(), T::get_member) { + Array(p_array, T::get_name(), T::get_member) { } _FORCE_INLINE_ Struct() : Array(T::get_member_count(), T::get_name(), T::get_member) { diff --git a/tests/core/variant/test_struct.h b/tests/core/variant/test_struct.h index 5005905d55ec..4ad33649a97f 100644 --- a/tests/core/variant/test_struct.h +++ b/tests/core/variant/test_struct.h @@ -88,12 +88,12 @@ TEST_CASE("[Struct] PropertyInfo") { TEST_CASE("[Struct] Validation") { struct NamedInt { - StringName name; - int value; + StringName name = StringName(); + int value = 0; }; STRUCT_LAYOUT(NamedIntLayout, "NamedInt", - STRUCT_MEMBER("name", Variant::STRING_NAME), - STRUCT_MEMBER("value", Variant::INT)); + STRUCT_MEMBER("name", Variant::STRING_NAME, StringName()), + STRUCT_MEMBER("value", Variant::INT, 0)); Struct named_int; named_int["name"] = "Godot"; @@ -109,40 +109,37 @@ TEST_CASE("[Struct] Validation") { } SUBCASE("Assignment") { - ERR_PRINT_OFF; - named_int.set_named("name", 4); - CHECK_EQ(named_int["name"], "Godot"); - - named_int.set_named("value", "Godot"); - CHECK_EQ((int)named_int["value"], 4); - ERR_PRINT_ON; - Struct a_match = named_int; CHECK_EQ(named_int, a_match); Array not_a_match; ERR_PRINT_OFF; + named_int.set_named("name", 4); + CHECK_MESSAGE(named_int["name"] == "Godot", "assigned an int to a string member"); + + named_int.set_named("value", "Godot"); + CHECK_MESSAGE((int)named_int["value"] == 4, "assigned a string to an int member"); + named_int = not_a_match; - CHECK_EQ(named_int, a_match); + CHECK_MESSAGE(named_int != not_a_match, "assigned an empty array to a struct"); not_a_match.resize(2); named_int = not_a_match; - CHECK_EQ(named_int, a_match); + CHECK_MESSAGE(named_int != not_a_match, "assigned a non-struct to a struct"); not_a_match[0] = 4; not_a_match[1] = "Godot"; named_int = not_a_match; - CHECK_EQ(named_int, a_match); + CHECK_MESSAGE(named_int != not_a_match, "assigned a non-struct to a struct"); not_a_match[0] = "Godooot"; not_a_match[1] = 5; named_int = not_a_match; - CHECK_EQ(named_int, a_match); - ERR_PRINT_ON; + CHECK_MESSAGE(named_int != not_a_match, "assigned a non-struct to a struct"); named_int.assign(not_a_match); - CHECK_EQ(named_int["name"], "Godooot"); - CHECK_EQ((int)named_int["value"], 5); + CHECK_MESSAGE(named_int != not_a_match, "assigned a non-struct to a struct"); + ERR_PRINT_ON; } } @@ -160,25 +157,34 @@ TEST_CASE("[Struct] Nesting") { BasicStruct value; }; STRUCT_LAYOUT(BasicStructLayout, "BasicStruct", - STRUCT_MEMBER("int_val", Variant::INT), - STRUCT_MEMBER("float_val", Variant::FLOAT)); + STRUCT_MEMBER("int_val", Variant::INT, 4), + STRUCT_MEMBER("float_val", Variant::FLOAT, 5.5)); STRUCT_LAYOUT(BasicStructLookalikeLayout, "BasicStructLookalike", - STRUCT_MEMBER("int_val", Variant::INT), - STRUCT_MEMBER("float_val", Variant::FLOAT)); + STRUCT_MEMBER("int_val", Variant::INT, 0), + STRUCT_MEMBER("float_val", Variant::FLOAT, 0.0)); STRUCT_LAYOUT(NestedStructLayout, "NestedStruct", - STRUCT_CLASS_MEMBER("node", "Node"), - STRUCT_STRUCT_MEMBER("value", BasicStructLayout)); + STRUCT_CLASS_MEMBER("node", "Node", Variant()), + STRUCT_STRUCT_MEMBER("value", BasicStructLayout, Struct())); + + Struct basic_struct; + Struct basic_struct_lookalike; + Struct nested_struct; + + SUBCASE("Defaults") { + CHECK_EQ((int)basic_struct["int_val"], 4); + CHECK_EQ((float)basic_struct["float_val"], 5.5); + + CHECK_EQ(nested_struct["node"], Variant()); + CHECK_EQ(nested_struct["value"], basic_struct); + } SUBCASE("Assignment") { - Struct basic_struct; basic_struct["int_val"] = 1; basic_struct["float_val"] = 3.14; - Struct basic_struct_lookalike; basic_struct_lookalike["int_val"] = 2; basic_struct_lookalike["float_val"] = 2.7; - Struct nested_struct; Node *node = memnew(Node); nested_struct.set_named("node", node); nested_struct.set_named("value", basic_struct); @@ -211,7 +217,6 @@ TEST_CASE("[Struct] Nesting") { array.push_back(0); CHECK_EQ(array.size(), 2); - Struct basic_struct_lookalike; basic_struct_lookalike["int_val"] = 3; basic_struct_lookalike["float_val"] = 5.4; array.push_back(basic_struct_lookalike); @@ -228,7 +233,7 @@ TEST_CASE("[Struct] ClassDB") { CHECK_EQ(struct_info->names[3], "hint"); CHECK_EQ(struct_info->types[3], Variant::INT); CHECK_EQ(struct_info->class_names[3], ""); - CHECK_EQ(struct_info->default_values[3], Variant()); + CHECK_EQ((int)struct_info->default_values[3], PROPERTY_HINT_NONE); } } // namespace TestStruct