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:
@@ -17,8 +17,6 @@ BinaryDeserializer::BinaryDeserializer(IBinaryReader * r): CLoaderBase(r)
|
||||
{
|
||||
version = Version::NONE;
|
||||
reverseEndianness = false;
|
||||
|
||||
registerTypes(applier);
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
BinarySerializer::BinarySerializer(IBinaryWriter * w): CSaverBase(w)
|
||||
{
|
||||
registerTypes(applier);
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@@ -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 >
|
||||
|
||||
@@ -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
|
||||
|
||||
73
lib/serializer/SerializerReflection.cpp
Normal file
73
lib/serializer/SerializerReflection.cpp
Normal 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
|
||||
80
lib/serializer/SerializerReflection.h
Normal file
80
lib/serializer/SerializerReflection.h
Normal 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();
|
||||
};
|
||||
Reference in New Issue
Block a user