diff --git a/include/caosdb/data_type.h b/include/caosdb/data_type.h index 42d376698872e086c05a5905484e2f63c11dd346..45d82f01ba952f1c56e8e02178d6721795fbc288 100644 --- a/include/caosdb/data_type.h +++ b/include/caosdb/data_type.h @@ -140,6 +140,21 @@ protected: class DataType : public ScalarProtoMessageWrapper<ProtoDataType> { public: + /** + * Copy constructor. + */ + inline DataType(const DataType &other) + : DataType(ProtoMessageWrapper<ProtoDataType>::CopyProtoMessage(other.wrapped)) {} + + /** + * Move constructor. + */ + inline DataType(DataType &&other) : DataType(other.wrapped) { + other.wrapped = nullptr; + other.list_data_type.reset(); + other.reference_data_type.reset(); + } + DataType(ProtoDataType *wrapped) : ScalarProtoMessageWrapper<ProtoDataType>(wrapped) {} DataType() : ScalarProtoMessageWrapper<ProtoDataType>(static_cast<ProtoDataType *>(nullptr)) {} /** @@ -227,6 +242,37 @@ public: return this->wrapped == other.wrapped; } + /** + * Copy assignment operator. + */ + inline auto operator=(const DataType &other) -> DataType & { + if(this != &other) { + this->reference_data_type.reset(); + this->list_data_type.reset(); + if(other.wrapped != nullptr) { + this->wrapped = ProtoMessageWrapper<ProtoDataType>::CopyProtoMessage(other.wrapped); + } else { + this->wrapped = nullptr; + } + } + return *this; + } + + /** + * Move assignment operator. + */ + inline auto operator=(DataType &&other) -> DataType & { + if(this != &other) { + this->wrapped = other.wrapped; + other.wrapped = nullptr; + other.reference_data_type.reset(); + this->reference_data_type.reset(); + other.list_data_type.reset(); + this->list_data_type.reset(); + } + return *this; + } + friend class Entity; friend class Property; diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index b31c2c91b9dbb2f41f5b768eca83a64e570e2d5b..c261c1c5af1df71fcf19de377c617092b10deeb0 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -187,6 +187,31 @@ public: return result.append(std::string("]\n")); } + /** + * Return true if the underlying Protobuf messages have the same + * serialization. + */ + inline auto operator==(const RepeatedPtrFieldWrapper<T,P> &other) const noexcept -> bool { + if (this->wrapped != nullptr && other.wrapped != nullptr && this->size() == other.size()) { + for(int i = 0; i < this->size(); i++) { + if(this->wrapped->Get(i).SerializeAsString() != other.wrapped->Get(i).SerializeAsString()) { + return false; + } + } + return true; + } + // last chance for "true": both nullptr? + return this->wrapped == other.wrapped; + } + + /** + * Return true if the underlying Protobuf messages have a different + * serialization. + */ + inline auto operator!=(const RepeatedPtrFieldWrapper<T,P> &other) const noexcept -> bool { + return !(*this == other); + } + protected: RepeatedPtrFieldWrapper() : ProtoMessageWrapper<RepeatedPtrField<P>>(){}; explicit inline RepeatedPtrFieldWrapper(RepeatedPtrField<P> *wrapped) @@ -337,15 +362,13 @@ private: */ class Messages : public RepeatedPtrFieldWrapper<Message, ProtoMessage> { public: - ~Messages(); - friend class Entity; // TODO(fspreck) Same here. // friend class Parent; // friend class Property; private: - inline Messages() : RepeatedPtrFieldWrapper(){}; + inline Messages() : RepeatedPtrFieldWrapper<Message, ProtoMessage>(){}; }; /** @@ -426,14 +449,13 @@ private: */ class Parents : public RepeatedPtrFieldWrapper<Parent, ProtoParent> { public: - ~Parents() = default; friend class Entity; private: - inline Parents() : RepeatedPtrFieldWrapper(){}; + inline Parents() : RepeatedPtrFieldWrapper<Parent, ProtoParent>(){}; explicit inline Parents( ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Parent> *wrapped) - : RepeatedPtrFieldWrapper(wrapped){}; + : RepeatedPtrFieldWrapper<Parent, ProtoParent>(wrapped){}; }; /** @@ -456,13 +478,10 @@ public: /** * Move constructor. */ - inline Property(Property &&other) : Property() { + inline Property(Property &&other) : Property(other.wrapped) { CAOSDB_LOG_TRACE(logger_name) << "Property::Property(Property &&) " << "- Move constructor"; - this->wrapped = std::move(other.wrapped); - this->value.wrapped = this->wrapped->mutable_value(); - this->data_type.wrapped = this->wrapped->mutable_data_type(); - + other.wrapped = nullptr; other.data_type.wrapped = nullptr; other.value.wrapped = nullptr; } @@ -563,8 +582,12 @@ public: CAOSDB_LOG_TRACE(logger_name) << "Property::operator=(const Property &) " << "- Copy assignment operator"; this->wrapped->CopyFrom(*other.wrapped); - this->value = Value(this->wrapped->mutable_value()); - this->data_type = DataType(this->wrapped->mutable_data_type()); + + this->value.wrapped = (this->wrapped->has_value() ? this->wrapped->mutable_value() + : static_cast<ProtoValue *>(nullptr)); + this->data_type.wrapped = + (this->wrapped->has_data_type() ? this->wrapped->mutable_data_type() + : static_cast<ProtoDataType *>(nullptr)); return *this; } @@ -574,7 +597,8 @@ public: auto operator=(Property &&other) -> Property & { CAOSDB_LOG_TRACE(logger_name) << "Property::operator=(Property &&) " << "- Move assignment operator"; - this->wrapped = std::move(other.wrapped); + this->wrapped = other.wrapped; + other.wrapped = nullptr; this->value = std::move(other.value); this->data_type = std::move(other.data_type); return *this; @@ -606,7 +630,6 @@ private: */ class Properties : public RepeatedPtrFieldWrapper<Property, ProtoProperty> { public: - ~Properties() = default; friend class Entity; private: @@ -677,19 +700,23 @@ public: /** * Move constructor. */ - explicit inline Entity(Entity &&original) : Entity(std::move(original.wrapped)) { - this->properties.wrapped = std::move(original.properties.wrapped); - this->parents.wrapped = std::move(original.parents.wrapped); - this->errors.wrapped = std::move(original.errors.wrapped); - this->warnings.wrapped = std::move(original.warnings.wrapped); - this->infos.wrapped = std::move(original.infos.wrapped); + explicit inline Entity(Entity &&original) : Entity(original.wrapped) { + original.wrapped = nullptr; + original.value.wrapped = nullptr; + original.data_type.wrapped = nullptr; + this->properties = std::move(original.properties); + this->parents = std::move(original.parents); + this->errors = std::move(original.errors); + this->warnings = std::move(original.warnings); + this->infos = std::move(original.infos); }; /** * Move assignment operator. */ auto operator=(Entity &&other) -> Entity & { - this->wrapped = std::move(other.wrapped); + this->wrapped = other.wrapped; + other.wrapped = nullptr; this->data_type = std::move(other.data_type); this->value = std::move(other.value); this->properties = std::move(other.properties); @@ -706,17 +733,17 @@ public: */ auto operator=(const Entity &other) -> Entity & { this->wrapped->CopyFrom(*other.wrapped); - this->data_type.wrapped->CopyFrom(*other.data_type.wrapped); - this->value.wrapped->CopyFrom(*other.value.wrapped); - this->properties.wrapped->CopyFrom(*other.properties.wrapped); - this->parents.wrapped->CopyFrom(*other.parents.wrapped); + this->data_type = other.data_type; + this->value = other.value; + this->properties = other.properties; + this->parents = other.parents; this->file_descriptor.local_path = boost::filesystem::path(other.file_descriptor.local_path); this->file_descriptor.file_transmission_id->CopyFrom( *other.file_descriptor.file_transmission_id); this->file_descriptor.wrapped->CopyFrom(*other.file_descriptor.wrapped); - this->errors.wrapped->CopyFrom(*other.errors.wrapped); - this->warnings.wrapped->CopyFrom(*other.warnings.wrapped); - this->infos.wrapped->CopyFrom(*other.infos.wrapped); + this->errors = other.errors; + this->warnings = other.warnings; + this->infos = other.infos; return *this; } diff --git a/include/caosdb/protobuf_helper.h b/include/caosdb/protobuf_helper.h index 4dfc11c396ded34db6bb2cbed2c938a5402dd7c7..63b2c54cdfaae5eef33705ac7074e11c1caf3653 100644 --- a/include/caosdb/protobuf_helper.h +++ b/include/caosdb/protobuf_helper.h @@ -70,11 +70,7 @@ public: * serialization. */ inline auto operator!=(const ProtoMessageWrapper<P> &other) const noexcept -> bool { - if (this->wrapped != nullptr && other.wrapped != nullptr) { - return this->wrapped->SerializeAsString() != other.wrapped->SerializeAsString(); - } - // only one is nullptr? - return this->wrapped != other.wrapped; + return !(*this == other); } protected: diff --git a/src/caosdb/entity.cpp b/src/caosdb/entity.cpp index bb73431e4af00ed1db48b8f87fa883db6f4f9b31..a3872e4cb2be380adca644b7c7a109c0fec2b3c2 100644 --- a/src/caosdb/entity.cpp +++ b/src/caosdb/entity.cpp @@ -38,8 +38,6 @@ using ProtoFileDescriptor = caosdb::entity::v1alpha1::FileDescriptor; using caosdb::utility::get_arena; using google::protobuf::Arena; -Messages::~Messages() = default; - // Parent ///////////////////////////////////////////////////////////////////// auto Parent::SetName(const std::string &name) -> void { this->wrapped->set_name(name); } diff --git a/test/test_entity.cpp b/test/test_entity.cpp index e456cb4f2d76a06066ec83a7783b1601c30c0415..f800d5a85a878728d73d9bbd23e1d92af99f226f 100644 --- a/test/test_entity.cpp +++ b/test/test_entity.cpp @@ -44,7 +44,11 @@ namespace caosdb::entity { using caosdb::entity::v1alpha1::IdResponse; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using ProtoParent = caosdb::entity::v1alpha1::Parent; +using ProtoProperty = caosdb::entity::v1alpha1::Property; +using ProtoAtomicDataType = caosdb::entity::v1alpha1::AtomicDataType; +using caosdb::entity::v1alpha1::EntityResponse; using caosdb::utility::get_arena; +using ProtoEntityRole = caosdb::entity::v1alpha1::EntityRole; TEST(test_entity, test_parent_setters) { auto parent = Parent(); @@ -138,6 +142,121 @@ TEST(test_entity, test_append_property) { EXPECT_EQ(prop.GetDataType(), same_prop.GetDataType()); } +TEST(test_entity, test_copy_constructor) { + ProtoParent parent; + parent.set_description("the parent desc"); + parent.set_id("the parent id"); + parent.set_name("the parent name"); + ProtoProperty property; + property.set_id("the-prop-id"); + property.set_description("the prop-desc"); + property.set_name("the-prop-name"); + property.mutable_value()->mutable_list_values()->mutable_values()->Add()->set_double_value(25.5); + property.mutable_data_type()->mutable_list_data_type()->set_atomic_data_type(ProtoAtomicDataType::ATOMIC_DATA_TYPE_DOUBLE); + EntityResponse entity_response; + entity_response.mutable_entity()->set_id("the-id"); + entity_response.mutable_entity()->set_name("the-name"); + entity_response.mutable_entity()->set_role(ProtoEntityRole::ENTITY_ROLE_RECORD); + entity_response.mutable_entity()->set_description("the description"); + entity_response.mutable_entity()->set_unit("the-unit"); + entity_response.mutable_entity()->mutable_value()->mutable_scalar_value()->set_string_value("the-value"); + entity_response.mutable_entity()->mutable_version()->set_id("version-id"); + entity_response.mutable_entity()->mutable_data_type()->mutable_reference_data_type()->set_name("refname"); + entity_response.mutable_entity()->mutable_file_descriptor(); + entity_response.mutable_entity()->mutable_properties()->Add()->CopyFrom(property); + entity_response.mutable_entity()->mutable_parents()->Add()->CopyFrom(parent); + + Entity this_entity(&entity_response); + Entity copy_entity(this_entity); + + EXPECT_EQ(this_entity, copy_entity); + EXPECT_EQ(this_entity.GetVersionId(), copy_entity.GetVersionId()); + EXPECT_EQ(this_entity.GetValue(), copy_entity.GetValue()); + EXPECT_EQ(this_entity.GetDataType(), copy_entity.GetDataType()); + EXPECT_EQ(this_entity.GetId(), copy_entity.GetId()); + EXPECT_EQ(this_entity.GetName(), copy_entity.GetName()); + EXPECT_EQ(this_entity.GetDescription(), copy_entity.GetDescription()); + EXPECT_EQ(this_entity.ToString(), copy_entity.ToString()); + EXPECT_EQ(this_entity.GetErrors().ToString(), copy_entity.GetErrors().ToString()); + EXPECT_EQ(this_entity.GetWarnings().ToString(), copy_entity.GetWarnings().ToString()); + EXPECT_EQ(this_entity.GetInfos().ToString(), copy_entity.GetInfos().ToString()); + EXPECT_EQ(this_entity.GetProperties(), copy_entity.GetProperties()); + + // change only description + this_entity.SetDescription("new description"); + EXPECT_NE(this_entity, copy_entity); + EXPECT_EQ(this_entity.GetVersionId(), copy_entity.GetVersionId()); + EXPECT_EQ(this_entity.GetValue(), copy_entity.GetValue()); + EXPECT_EQ(this_entity.GetDataType(), copy_entity.GetDataType()); + EXPECT_EQ(this_entity.GetId(), copy_entity.GetId()); + EXPECT_EQ(this_entity.GetName(), copy_entity.GetName()); + EXPECT_NE(this_entity.GetDescription(), copy_entity.GetDescription()); + EXPECT_NE(this_entity.ToString(), copy_entity.ToString()); +} + +TEST(test_entity, test_move_constructor) { + ProtoParent parent; + parent.set_description("the parent desc"); + parent.set_id("the parent id"); + parent.set_name("the parent name"); + ProtoProperty property; + property.set_id("the-prop-id"); + property.set_description("the prop-desc"); + property.set_name("the-prop-name"); + property.mutable_value()->mutable_list_values()->mutable_values()->Add()->set_double_value(25.5); + property.mutable_data_type()->mutable_list_data_type()->set_atomic_data_type(ProtoAtomicDataType::ATOMIC_DATA_TYPE_DOUBLE); + EntityResponse entity_response; + entity_response.mutable_errors()->Add()->set_code(25); + entity_response.mutable_errors()->Mutable(0)->set_description("asdf"); + entity_response.mutable_warnings()->Add()->set_code(23); + entity_response.mutable_warnings()->Mutable(0)->set_description("asdgsafdg"); + entity_response.mutable_infos()->Add()->set_code(235); + entity_response.mutable_infos()->Mutable(0)->set_description("asdfsad"); + entity_response.mutable_entity()->set_id("the-id"); + entity_response.mutable_entity()->set_name("the-name"); + entity_response.mutable_entity()->set_role(ProtoEntityRole::ENTITY_ROLE_RECORD); + entity_response.mutable_entity()->set_description("the description"); + entity_response.mutable_entity()->set_unit("the-unit"); + entity_response.mutable_entity()->mutable_value()->mutable_scalar_value()->set_string_value("the-value"); + entity_response.mutable_entity()->mutable_version()->set_id("version-id"); + entity_response.mutable_entity()->mutable_data_type()->mutable_reference_data_type()->set_name("refname"); + entity_response.mutable_entity()->mutable_file_descriptor(); + entity_response.mutable_entity()->mutable_properties()->Add()->CopyFrom(property); + entity_response.mutable_entity()->mutable_parents()->Add()->CopyFrom(parent); + + Entity this_entity(&entity_response); + std::string original_string = this_entity.ToString(); + Entity copy_entity(this_entity); + EXPECT_EQ(this_entity, copy_entity); + + Entity move_entity(std::move(this_entity)); + EXPECT_NE(this_entity, copy_entity); // NOLINT + + EXPECT_EQ(move_entity, copy_entity); + EXPECT_EQ(move_entity.GetVersionId(), copy_entity.GetVersionId()); + EXPECT_EQ(move_entity.GetValue(), copy_entity.GetValue()); + EXPECT_EQ(move_entity.GetDataType(), copy_entity.GetDataType()); + EXPECT_EQ(move_entity.GetId(), copy_entity.GetId()); + EXPECT_EQ(move_entity.GetName(), copy_entity.GetName()); + EXPECT_EQ(move_entity.GetDescription(), copy_entity.GetDescription()); + EXPECT_EQ(move_entity.ToString(), copy_entity.ToString()); + EXPECT_EQ(move_entity.GetErrors().ToString(), copy_entity.GetErrors().ToString()); + EXPECT_EQ(move_entity.GetWarnings().ToString(), copy_entity.GetWarnings().ToString()); + EXPECT_EQ(move_entity.GetInfos().ToString(), copy_entity.GetInfos().ToString()); + EXPECT_EQ(move_entity.GetProperties(), copy_entity.GetProperties()); + + // change only description + move_entity.SetDescription("new description"); + EXPECT_NE(move_entity, copy_entity); + EXPECT_EQ(move_entity.GetVersionId(), copy_entity.GetVersionId()); + EXPECT_EQ(move_entity.GetValue(), copy_entity.GetValue()); + EXPECT_EQ(move_entity.GetDataType(), copy_entity.GetDataType()); + EXPECT_EQ(move_entity.GetId(), copy_entity.GetId()); + EXPECT_EQ(move_entity.GetName(), copy_entity.GetName()); + EXPECT_NE(move_entity.GetDescription(), copy_entity.GetDescription()); + EXPECT_NE(move_entity.ToString(), copy_entity.ToString()); +} + TEST(test_entity, test_property_copy_constructor) { Property prop; prop.SetName("prop_name"); @@ -191,15 +310,18 @@ TEST(test_entity, test_property_move_assignment) { prop.SetValue("prop_value"); prop.SetUnit("prop_unit"); prop.SetDataType("prop_dtype"); + const auto prop_string = prop.ToString(); // we compare the moved one with this one const Property copy_prop(prop); Property other_prop = std::move(prop); - // EXPECT_NE(prop, copy_prop); NOLINT - // EXPECT_NE(prop, other_prop); NOLINT - // EXPECT_EQ(prop.ToString(), "{}"); NOLINT + EXPECT_NE(prop, copy_prop); // NOLINT + EXPECT_NE(prop, other_prop); // NOLINT + EXPECT_NE(prop.ToString(), prop_string); // NOLINT + EXPECT_EQ(copy_prop.ToString(), prop_string); + EXPECT_EQ(other_prop.ToString(), prop_string); EXPECT_EQ(copy_prop, other_prop); EXPECT_EQ(copy_prop.GetName(), other_prop.GetName()); EXPECT_EQ(copy_prop.GetId(), other_prop.GetId());