1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-06 09:09:40 +02:00

Extracted handling of type ID's from serializer into a separate class

This commit is contained in:
Ivan Savenko
2024-08-23 22:32:02 +00:00
parent 232a759db7
commit 01396b62b7
22 changed files with 175 additions and 177 deletions

View File

@@ -17,8 +17,6 @@ BinaryDeserializer::BinaryDeserializer(IBinaryReader * r): CLoaderBase(r)
{
version = Version::NONE;
reverseEndianness = false;
registerTypes(applier);
}
VCMI_LIB_NAMESPACE_END

View File

@@ -10,7 +10,7 @@
#pragma once
#include "CSerializer.h"
#include "CTypeList.h"
#include "SerializerReflection.h"
#include "ESerializationVersion.h"
#include "../mapObjects/CGHeroInstance.h"
@@ -76,36 +76,6 @@ class BinaryDeserializer : public CLoaderBase
return true;
}
template <typename T, typename Enable = void>
struct ClassObjectCreator
{
static T *invoke(IGameCallback *cb)
{
static_assert(!std::is_base_of_v<GameCallbackHolder, T>, "Cannot call new upon map objects!");
static_assert(!std::is_abstract_v<T>, "Cannot call new upon abstract classes!");
return new T();
}
};
template<typename T>
struct ClassObjectCreator<T, typename std::enable_if_t<std::is_abstract_v<T>>>
{
static T *invoke(IGameCallback *cb)
{
throw std::runtime_error("Something went really wrong during deserialization. Attempted creating an object of an abstract class " + std::string(typeid(T).name()));
}
};
template<typename T>
struct ClassObjectCreator<T, typename std::enable_if_t<std::is_base_of_v<GameCallbackHolder, T> && !std::is_abstract_v<T>>>
{
static T *invoke(IGameCallback *cb)
{
static_assert(!std::is_abstract_v<T>, "Cannot call new upon abstract classes!");
return new T(cb);
}
};
STRONG_INLINE uint32_t readAndCheckLength()
{
uint32_t length;
@@ -119,40 +89,6 @@ class BinaryDeserializer : public CLoaderBase
return length;
}
template <typename Type> class CPointerLoader;
class IPointerLoader
{
public:
virtual Serializeable * loadPtr(CLoaderBase &ar, IGameCallback * cb, uint32_t pid) const =0; //data is pointer to the ACTUAL POINTER
virtual ~IPointerLoader() = default;
template<typename Type> static IPointerLoader *getApplier(const Type * t = nullptr)
{
return new CPointerLoader<Type>();
}
};
template <typename Type>
class CPointerLoader : public IPointerLoader
{
public:
Serializeable * loadPtr(CLoaderBase &ar, IGameCallback * cb, uint32_t pid) const override //data is pointer to the ACTUAL POINTER
{
auto & s = static_cast<BinaryDeserializer &>(ar);
//create new object under pointer
Type * ptr = ClassObjectCreator<Type>::invoke(cb); //does new npT or throws for abstract classes
s.ptrAllocated(ptr, pid);
ptr->serialize(s);
return static_cast<Serializeable*>(ptr);
}
};
CApplier<IPointerLoader> applier;
int write(const void * data, unsigned size);
public:
@@ -360,24 +296,27 @@ public:
uint16_t tid;
load( tid );
typedef typename std::remove_pointer_t<T> npT;
typedef typename std::remove_const_t<npT> ncpT;
if(!tid)
{
typedef typename std::remove_pointer_t<T> npT;
typedef typename std::remove_const_t<npT> ncpT;
data = ClassObjectCreator<ncpT>::invoke(cb);
ptrAllocated(data, pid);
load(*data);
}
else
{
auto * app = applier.getApplier(tid);
auto * app = CSerializationApplier::getInstance().getApplier(tid);
if(app == nullptr)
{
logGlobal->error("load %d %d - no loader exists", tid, pid);
data = nullptr;
return;
}
data = dynamic_cast<T>(app->loadPtr(*this, cb, pid));
auto dataNonConst = dynamic_cast<ncpT*>(app->createPtr(*this, cb));
data = dataNonConst;
ptrAllocated(data, pid);
app->loadPtr(*this, cb, dataNonConst);
}
}

View File

@@ -15,7 +15,6 @@ VCMI_LIB_NAMESPACE_BEGIN
BinarySerializer::BinarySerializer(IBinaryWriter * w): CSaverBase(w)
{
registerTypes(applier);
}
VCMI_LIB_NAMESPACE_END

View File

@@ -11,6 +11,7 @@
#include "CSerializer.h"
#include "CTypeList.h"
#include "SerializerReflection.h"
#include "ESerializationVersion.h"
#include "Serializeable.h"
#include "../mapObjects/CArmedInstance.h"
@@ -80,37 +81,6 @@ class BinarySerializer : public CSaverBase
return false;
}
template <typename T> class CPointerSaver;
class CBasicPointerSaver
{
public:
virtual void savePtr(CSaverBase &ar, const Serializeable *data) const =0;
virtual ~CBasicPointerSaver() = default;
template<typename T> static CBasicPointerSaver *getApplier(const T * t=nullptr)
{
return new CPointerSaver<T>();
}
};
template <typename T>
class CPointerSaver : public CBasicPointerSaver
{
public:
void savePtr(CSaverBase &ar, const Serializeable *data) const override
{
auto & s = static_cast<BinarySerializer &>(ar);
const T *ptr = dynamic_cast<const T*>(data);
//T is most derived known type, it's time to call actual serialize
const_cast<T*>(ptr)->serialize(s);
}
};
CApplier<CBasicPointerSaver> applier;
public:
using Version = ESerializationVersion;
@@ -278,7 +248,7 @@ public:
if(!tid)
save(*data); //if type is unregistered simply write all data in a standard way
else
applier.getApplier(tid)->savePtr(*this, static_cast<const Serializeable*>(data)); //call serializer specific for our real type
CSerializationApplier::getInstance().getApplier(tid)->savePtr(*this, static_cast<const Serializeable*>(data)); //call serializer specific for our real type
}
template < typename T, typename std::enable_if_t < is_serializeable<BinarySerializer, T>::value, int > = 0 >

