(WIP) Some reflection stuff.
This commit is contained in:
parent
473a3aafab
commit
cf0d43d950
1
LibConf
1
LibConf
@ -52,6 +52,7 @@ src_files = Split("""
|
|||||||
source/pipeline.cpp
|
source/pipeline.cpp
|
||||||
source/render_pass.cpp
|
source/render_pass.cpp
|
||||||
source/semaphore.cpp
|
source/semaphore.cpp
|
||||||
|
source/serialization.cpp
|
||||||
source/shader_module.cpp
|
source/shader_module.cpp
|
||||||
source/swapchain.cpp
|
source/swapchain.cpp
|
||||||
source/texture.cpp
|
source/texture.cpp
|
||||||
|
105
include/iwa/serialization.hpp
Normal file
105
include/iwa/serialization.hpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(IWA_SERIALIZATION_HPP_INCLUDED)
|
||||||
|
#define IWA_SERIALIZATION_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#include <mijin/io/stream.hpp>
|
||||||
|
#include <yaml-cpp/yaml.h>
|
||||||
|
#include "iwa/type_meta.hpp"
|
||||||
|
|
||||||
|
namespace iwa
|
||||||
|
{
|
||||||
|
enum class SerializationFormat
|
||||||
|
{
|
||||||
|
YAML
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SerializeOptions
|
||||||
|
{
|
||||||
|
mijin::Stream* stream;
|
||||||
|
SerializationFormat format = SerializationFormat::YAML;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DeserializeOptions
|
||||||
|
{
|
||||||
|
mijin::Stream* stream;
|
||||||
|
SerializationFormat format = SerializationFormat::YAML;
|
||||||
|
};
|
||||||
|
|
||||||
|
void structToYAML(const void* data, const Struct& type, YAML::Emitter& emitter);
|
||||||
|
void structFromYAML(void* data, const Struct& type, const YAML::Node& node);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void valueToYAML(const T& value, YAML::Emitter& emitter)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_fundamental_v<std::remove_reference_t<T>>)
|
||||||
|
{
|
||||||
|
emitter << value;
|
||||||
|
}
|
||||||
|
else if constexpr (BuildStruct<T>::value)
|
||||||
|
{
|
||||||
|
structToYAML(&value, reflectStruct<T>(), emitter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void valueFromYAML(T& value, const YAML::Node& node)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_fundamental_v<std::remove_reference_t<T>>)
|
||||||
|
{
|
||||||
|
value = node.as<T>();
|
||||||
|
}
|
||||||
|
else if constexpr (BuildStruct<T>::value)
|
||||||
|
{
|
||||||
|
structFromYAML(&value, reflectStruct<T>(), node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void serializeYAML(const T& value, const SerializeOptions& options)
|
||||||
|
{
|
||||||
|
YAML::Emitter emitter;
|
||||||
|
valueToYAML(value, emitter);
|
||||||
|
if (mijin::StreamError error = options.stream->writeText(emitter.c_str()); error != mijin::StreamError::SUCCESS)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Error writing YAML.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void deserializeYAML(T& value, const DeserializeOptions& options)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
if (mijin::StreamError error = options.stream->readAsString(text); error != mijin::StreamError::SUCCESS)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Error reading text.");
|
||||||
|
}
|
||||||
|
const YAML::Node node = YAML::Load(text);
|
||||||
|
valueFromYAML(value, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void serialize(const T& value, const SerializeOptions& options)
|
||||||
|
{
|
||||||
|
switch (options.format)
|
||||||
|
{
|
||||||
|
case SerializationFormat::YAML:
|
||||||
|
serializeYAML(value, options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void deserialize(T& value, const DeserializeOptions& options)
|
||||||
|
{
|
||||||
|
switch (options.format)
|
||||||
|
{
|
||||||
|
case SerializationFormat::YAML:
|
||||||
|
deserializeYAML(value, options);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !defined(IWA_SERIALIZATION_HPP_INCLUDED)
|
@ -4,14 +4,285 @@
|
|||||||
#if !defined(IWA_CLASS_HPP_INCLUDED)
|
#if !defined(IWA_CLASS_HPP_INCLUDED)
|
||||||
#define IWA_CLASS_HPP_INCLUDED
|
#define IWA_CLASS_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <source_location>
|
#include <source_location>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <mijin/util/bitflags.hpp>
|
||||||
#include <mijin/util/traits.hpp>
|
#include <mijin/util/traits.hpp>
|
||||||
|
|
||||||
namespace iwa
|
namespace iwa
|
||||||
{
|
{
|
||||||
|
class Class;
|
||||||
|
class Struct;
|
||||||
|
struct Type;
|
||||||
|
|
||||||
|
template<typename TStruct>
|
||||||
|
struct BuildStruct : std::false_type {};
|
||||||
|
|
||||||
|
#define IWA_STRUCT_REFL_BEGIN(Type) \
|
||||||
|
template<> \
|
||||||
|
struct iwa::BuildStruct<Type> : std::true_type \
|
||||||
|
{ \
|
||||||
|
void operator()([[maybe_unused]] StructBuilder<Type>& builder) noexcept \
|
||||||
|
{ \
|
||||||
|
using reflected_t [[maybe_unused]] = Type;
|
||||||
|
#define IWA_STRUCT_REFL_END() \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
#define IWA_STRUCT_REFL_PROPERTY(Name) \
|
||||||
|
builder.addProperty(#Name, &reflected_t::Name, offsetof(reflected_t, Name))
|
||||||
|
|
||||||
|
struct ClassType
|
||||||
|
{
|
||||||
|
const Class* clazz;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StructType
|
||||||
|
{
|
||||||
|
const Struct* struct_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InnerType
|
||||||
|
{
|
||||||
|
std::unique_ptr<Type> type;
|
||||||
|
|
||||||
|
InnerType() noexcept : type(std::make_unique<Type>()) {}
|
||||||
|
InnerType(const InnerType& other) noexcept : type(std::make_unique<Type>(*other.type)) {}
|
||||||
|
InnerType(InnerType&&) noexcept = default;
|
||||||
|
|
||||||
|
InnerType& operator=(const InnerType& other) noexcept
|
||||||
|
{
|
||||||
|
type = std::make_unique<Type>(*other.type);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
InnerType& operator=(InnerType&&) noexcept = default;
|
||||||
|
|
||||||
|
template<typename TType>
|
||||||
|
InnerType& operator=(TType&& newType) noexcept
|
||||||
|
{
|
||||||
|
type = std::make_unique<Type>(std::forward<TType>(newType));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Type& operator*() noexcept { return *type; }
|
||||||
|
[[nodiscard]] const Type& operator*() const noexcept { return *type; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ArrayType
|
||||||
|
{
|
||||||
|
InnerType baseType;
|
||||||
|
std::size_t arraySize = 0;
|
||||||
|
std::size_t arrayStride = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PointerType
|
||||||
|
{
|
||||||
|
InnerType baseType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReferenceType
|
||||||
|
{
|
||||||
|
InnerType baseType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstType
|
||||||
|
{
|
||||||
|
InnerType baseType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VolatileType
|
||||||
|
{
|
||||||
|
InnerType baseType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VoidType {};
|
||||||
|
|
||||||
|
struct BoolType {};
|
||||||
|
|
||||||
|
enum class IntBits
|
||||||
|
{
|
||||||
|
_8,
|
||||||
|
_16,
|
||||||
|
_32,
|
||||||
|
_64
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr IntBits intBitsFromSize(std::size_t size) noexcept
|
||||||
|
{
|
||||||
|
switch (size)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
return IntBits::_8;
|
||||||
|
case 16:
|
||||||
|
return IntBits::_16;
|
||||||
|
case 32:
|
||||||
|
return IntBits::_32;
|
||||||
|
case 64:
|
||||||
|
return IntBits::_64;
|
||||||
|
}
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IntType
|
||||||
|
{
|
||||||
|
IntBits bits;
|
||||||
|
bool isSigned = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FloatBits
|
||||||
|
{
|
||||||
|
_32,
|
||||||
|
_64
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr FloatBits floatBitsFromSize(std::size_t size) noexcept
|
||||||
|
{
|
||||||
|
switch (size)
|
||||||
|
{
|
||||||
|
case 32:
|
||||||
|
return FloatBits::_32;
|
||||||
|
case 64:
|
||||||
|
return FloatBits::_64;
|
||||||
|
}
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FloatType
|
||||||
|
{
|
||||||
|
FloatBits bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CharType
|
||||||
|
{
|
||||||
|
WCHAR_T,
|
||||||
|
CHAR8_T,
|
||||||
|
CHAR16_T,
|
||||||
|
CHAR32_T
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class STDType
|
||||||
|
{
|
||||||
|
STRING
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BitFlagsType
|
||||||
|
{
|
||||||
|
// InnerType baseType;
|
||||||
|
unsigned maxBits = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using type_t = std::variant<VoidType, BoolType, IntType, FloatType, CharType, ArrayType, ClassType, StructType, PointerType, ReferenceType, ConstType, VolatileType, STDType, BitFlagsType>;
|
||||||
|
struct Type : type_t
|
||||||
|
{
|
||||||
|
template<typename... TArgs>
|
||||||
|
constexpr Type(TArgs&&... args) noexcept : type_t(std::forward<TArgs>(args)...) {}
|
||||||
|
constexpr Type(const Type&) noexcept = default;
|
||||||
|
constexpr Type(Type&&) noexcept = default;
|
||||||
|
|
||||||
|
constexpr Type& operator=(const Type&) noexcept = default;
|
||||||
|
constexpr Type& operator=(Type&&) noexcept = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TStruct>
|
||||||
|
const Struct& reflectStruct() noexcept;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Type getType() noexcept
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<T, void>)
|
||||||
|
{
|
||||||
|
return VoidType();
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, bool>)
|
||||||
|
{
|
||||||
|
return BoolType();
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, wchar_t>)
|
||||||
|
{
|
||||||
|
return CharType::WCHAR_T;
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, char8_t>)
|
||||||
|
{
|
||||||
|
return CharType::CHAR8_T;
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, char16_t>)
|
||||||
|
{
|
||||||
|
return CharType::CHAR16_T;
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, char32_t>)
|
||||||
|
{
|
||||||
|
return CharType::CHAR32_T;
|
||||||
|
}
|
||||||
|
if constexpr (std::is_integral_v<T>)
|
||||||
|
{
|
||||||
|
return IntType{
|
||||||
|
.bits = intBitsFromSize(sizeof(T)),
|
||||||
|
.isSigned = std::is_signed_v<T>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_floating_point_v<T>)
|
||||||
|
{
|
||||||
|
return FloatType{
|
||||||
|
.bits = floatBitsFromSize(sizeof(T))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_pointer_v<T>)
|
||||||
|
{
|
||||||
|
return PointerType{
|
||||||
|
.baseType = getType<std::remove_pointer_t<T>>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_reference_v<T>)
|
||||||
|
{
|
||||||
|
return PointerType{
|
||||||
|
.baseType = getType<std::remove_reference_t<T>>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_const_v<T>)
|
||||||
|
{
|
||||||
|
return ConstType{
|
||||||
|
.baseType = getType<std::remove_const_t<T>>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_volatile_v<T>)
|
||||||
|
{
|
||||||
|
return ConstType{
|
||||||
|
.baseType = getType<std::remove_volatile_t<T>>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_array_v<T>)
|
||||||
|
{
|
||||||
|
using base_t = std::remove_extent_t<T>;
|
||||||
|
return ArrayType{
|
||||||
|
.baseType = getType<base_t>(),
|
||||||
|
.arraySize = sizeof(T) / sizeof(base_t),
|
||||||
|
.arrayStride = sizeof(base_t)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (mijin::is_bitflags_v<T>)
|
||||||
|
{
|
||||||
|
return BitFlagsType{
|
||||||
|
// .baseType = getType<T::bits_t>()
|
||||||
|
.maxBits = 8 * sizeof(T)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (BuildStruct<T>::value)
|
||||||
|
{
|
||||||
|
return StructType{
|
||||||
|
.struct_ = &reflectStruct<T>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, std::string>)
|
||||||
|
{
|
||||||
|
return STDType::STRING;
|
||||||
|
}
|
||||||
|
return VoidType(); // TODO!
|
||||||
|
}
|
||||||
|
|
||||||
struct Function
|
struct Function
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
@ -20,6 +291,9 @@ struct Function
|
|||||||
struct Property
|
struct Property
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
|
Type type;
|
||||||
|
int Struct::* member;
|
||||||
|
std::size_t offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClassCreationArgs
|
struct ClassCreationArgs
|
||||||
@ -83,10 +357,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename TField>
|
template<typename TField>
|
||||||
ClassBuilder& addProperty(const char* name, TField TObject::* field)
|
ClassBuilder& addProperty(const char* name, TField TObject::* property, std::size_t offset)
|
||||||
{
|
{
|
||||||
(void) field;
|
mProperties.push_back({
|
||||||
mProperties.push_back({.name = name});
|
.name = name,
|
||||||
|
.type = getType<TField>(),
|
||||||
|
.member = reinterpret_cast<int Struct::*>(property),
|
||||||
|
.offset = offset
|
||||||
|
});
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -108,16 +386,19 @@ namespace \
|
|||||||
struct StructCreationArgs
|
struct StructCreationArgs
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::vector<Property> properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Struct
|
class Struct
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string mName;
|
std::string mName;
|
||||||
|
std::vector<Property> mProperties;
|
||||||
public:
|
public:
|
||||||
explicit Struct(StructCreationArgs args) noexcept : mName(std::move(args.name)) {}
|
explicit Struct(StructCreationArgs args) noexcept : mName(std::move(args.name)), mProperties(std::move(args.properties)) {}
|
||||||
|
|
||||||
[[nodiscard]] const std::string& getName() const noexcept { return mName; }
|
[[nodiscard]] const std::string& getName() const noexcept { return mName; }
|
||||||
|
[[nodiscard]] const std::vector<Property> getProperties() const noexcept { return mProperties; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename TStruct>
|
template<typename TStruct>
|
||||||
@ -125,13 +406,27 @@ class StructBuilder
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string mName;
|
std::string mName;
|
||||||
|
std::vector<Property> mProperties;
|
||||||
public:
|
public:
|
||||||
explicit StructBuilder(std::string name) noexcept : mName(std::move(name)) {}
|
explicit StructBuilder(std::string name) noexcept : mName(std::move(name)) {}
|
||||||
|
|
||||||
|
template<typename TField>
|
||||||
|
StructBuilder& addProperty(const char* name, TField TStruct::* property, std::size_t offset)
|
||||||
|
{
|
||||||
|
mProperties.push_back({
|
||||||
|
.name = name,
|
||||||
|
.type = getType<TField>(),
|
||||||
|
.member = reinterpret_cast<int Struct::*>(property),
|
||||||
|
.offset = offset
|
||||||
|
});
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] Struct make() noexcept
|
[[nodiscard]] Struct make() noexcept
|
||||||
{
|
{
|
||||||
return Struct({
|
return Struct({
|
||||||
.name = std::move(mName)
|
.name = std::move(mName),
|
||||||
|
.properties = std::move(mProperties)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -139,12 +434,6 @@ public:
|
|||||||
void registerClass(const Class* clazz) noexcept;
|
void registerClass(const Class* clazz) noexcept;
|
||||||
[[nodiscard]] std::span<const Class*> getRegisteredClasses() noexcept;
|
[[nodiscard]] std::span<const Class*> getRegisteredClasses() noexcept;
|
||||||
|
|
||||||
template<typename TStruct>
|
|
||||||
void buildStruct(StructBuilder<TStruct>& /* builder */) noexcept
|
|
||||||
{
|
|
||||||
static_assert(mijin::always_false_v<TStruct>, "Missing buildStruct() function.");
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace impl
|
namespace impl
|
||||||
{
|
{
|
||||||
[[nodiscard]] constexpr std::string getStructNameFromSourceLocation(const std::source_location& sourceLocation) noexcept
|
[[nodiscard]] constexpr std::string getStructNameFromSourceLocation(const std::source_location& sourceLocation) noexcept
|
||||||
@ -175,14 +464,277 @@ namespace impl
|
|||||||
template<typename TStruct>
|
template<typename TStruct>
|
||||||
const Struct& reflectStruct() noexcept
|
const Struct& reflectStruct() noexcept
|
||||||
{
|
{
|
||||||
|
static_assert(BuildStruct<TStruct>::value, "No BuildStruct for this type.");
|
||||||
static const Struct theStruct = [](std::string&& name)
|
static const Struct theStruct = [](std::string&& name)
|
||||||
{
|
{
|
||||||
StructBuilder<TStruct> builder(std::move(name));
|
StructBuilder<TStruct> builder(std::move(name));
|
||||||
buildStruct(builder);
|
BuildStruct<TStruct>()(builder);
|
||||||
return builder.make();
|
return builder.make();
|
||||||
}(impl::getStructNameFromSourceLocation(std::source_location::current()));
|
}(impl::getStructNameFromSourceLocation(std::source_location::current()));
|
||||||
return theStruct;
|
return theStruct;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename TVisitor>
|
||||||
|
constexpr decltype(auto) visitIntValue(TVisitor&& visitor, const void* value, const IntType& type)
|
||||||
|
{
|
||||||
|
using return_t = std::result_of_t<TVisitor(const int&)>;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<return_t, void>)
|
||||||
|
{
|
||||||
|
if (type.isSigned)
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case IntBits::_8:
|
||||||
|
return std::invoke(visitor, *static_cast<const std::int8_t*>(value));
|
||||||
|
case IntBits::_16:
|
||||||
|
return std::invoke(visitor, *static_cast<const std::int16_t*>(value));
|
||||||
|
case IntBits::_32:
|
||||||
|
return std::invoke(visitor, *static_cast<const std::int32_t*>(value));
|
||||||
|
case IntBits::_64:
|
||||||
|
return std::invoke(visitor, *static_cast<const std::int64_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case IntBits::_8:
|
||||||
|
return std::invoke(visitor, *static_cast<const std::uint8_t*>(value));
|
||||||
|
case IntBits::_16:
|
||||||
|
return std::invoke(visitor, *static_cast<const std::uint16_t*>(value));
|
||||||
|
case IntBits::_32:
|
||||||
|
return std::invoke(visitor, *static_cast<const std::uint32_t*>(value));
|
||||||
|
case IntBits::_64:
|
||||||
|
return std::invoke(visitor, *static_cast<const std::uint64_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (type.isSigned)
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case IntBits::_8:
|
||||||
|
std::invoke(visitor, *static_cast<const std::int8_t*>(value));
|
||||||
|
case IntBits::_16:
|
||||||
|
std::invoke(visitor, *static_cast<const std::int16_t*>(value));
|
||||||
|
case IntBits::_32:
|
||||||
|
std::invoke(visitor, *static_cast<const std::int32_t*>(value));
|
||||||
|
case IntBits::_64:
|
||||||
|
std::invoke(visitor, *static_cast<const std::int64_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case IntBits::_8:
|
||||||
|
std::invoke(visitor, *static_cast<const std::uint8_t*>(value));
|
||||||
|
case IntBits::_16:
|
||||||
|
std::invoke(visitor, *static_cast<const std::uint16_t*>(value));
|
||||||
|
case IntBits::_32:
|
||||||
|
std::invoke(visitor, *static_cast<const std::uint32_t*>(value));
|
||||||
|
case IntBits::_64:
|
||||||
|
std::invoke(visitor, *static_cast<const std::uint64_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TVisitor>
|
||||||
|
constexpr decltype(auto) visitFloatValue(TVisitor&& visitor, const void* value, const FloatType& type)
|
||||||
|
{
|
||||||
|
using return_t = std::result_of_t<TVisitor(const float&)>;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<return_t, void>)
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case FloatBits::_32:
|
||||||
|
return std::invoke(visitor, *static_cast<const float*>(value));
|
||||||
|
case FloatBits::_64:
|
||||||
|
return std::invoke(visitor, *static_cast<const double*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case FloatBits::_32:
|
||||||
|
std::invoke(visitor, *static_cast<const float*>(value));
|
||||||
|
case FloatBits::_64:
|
||||||
|
std::invoke(visitor, *static_cast<const double*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TVisitor>
|
||||||
|
constexpr decltype(auto) visitCharValue(TVisitor&& visitor, const void* value, CharType type)
|
||||||
|
{
|
||||||
|
using return_t = std::result_of_t<TVisitor(const char8_t&)>;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<return_t, void>)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CharType::WCHAR_T:
|
||||||
|
return std::invoke(visitor, *static_cast<const wchar_t*>(value));
|
||||||
|
case CharType::CHAR8_T:
|
||||||
|
return std::invoke(visitor, *static_cast<const char8_t*>(value));
|
||||||
|
case CharType::CHAR16_T:
|
||||||
|
return std::invoke(visitor, *static_cast<const char16_t*>(value));
|
||||||
|
case CharType::CHAR32_T:
|
||||||
|
return std::invoke(visitor, *static_cast<const char32_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CharType::WCHAR_T:
|
||||||
|
std::invoke(visitor, *static_cast<const wchar_t*>(value));
|
||||||
|
case CharType::CHAR8_T:
|
||||||
|
std::invoke(visitor, *static_cast<const char8_t*>(value));
|
||||||
|
case CharType::CHAR16_T:
|
||||||
|
std::invoke(visitor, *static_cast<const char16_t*>(value));
|
||||||
|
case CharType::CHAR32_T:
|
||||||
|
std::invoke(visitor, *static_cast<const char32_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TVisitor>
|
||||||
|
constexpr decltype(auto) visitIntValue(TVisitor&& visitor, void* value, const IntType& type)
|
||||||
|
{
|
||||||
|
using return_t = std::result_of_t<TVisitor(int&)>;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<return_t, void>)
|
||||||
|
{
|
||||||
|
if (type.isSigned)
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case IntBits::_8:
|
||||||
|
return std::invoke(visitor, *static_cast<std::int8_t*>(value));
|
||||||
|
case IntBits::_16:
|
||||||
|
return std::invoke(visitor, *static_cast<std::int16_t*>(value));
|
||||||
|
case IntBits::_32:
|
||||||
|
return std::invoke(visitor, *static_cast<std::int32_t*>(value));
|
||||||
|
case IntBits::_64:
|
||||||
|
return std::invoke(visitor, *static_cast<std::int64_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case IntBits::_8:
|
||||||
|
return std::invoke(visitor, *static_cast<std::uint8_t*>(value));
|
||||||
|
case IntBits::_16:
|
||||||
|
return std::invoke(visitor, *static_cast<std::uint16_t*>(value));
|
||||||
|
case IntBits::_32:
|
||||||
|
return std::invoke(visitor, *static_cast<std::uint32_t*>(value));
|
||||||
|
case IntBits::_64:
|
||||||
|
return std::invoke(visitor, *static_cast<std::uint64_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (type.isSigned)
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case IntBits::_8:
|
||||||
|
std::invoke(visitor, *static_cast<std::int8_t*>(value));
|
||||||
|
case IntBits::_16:
|
||||||
|
std::invoke(visitor, *static_cast<std::int16_t*>(value));
|
||||||
|
case IntBits::_32:
|
||||||
|
std::invoke(visitor, *static_cast<std::int32_t*>(value));
|
||||||
|
case IntBits::_64:
|
||||||
|
std::invoke(visitor, *static_cast<std::int64_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case IntBits::_8:
|
||||||
|
std::invoke(visitor, *static_cast<std::uint8_t*>(value));
|
||||||
|
case IntBits::_16:
|
||||||
|
std::invoke(visitor, *static_cast<std::uint16_t*>(value));
|
||||||
|
case IntBits::_32:
|
||||||
|
std::invoke(visitor, *static_cast<std::uint32_t*>(value));
|
||||||
|
case IntBits::_64:
|
||||||
|
std::invoke(visitor, *static_cast<std::uint64_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TVisitor>
|
||||||
|
constexpr decltype(auto) visitFloatValue(TVisitor&& visitor, void* value, const FloatType& type)
|
||||||
|
{
|
||||||
|
using return_t = std::result_of_t<TVisitor(float&)>;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<return_t, void>)
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case FloatBits::_32:
|
||||||
|
return std::invoke(visitor, *static_cast<float*>(value));
|
||||||
|
case FloatBits::_64:
|
||||||
|
return std::invoke(visitor, *static_cast<double*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (type.bits)
|
||||||
|
{
|
||||||
|
case FloatBits::_32:
|
||||||
|
std::invoke(visitor, *static_cast<float*>(value));
|
||||||
|
case FloatBits::_64:
|
||||||
|
std::invoke(visitor, *static_cast<double*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TVisitor>
|
||||||
|
constexpr decltype(auto) visitCharValue(TVisitor&& visitor, void* value, CharType type)
|
||||||
|
{
|
||||||
|
using return_t = std::result_of_t<TVisitor(char8_t&)>;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<return_t, void>)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CharType::WCHAR_T:
|
||||||
|
return std::invoke(visitor, *static_cast<wchar_t*>(value));
|
||||||
|
case CharType::CHAR8_T:
|
||||||
|
return std::invoke(visitor, *static_cast<char8_t*>(value));
|
||||||
|
case CharType::CHAR16_T:
|
||||||
|
return std::invoke(visitor, *static_cast<char16_t*>(value));
|
||||||
|
case CharType::CHAR32_T:
|
||||||
|
return std::invoke(visitor, *static_cast<char32_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CharType::WCHAR_T:
|
||||||
|
std::invoke(visitor, *static_cast<wchar_t*>(value));
|
||||||
|
case CharType::CHAR8_T:
|
||||||
|
std::invoke(visitor, *static_cast<char8_t*>(value));
|
||||||
|
case CharType::CHAR16_T:
|
||||||
|
std::invoke(visitor, *static_cast<char16_t*>(value));
|
||||||
|
case CharType::CHAR32_T:
|
||||||
|
std::invoke(visitor, *static_cast<char32_t*>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !defined(IWA_CLASS_HPP_INCLUDED)
|
#endif // !defined(IWA_CLASS_HPP_INCLUDED)
|
||||||
|
@ -81,13 +81,18 @@ public:
|
|||||||
mijin::Signal<> mouseLeft;
|
mijin::Signal<> mouseLeft;
|
||||||
mijin::Signal<> closeRequested;
|
mijin::Signal<> closeRequested;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// reflection
|
// reflection
|
||||||
template<>
|
// IWA_STRUCT_REFL_BEGIN(iwa::WindowCreationFlags)
|
||||||
inline void buildStruct<WindowCreationArgs>(StructBuilder<WindowCreationArgs>& builder) noexcept
|
// // IWA_STRUCT_REFL_PROPERTY(hidden)
|
||||||
{
|
// IWA_STRUCT_REFL_END()
|
||||||
(void) builder;
|
|
||||||
}
|
IWA_STRUCT_REFL_BEGIN(iwa::WindowCreationArgs)
|
||||||
}
|
IWA_STRUCT_REFL_PROPERTY(title);
|
||||||
|
IWA_STRUCT_REFL_PROPERTY(flags);
|
||||||
|
IWA_STRUCT_REFL_PROPERTY(width);
|
||||||
|
IWA_STRUCT_REFL_PROPERTY(height);
|
||||||
|
IWA_STRUCT_REFL_END()
|
||||||
|
|
||||||
#endif //IWA_WINDOW_HPP_INCLUDED
|
#endif //IWA_WINDOW_HPP_INCLUDED
|
||||||
|
247
source/serialization.cpp
Normal file
247
source/serialization.cpp
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
#include "iwa/serialization.hpp"
|
||||||
|
|
||||||
|
namespace iwa
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void propertyToYAML(const void* data, const iwa::Type& type, YAML::Emitter& emitter);
|
||||||
|
void propertyFromYAML(void* data, const iwa::Type& type, const YAML::Node& node);
|
||||||
|
|
||||||
|
struct PropertyEmitVisitor
|
||||||
|
{
|
||||||
|
const void* data;
|
||||||
|
YAML::Emitter& emitter;
|
||||||
|
|
||||||
|
void operator()(const VoidType&)
|
||||||
|
{
|
||||||
|
emitter << YAML::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const BoolType&)
|
||||||
|
{
|
||||||
|
emitter << *static_cast<const bool*>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const IntType& type)
|
||||||
|
{
|
||||||
|
visitIntValue([&](auto&& value)
|
||||||
|
{
|
||||||
|
emitter << value;
|
||||||
|
}, data, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const FloatType& type)
|
||||||
|
{
|
||||||
|
visitFloatValue([&](auto&& value)
|
||||||
|
{
|
||||||
|
emitter << value;
|
||||||
|
}, data, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const CharType& type)
|
||||||
|
{
|
||||||
|
visitCharValue([&](auto&& value)
|
||||||
|
{
|
||||||
|
emitter << value;
|
||||||
|
}, data, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const ArrayType& type)
|
||||||
|
{
|
||||||
|
emitter << YAML::BeginSeq;
|
||||||
|
for (std::size_t idx = 0; idx < type.arraySize; ++idx)
|
||||||
|
{
|
||||||
|
const std::byte* ele = static_cast<const std::byte*>(data) + (idx * type.arrayStride);
|
||||||
|
propertyToYAML(ele, *type.baseType, emitter);
|
||||||
|
}
|
||||||
|
emitter << YAML::EndSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const ClassType&)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Not implemented.");
|
||||||
|
emitter << YAML::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const StructType& type)
|
||||||
|
{
|
||||||
|
structToYAML(data, *type.struct_, emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const PointerType&)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Not implemented.");
|
||||||
|
emitter << YAML::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const ReferenceType&)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Not implemented.");
|
||||||
|
emitter << YAML::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const ConstType& type)
|
||||||
|
{
|
||||||
|
propertyToYAML(data, *type.baseType, emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const VolatileType& type)
|
||||||
|
{
|
||||||
|
propertyToYAML(data, *type.baseType, emitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const STDType& type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case STDType::STRING:
|
||||||
|
emitter << *static_cast<const std::string*>(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const BitFlagsType&)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Not implemented.");
|
||||||
|
emitter << YAML::Null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PropertyParseVisitor
|
||||||
|
{
|
||||||
|
void* data;
|
||||||
|
const YAML::Node& node;
|
||||||
|
|
||||||
|
void operator()(const VoidType&) { /* ??? */ }
|
||||||
|
|
||||||
|
void operator()(const BoolType&)
|
||||||
|
{
|
||||||
|
*static_cast<bool*>(data) = node.as<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const IntType& type)
|
||||||
|
{
|
||||||
|
visitIntValue([&](auto& value)
|
||||||
|
{
|
||||||
|
using type_t = std::decay_t<decltype(value)>;
|
||||||
|
value = node.as<type_t>();
|
||||||
|
}, data, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const FloatType& type)
|
||||||
|
{
|
||||||
|
visitFloatValue([&](auto& value)
|
||||||
|
{
|
||||||
|
using type_t = std::decay_t<decltype(value)>;
|
||||||
|
value = node.as<type_t>();
|
||||||
|
}, data, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const CharType& type)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Not implemented.");
|
||||||
|
(void) type;
|
||||||
|
// visitCharValue([&](auto& value)
|
||||||
|
// {
|
||||||
|
// using type_t = std::decay_t<decltype(value)>;
|
||||||
|
// value = node.as<type_t>();
|
||||||
|
// }, data, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const ArrayType& type)
|
||||||
|
{
|
||||||
|
if (!node.IsSequence() || node.size() != type.arraySize)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Node not an array or size doesn't fit.");
|
||||||
|
}
|
||||||
|
for (std::size_t idx = 0; idx < type.arraySize; ++idx)
|
||||||
|
{
|
||||||
|
std::byte* ele = static_cast<std::byte*>(data) + (idx * type.arrayStride);
|
||||||
|
propertyFromYAML(ele, *type.baseType, node[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const ClassType&)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const StructType& type)
|
||||||
|
{
|
||||||
|
structFromYAML(data, *type.struct_, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const PointerType&)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const ReferenceType&)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const ConstType&)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Can't read const values.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const VolatileType& type)
|
||||||
|
{
|
||||||
|
propertyFromYAML(data, *type.baseType, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const STDType& type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case STDType::STRING:
|
||||||
|
*static_cast<std::string*>(data) = node.as<std::string>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const BitFlagsType&)
|
||||||
|
{
|
||||||
|
MIJIN_ERROR("Not implemented.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void propertyToYAML(const void* data, const iwa::Type& type, YAML::Emitter& emitter)
|
||||||
|
{
|
||||||
|
std::visit(PropertyEmitVisitor{data, emitter}, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void propertyFromYAML(void* data, const iwa::Type& type, const YAML::Node& node)
|
||||||
|
{
|
||||||
|
std::visit(PropertyParseVisitor{data, node}, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void structToYAML(const void* data, const Struct& type, YAML::Emitter& emitter)
|
||||||
|
{
|
||||||
|
emitter << YAML::BeginMap;
|
||||||
|
for (const Property& property : type.getProperties())
|
||||||
|
{
|
||||||
|
emitter << YAML::Key << property.name;
|
||||||
|
propertyToYAML(static_cast<const std::byte*>(data) + property.offset, property.type, emitter);
|
||||||
|
}
|
||||||
|
emitter << YAML::EndMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void structFromYAML(void* data, const Struct& type, const YAML::Node& node)
|
||||||
|
{
|
||||||
|
if (!node.IsMap())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Expected map.");
|
||||||
|
}
|
||||||
|
for (const Property& property : type.getProperties())
|
||||||
|
{
|
||||||
|
const YAML::Node& propNode = node[property.name];
|
||||||
|
if (propNode.IsNull()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
propertyFromYAML(static_cast<std::byte*>(data) + property.offset, property.type, propNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user