(WIP) Some reflection stuff.

This commit is contained in:
Patrick 2024-07-06 10:24:33 +02:00
parent 473a3aafab
commit cf0d43d950
5 changed files with 928 additions and 18 deletions

View File

@ -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

View 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)

View File

@ -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)

View File

@ -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
View 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);
}
}
}