Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] Support serialization through (smart) pointers to abstract classes #13153

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 34 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 = Kratos::shared_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 @@ -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
149 changes: 149 additions & 0 deletions kratos/tests/cpp_tests/sources/test_serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
namespace Kratos::Testing
{

using namespace Kratos;

/*********************************************************************/
/* Helper Functions */
/*********************************************************************/
Expand Down Expand Up @@ -89,6 +91,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 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 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 +252,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 +312,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