View File

@@ -69,37 +69,4 @@ public:
}
};
/// Wrapper over CTypeList. Allows execution of templated class T for any type
/// that was resgistered for this applier
template<typename T>
class CApplier : boost::noncopyable
{
std::map<int32_t, std::unique_ptr<T>> apps;
template<typename RegisteredType>
void addApplier(ui16 ID)
{
if(!apps.count(ID))
{
RegisteredType * rtype = nullptr;
apps[ID].reset(T::getApplier(rtype));
}
}
public:
T * getApplier(ui16 ID)
{
if(!apps.count(ID))
throw std::runtime_error("No applier found.");
return apps[ID].get();
}
template<typename Base, typename Derived>
void registerType(const Base * b = nullptr, const Derived * d = nullptr)
{
addApplier<Base>(CTypeList::getInstance().getTypeID<Base>(nullptr));
addApplier<Derived>(CTypeList::getInstance().getTypeID<Derived>(nullptr));
}
};
VCMI_LIB_NAMESPACE_END

View File

@@ -0,0 +1,73 @@
/*
* SerializerReflection.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "SerializerReflection.h"
#include "BinaryDeserializer.h"
#include "BinarySerializer.h"
#include "CTypeList.h"
#include "../registerTypes/RegisterTypes.h"
VCMI_LIB_NAMESPACE_BEGIN
template<typename Type>
class SerializerReflection final : public ISerializerReflection
{
public:
Serializeable * createPtr(BinaryDeserializer &ar, IGameCallback * cb) const override
{
return ClassObjectCreator<Type>::invoke(cb);
}
void loadPtr(BinaryDeserializer &ar, IGameCallback * cb, Serializeable * data) const override
{
auto * realPtr = dynamic_cast<Type *>(data);
realPtr->serialize(ar);
}
void savePtr(BinarySerializer &s, const Serializeable *data) const override
{
const Type *ptr = dynamic_cast<const Type*>(data);
//T is most derived known type, it's time to call actual serialize
const_cast<Type*>(ptr)->serialize(s);
}
};
template<typename RegisteredType>
void CSerializationApplier::addApplier(ui16 ID)
{
if(!apps.count(ID))
{
logGlobal->info("Registering type %d (%s)", ID, typeid(RegisteredType).name());
apps[ID].reset(new SerializerReflection<RegisteredType>);
}
}
template<typename Base, typename Derived>
void CSerializationApplier::registerType(const Base * b, const Derived * d)
{
addApplier<Base>(CTypeList::getInstance().getTypeID<Base>(nullptr));
addApplier<Derived>(CTypeList::getInstance().getTypeID<Derived>(nullptr));
}
CSerializationApplier::CSerializationApplier()
{
registerTypes(*this);
}
CSerializationApplier & CSerializationApplier::getInstance()
{
static CSerializationApplier registry;
return registry;
}
VCMI_LIB_NAMESPACE_END

View File

@@ -0,0 +1,80 @@
/*
* SerializerReflection.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
VCMI_LIB_NAMESPACE_BEGIN
class IGameCallback;
class Serializeable;
class GameCallbackHolder;
class BinaryDeserializer;
class BinarySerializer;
class GameCallbackHolder;
template <typename T, typename Enable = void>
struct ClassObjectCreator
{
static T *invoke(IGameCallback *cb)
{
static_assert(!std::is_base_of_v<GameCallbackHolder, T>, "Cannot call new upon map objects!");
static_assert(!std::is_abstract_v<T>, "Cannot call new upon abstract classes!");
return new T();
}
};
template<typename T>
struct ClassObjectCreator<T, typename std::enable_if_t<std::is_abstract_v<T>>>
{
static T *invoke(IGameCallback *cb)
{
throw std::runtime_error("Something went really wrong during deserialization. Attempted creating an object of an abstract class " + std::string(typeid(T).name()));
}
};
template<typename T>
struct ClassObjectCreator<T, typename std::enable_if_t<std::is_base_of_v<GameCallbackHolder, T> && !std::is_abstract_v<T>>>
{
static T *invoke(IGameCallback *cb)
{
static_assert(!std::is_abstract_v<T>, "Cannot call new upon abstract classes!");
return new T(cb);
}
};
class ISerializerReflection : boost::noncopyable
{
public:
virtual Serializeable * createPtr(BinaryDeserializer &ar, IGameCallback * cb) const =0;
virtual void loadPtr(BinaryDeserializer &ar, IGameCallback * cb, Serializeable * data) const =0;
virtual void savePtr(BinarySerializer &ar, const Serializeable *data) const =0;
virtual ~ISerializerReflection() = default;
};
class DLL_LINKAGE CSerializationApplier
{
std::map<int32_t, std::unique_ptr<ISerializerReflection>> apps;
template<typename RegisteredType>
void addApplier(ui16 ID);
CSerializationApplier();
public:
ISerializerReflection * getApplier(ui16 ID)
{
if(!apps.count(ID))
throw std::runtime_error("No applier found.");
return apps[ID].get();
}
template<typename Base, typename Derived>
void registerType(const Base * b = nullptr, const Derived * d = nullptr);
static CSerializationApplier & getInstance();
};