Skip to content
Merged
53 changes: 45 additions & 8 deletions kratos/includes/serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ class KRATOS_API(KRATOS_CORE) Serializer
//msRegisteredObjects.insert(RegisteredObjectsContainerType::value_type(rName,&pPrototype));
}

static void Deregister(const std::string& rName)
{
msRegisteredObjects.erase(rName);
msRegisteredObjectsName.erase(rName);
}

template<class TDataType>
void load(std::string const & rTag, TDataType& rObject)
{
Expand All @@ -225,8 +231,13 @@ class KRATOS_API(KRATOS_CORE) Serializer
{
if(pointer_type == SP_BASE_CLASS_POINTER)
{
if(!pValue) {
pValue = Kratos::shared_ptr<TDataType>(new TDataType);
if constexpr (!std::is_abstract_v<TDataType>) {
if(!pValue) {
pValue = this->MakeShared<TDataType>();
}
}
else {
KRATOS_ERROR << "Cannot instantiate an abstract class\n";
}
}
else if(pointer_type == SP_DERIVED_CLASS_POINTER)
Expand Down Expand Up @@ -270,8 +281,13 @@ class KRATOS_API(KRATOS_CORE) Serializer
{
if(pointer_type == SP_BASE_CLASS_POINTER)
{
if(!pValue) {
pValue = Kratos::intrusive_ptr<TDataType>(new TDataType);
if constexpr (!std::is_abstract_v<TDataType>) {
if(!pValue) {
pValue = Kratos::intrusive_ptr<TDataType>(new TDataType);
}
}
else {
KRATOS_ERROR << "Cannot instantiate an abstract class\n";
}
}
else if(pointer_type == SP_DERIVED_CLASS_POINTER)
Expand Down Expand Up @@ -315,8 +331,13 @@ class KRATOS_API(KRATOS_CORE) Serializer
{
if(pointer_type == SP_BASE_CLASS_POINTER)
{
if(!pValue) {
pValue = Kratos::unique_ptr<TDataType>(new TDataType);
if constexpr (!std::is_abstract_v<TDataType>) {
if(!pValue) {
pValue = Kratos::unique_ptr<TDataType>(new TDataType);
}
}
else {
KRATOS_ERROR << "Cannot instantiate an abstract class\n";
}
}
else if(pointer_type == SP_DERIVED_CLASS_POINTER)
Expand Down Expand Up @@ -360,8 +381,13 @@ class KRATOS_API(KRATOS_CORE) Serializer
{
if(pointer_type == SP_BASE_CLASS_POINTER)
{
if(!pValue) {
pValue = new TDataType;
if constexpr (!std::is_abstract_v<TDataType>) {
if(!pValue) {
pValue = new TDataType;
}
}
else {
KRATOS_ERROR << "Cannot instantiate an abstract class\n";
}
}
else if(pointer_type == SP_DERIVED_CLASS_POINTER)
Expand Down Expand Up @@ -1410,6 +1436,17 @@ class KRATOS_API(KRATOS_CORE) Serializer
/// Sets the pointer of the stream buffer at the end
void SeekEnd();

template <typename T>
std::shared_ptr<T> MakeShared()
{
if constexpr (std::is_default_constructible_v<T>)
{
return std::make_shared<T>();
} else {
return std::shared_ptr<T>(new T);
}
}

///@}
///@name Private Access
///@{
Expand Down
147 changes: 147 additions & 0 deletions kratos/tests/cpp_tests/sources/test_serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,85 @@ void FillMatrixWithValues(TObjectType& rObject)
}
}

// Two dummy classes for testing (de)serialization of a derived class through
// a pointer to an abstract base class
class AbstractTestClass
{
public:
virtual ~AbstractTestClass() = default;
[[nodiscard]] virtual int foo() const = 0;

private:
friend class Kratos::Serializer;
virtual void save(Serializer&) const = 0;
virtual void load(Serializer&) = 0;

// The following members are required to use this class with intrusive_ptr
friend void intrusive_ptr_add_ref(const AbstractTestClass* pInstance)
{
if (pInstance) ++(pInstance->mRefCount);
}

friend void intrusive_ptr_release(const AbstractTestClass* pInstance)
{
if (pInstance) {
--(pInstance->mRefCount);
if (pInstance->mRefCount == 0) delete pInstance;
}
}

mutable std::size_t mRefCount = 0; // Must be mutable, since previous two members receive a pointer-to-const
};

std::ostream& operator<<(std::ostream& rOStream, const AbstractTestClass&)
{
return rOStream;
}

class DerivedTestClass : public AbstractTestClass
{
public:
explicit DerivedTestClass(int FooNumber = 0) : mFooNumber(FooNumber) {}
~DerivedTestClass() override = default;
[[nodiscard]] int foo() const override { return mFooNumber; }

private:
friend class Kratos::Serializer;

void save(Serializer& rSerializer) const override
{
rSerializer.save("mFooNumber", mFooNumber);
}

void load(Serializer& rSerializer) override
{
rSerializer.load("mFooNumber", mFooNumber);
}

int mFooNumber;
};

class ScopedTestClassRegistration
{
public:
ScopedTestClassRegistration()
{
Serializer::Register("DerivedTestClass", DerivedTestClass{});
}

~ScopedTestClassRegistration()
{
Serializer::Deregister("DerivedTestClass");
}

// Since the destructor has been defined, use the Rule of Five
ScopedTestClassRegistration(const ScopedTestClassRegistration&) = delete;
ScopedTestClassRegistration& operator=(const ScopedTestClassRegistration&) = delete;

ScopedTestClassRegistration(ScopedTestClassRegistration&&) noexcept = default;
ScopedTestClassRegistration& operator=(ScopedTestClassRegistration&&) noexcept = default;
};

/*********************************************************************/
/* Testing the Datatypes that for which the
"KRATOS_SERIALIZATION_DIRECT_LOAD" macro is used */
Expand Down Expand Up @@ -171,6 +250,42 @@ KRATOS_TEST_CASE_IN_SUITE(SerializerLongLong, KratosCoreFastSuite)
/* Testing the Datatypes that have a specific save/load implementation */
/*********************************************************************/

KRATOS_TEST_CASE_IN_SUITE(SerializerRawOwningPointerToAbstractBase, KratosCoreFastSuiteWithoutKernel)
{
StreamSerializer serializer;
ScopedTestClassRegistration scoped_registration;

const std::string tag_string("TestString");
const AbstractTestClass* p_instance = new DerivedTestClass{42};
serializer.save(tag_string, p_instance);
delete p_instance;
p_instance = nullptr; // Avoid having a dangling pointer

AbstractTestClass* p_loaded_instance = nullptr;
serializer.load(tag_string, p_loaded_instance);

ASSERT_NE(p_loaded_instance, nullptr);
KRATOS_EXPECT_EQ(p_loaded_instance->foo(), 42);

delete p_loaded_instance;
}

KRATOS_TEST_CASE_IN_SUITE(SerializerKratosUniquePtrToAbstractBase, KratosCoreFastSuiteWithoutKernel)
{
StreamSerializer serializer;
ScopedTestClassRegistration scoped_registration;

const std::string tag_string("TestString");
const Kratos::unique_ptr<AbstractTestClass> p_instance = Kratos::make_unique<DerivedTestClass>(42);
serializer.save(tag_string, p_instance);

Kratos::unique_ptr<AbstractTestClass> p_loaded_instance;
serializer.load(tag_string, p_loaded_instance);

ASSERT_NE(p_loaded_instance, nullptr);
KRATOS_EXPECT_EQ(p_loaded_instance->foo(), 42);
}

KRATOS_TEST_CASE_IN_SUITE(SerializerKratosSharedPtr, KratosCoreFastSuite)
{
StreamSerializer serializer;
Expand All @@ -195,6 +310,38 @@ KRATOS_TEST_CASE_IN_SUITE(SerializerKratosSharedPtr, KratosCoreFastSuite)
KRATOS_EXPECT_EQ((*p_loaded_array)[i], (*p_array)[i]);
}

KRATOS_TEST_CASE_IN_SUITE(SerializerKratosSharedPtrToAbstractBase, KratosCoreFastSuiteWithoutKernel)
{
StreamSerializer serializer;
ScopedTestClassRegistration scoped_registration;

const std::string tag_string("TestString");
const Kratos::shared_ptr<AbstractTestClass> p_instance = Kratos::make_shared<DerivedTestClass>(42);
serializer.save(tag_string, p_instance);

Kratos::shared_ptr<AbstractTestClass> p_loaded_instance;
serializer.load(tag_string, p_loaded_instance);

ASSERT_NE(p_loaded_instance, nullptr);
KRATOS_EXPECT_EQ(p_loaded_instance->foo(), 42);
}

KRATOS_TEST_CASE_IN_SUITE(SerializerKratosIntrusivePtrToAbstractBase, KratosCoreFastSuiteWithoutKernel)
{
StreamSerializer serializer;
ScopedTestClassRegistration scoped_registration;

const std::string tag_string("TestString");
const Kratos::intrusive_ptr<AbstractTestClass> p_instance = Kratos::make_intrusive<DerivedTestClass>(42);
serializer.save(tag_string, p_instance);

Kratos::intrusive_ptr<AbstractTestClass> p_loaded_instance;
serializer.load(tag_string, p_loaded_instance);

ASSERT_NE(p_loaded_instance, nullptr);
KRATOS_EXPECT_EQ(p_loaded_instance->foo(), 42);
}

KRATOS_TEST_CASE_IN_SUITE(SerializerStdArray, KratosCoreFastSuite)
{
using Array5Dbl = std::array<double,5>;
Expand Down
Loading