Created
December 8, 2023 22:11
-
-
Save oreyg/125851611d0fece3675aa3a43a9c125a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Stream-like archive, that treats data as a flat blob | |
template<typename TArchive> | |
concept TStreamArchiveConcept = requires(TArchive& archive, | |
char& c8, | |
b8& b8, | |
u16& u16, | |
u32& u32, | |
u64& u64, | |
i16& i16, | |
i32& i32, | |
i64& i64, | |
f32& f32, | |
f64& f64, | |
size_t& size, | |
std::string& str, | |
std::string_view sv | |
) | |
{ | |
// --- Arithmetic types --- | |
{ | |
archive.Map(c8) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(b8) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(u16) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(u32) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(u64) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(i16) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(i32) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(i64) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(f32) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(f64) | |
} -> std::same_as<void>; | |
// --- std::string types --- | |
{ | |
// For input we are forced to provide strings, as we can resize them | |
archive.Map(str) | |
} -> std::same_as<void>; | |
{ | |
// TO-DO: implement dynamic strings for input (maybe string-hash matching?) | |
archive.Map(sv) | |
} -> std::same_as<void>; | |
// --- Array types --- | |
{ | |
// PopArray receives the output of PushArray | |
archive.PopArray(archive.PushArray(size)) | |
}; | |
// --- Object types --- | |
{ | |
// PopArray receives the output of PushArray | |
archive.PopObject(archive.PushObject()) | |
}; | |
}; | |
// DOM-like archive, that does support named objects and arrays | |
template<typename TArchive> | |
concept TDOMArchiveConcept = requires(TArchive& archive, | |
char& c8, | |
b8& b8, | |
u16& u16, | |
u32& u32, | |
u64& u64, | |
i16& i16, | |
i32& i32, | |
i64& i64, | |
f32& f32, | |
f64& f64, | |
size_t& size, | |
std::string& str, | |
std::string_view sv | |
) | |
{ | |
// Access object by Names | |
// --- Arithmetic types --- | |
{ | |
archive.Map(std::string_view(), b8) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(std::string_view(), c8) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(std::string_view(), u16) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(std::string_view(), u32) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(std::string_view(), u64) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(std::string_view(), i16) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(std::string_view(), i32) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(std::string_view(), i64) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(std::string_view(), f32) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(std::string_view(), f64) | |
} -> std::same_as<void>; | |
// --- std::string types --- | |
{ | |
// For input we are forced to provide strings, as we can resize them | |
archive.Map(std::string_view(), str) | |
} -> std::same_as<void>; | |
{ | |
// TO-DO: implement dynamic strings for input (maybe string-hash matching?) | |
archive.Map(std::string_view(), sv) | |
} -> std::same_as<void>; | |
// --- Array types --- | |
{ | |
// PopArray receives the output of PushArray | |
archive.PopArray(archive.PushArray(std::string_view(), size)) | |
}; | |
// --- Object types --- | |
{ | |
// PopArray receives the output of PushArray | |
archive.PopObject(archive.PushObject(std::string_view())) | |
}; | |
// Access array element by indices | |
// --- Arithmetic types --- | |
{ | |
archive.Map(size_t(), b8) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(size_t(), c8) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(size_t(), u16) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(size_t(), u32) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(size_t(), u64) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(size_t(), i16) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(size_t(), i32) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(size_t(), i64) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(size_t(), f32) | |
} -> std::same_as<void>; | |
{ | |
archive.Map(size_t(), f64) | |
} -> std::same_as<void>; | |
// --- std::string types --- | |
{ | |
// For input we are forced to provide strings, as we can resize them | |
archive.Map(size_t(), str) | |
} -> std::same_as<void>; | |
{ | |
// TO-DO: implement dynamic strings for input (maybe string-hash matching?) | |
archive.Map(size_t(), sv) | |
} -> std::same_as<void>; | |
// --- Array types --- | |
{ | |
// PopArray receives the output of PushArray | |
archive.PopArray(archive.PushArray(size_t(), size)) | |
}; | |
// --- Object types --- | |
{ | |
// PopArray receives the output of PushArray | |
archive.PopObject(archive.PushObject(size_t())) | |
}; | |
}; | |
// Extractor-like archive, that extracts schema out of the given class | |
template<typename TArchive> | |
concept TInspectorConcept = requires(TArchive& inspector, MetaField&& metaField, void* rootPtr) | |
{ | |
{ | |
inspector.emplace_back(std::move(metaField)) | |
}; | |
}; | |
template<typename Archive, typename T> | |
inline void ReflectField(Archive& archive, std::string_view name, T& value) | |
{ | |
static constexpr bool IsDOMArchive = TDOMArchiveConcept<Archive>; | |
static constexpr bool IsStreamArchive = TStreamArchiveConcept<Archive>; | |
static constexpr bool IsInspector = TInspectorConcept<Archive>; | |
static_assert(IsDOMArchive || IsStreamArchive || IsInspector, "Provided archive does not correspond to DOM or Stream interface."); | |
if constexpr (IsInspector) | |
{ | |
InspectInternal(archive, value, name); | |
} | |
else if constexpr (IsDOMArchive) | |
{ | |
ReflectInternal(archive, value, name); | |
} | |
else | |
{ | |
ReflectInternal(archive, value); | |
} | |
} | |
template<typename Archive, typename T> | |
inline void ReflectIndex(Archive& archive, size_t index, T& value) | |
{ | |
static constexpr bool IsDOMArchive = TDOMArchiveConcept<Archive>; | |
static constexpr bool IsStreamArchive = TStreamArchiveConcept<Archive>; | |
static_assert(IsDOMArchive || IsStreamArchive, "Provided archive does not correspond to DOM or Stream interface."); | |
if constexpr (IsDOMArchive) | |
{ | |
ReflectInternal(archive, value, index); | |
} | |
else | |
{ | |
ReflectInternal(archive, value); | |
} | |
} | |
template<typename Archive, typename T> | |
inline void Serialize(Archive& archive, T& value) | |
{ | |
static_assert(IsClassType<T>, "Provided type is not a class."); | |
static constexpr bool IsDOMArchive = TDOMArchiveConcept<Archive>; | |
static constexpr bool IsStreamArchive = TStreamArchiveConcept<Archive>; | |
static constexpr bool IsInspector = TInspectorConcept<Archive>; | |
static_assert(IsDOMArchive || IsStreamArchive || IsInspector, "Provided archive does not correspond to DOM or Stream interface."); | |
using hqmeta::Reflect; | |
if constexpr (requires { Reflect(archive, value); }) | |
{ | |
Reflect(archive, value); | |
} | |
else | |
{ | |
static_assert(requires { value.Reflect(archive); }, "Provided type does not implement metadata using HQMETA macro."); | |
value.Reflect(archive); | |
} | |
} | |
template<typename Archive, typename T> | |
inline void Serialize(Archive& archive, std::unique_ptr<T>& value) | |
{ | |
Serialize(archive, *value.get()); | |
} | |
template<TInspectorConcept Archive, typename T> | |
inline void InspectInternal(Archive& archive, T& value, std::string_view name) | |
{ | |
if constexpr (IsArrayType<T>) | |
{ | |
using TValue = decltype(value[0]); | |
constexpr auto typeName = hq::GetTypeName<TValue>(); | |
if constexpr (TArrayConcept<T>) | |
{ | |
MetaField field(typeName, name, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&value)), sizeof(T), EMetaField::TArray); | |
archive.emplace_back(std::move(field)); | |
} | |
else | |
{ | |
MetaField field(typeName, name, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&value)), sizeof(T), EMetaField::CArray); | |
archive.emplace_back(std::move(field)); | |
} | |
} | |
else | |
{ | |
constexpr auto typeName = hq::GetTypeName<T>(); | |
MetaField field(typeName, name, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&value)), sizeof(T)); | |
archive.emplace_back(std::move(field)); | |
} | |
} | |
template<typename T> | |
inline void ExtractSchema(MetaRegistry& registry) | |
{ | |
using TFieldInspector = std::vector<MetaField>; | |
TFieldInspector fields; | |
// YES | |
Serialize(fields, *reinterpret_cast<T*>(nullptr)); | |
MetaClass metaClass{ | |
hq::GetTypeName<T>(), | |
sizeof(T), | |
alignof(T), | |
std::move(fields), | |
VTable::Make<T>() | |
}; | |
registry.AppendClass(std::move(metaClass)); | |
} | |
///// ------------------------- JSON | |
class OutputJSONArchiveImpl | |
{ | |
private: | |
using JSON = nlohmann::json; | |
std::unique_ptr<JSON> m_root; | |
JSON* m_json; | |
public: | |
template<typename T> | |
requires(!IsStringType<T>) | |
void Map(std::string_view name, T& value) | |
{ | |
(*m_json)[name] = value; | |
} | |
void Map(std::string_view name, std::string_view value) | |
{ | |
(*m_json)[name] = value; | |
} | |
void Map(std::string_view name, std::string& value) | |
{ | |
(*m_json)[name] = value; | |
} | |
template<typename T> | |
requires(!IsStringType<T>) | |
void Map(size_t index, T& value) | |
{ | |
(*m_json)[index] = value; | |
} | |
void Map(size_t index, std::string_view value) | |
{ | |
(*m_json)[index] = value; | |
} | |
void Map(size_t index, std::string& value) | |
{ | |
(*m_json)[index] = value; | |
} | |
OutputJSONArchiveImpl() | |
: m_root(std::make_unique<JSON>()) | |
, m_json(m_root.get()) | |
{ | |
} | |
JSON* PushObject(std::string_view name) | |
{ | |
JSON* oldValue = m_json; | |
JSON* json = &(*m_json)[name]; | |
(*json) = JSON::object(); | |
m_json = json; | |
return oldValue; | |
} | |
JSON* PushObject(size_t index) | |
{ | |
JSON* oldValue = m_json; | |
(*m_json)[index] = JSON::object(); | |
m_json = &m_json->back(); | |
return oldValue; | |
} | |
void PopObject(JSON* obj) | |
{ | |
m_json = obj; | |
} | |
JSON* PushArray(std::string_view name, size_t& outSize) | |
{ | |
UNREFERENCED(outSize); | |
JSON* oldValue = m_json; | |
JSON& json = (*m_json)[name]; | |
json = JSON::array(); | |
m_json = &json; | |
return oldValue; | |
} | |
JSON* PushArray(size_t index, size_t& outSize) | |
{ | |
UNREFERENCED(outSize); | |
JSON* oldValue = m_json; | |
(*m_json)[index] = JSON::array(); | |
m_json = &m_json->back(); | |
return oldValue; | |
} | |
void PopArray(JSON* obj) | |
{ | |
m_json = obj; | |
} | |
std::vector<u8> Dump() | |
{ | |
std::vector<u8> data; | |
auto str = m_json->dump(); | |
data.resize(str.size()); | |
memcpy(const_cast<u8*>(data.data()), str.data(), str.size()); | |
return data; | |
} | |
std::string ToString() const | |
{ | |
std::string data; | |
auto str = m_json->dump(); | |
data.resize(str.size()); | |
memcpy(const_cast<c8*>(data.data()), str.data(), str.size()); | |
return data; | |
} | |
}; | |
class InputJSONArchiveImpl | |
{ | |
private: | |
using JSON = nlohmann::json; | |
std::unique_ptr<JSON> m_root; | |
JSON* m_json; | |
public: | |
template<typename T> | |
requires(!IsStringType<T>) | |
void Map(std::string_view name, T& value) | |
{ | |
if (!m_json) | |
{ | |
return; | |
} | |
if (m_json->is_null()) | |
{ | |
return; | |
} | |
if (m_json->is_array()) | |
{ | |
return; | |
} | |
if (m_json->contains(name)) | |
{ | |
value = m_json->at(name).get<T>(); | |
} | |
} | |
void Map(std::string_view name, std::string& value) | |
{ | |
if (m_json == nullptr || m_json->is_null()) | |
{ | |
value.resize(0); | |
return; | |
} | |
std::string_view out{}; | |
if (m_json->contains(name)) | |
{ | |
out = m_json->at(name).get<std::string_view>(); | |
} | |
value = out; | |
} | |
void Map(std::string_view name, std::string_view value) | |
{ | |
} | |
template<typename T> | |
requires(!IsStringType<T>) | |
void Map(size_t index, T& value) | |
{ | |
if (!m_json) | |
{ | |
return; | |
} | |
if (m_json->is_null()) | |
{ | |
return; | |
} | |
if (!m_json->is_array()) | |
{ | |
return; | |
} | |
if (index < m_json->size()) | |
{ | |
value = (*m_json)[index].get<T>(); | |
} | |
} | |
void Map(size_t index, std::string& value) | |
{ | |
if (m_json == nullptr || m_json->is_null()) | |
{ | |
value.resize(0); | |
return; | |
} | |
std::string_view out{}; | |
if (index < m_json->size()) | |
{ | |
out = (*m_json)[index].get<std::string_view>(); | |
} | |
value = out; | |
} | |
void Map(size_t index, std::string_view value) | |
{ | |
if (m_json == nullptr || m_json->is_null()) | |
{ | |
return; | |
} | |
std::string_view out{}; | |
if (index < m_json->size()) | |
{ | |
out = (*m_json)[index].get<std::string_view>(); | |
} | |
value = out; | |
} | |
InputJSONArchiveImpl(std::string_view data) | |
: m_root(std::make_unique<JSON>(JSON::parse(data))) | |
, m_json(m_root.get()) | |
{} | |
JSON* PushObject(size_t index) | |
{ | |
JSON* oldValue = m_json; | |
if (m_json == nullptr || m_json->is_null()) | |
{ | |
return oldValue; | |
} | |
if (index < m_json->size()) | |
{ | |
m_json = &(*m_json)[index]; | |
} | |
else | |
{ | |
m_json = nullptr; | |
} | |
return oldValue; | |
} | |
JSON* PushArray(size_t index, size_t& outSize) | |
{ | |
JSON* oldValue = m_json; | |
if (m_json == nullptr || m_json->is_null()) | |
{ | |
return oldValue; | |
} | |
if (index < m_json->size()) | |
{ | |
m_json = &(*m_json)[index]; | |
} | |
else | |
{ | |
m_json = nullptr; | |
} | |
if (m_json && m_json->is_array()) | |
{ | |
outSize = m_json->size(); | |
} | |
return oldValue; | |
} | |
JSON* PushObject(std::string_view name) | |
{ | |
JSON* oldValue = m_json; | |
if (m_json == nullptr || m_json->is_null()) | |
{ | |
return oldValue; | |
} | |
if (m_json->contains(name)) | |
{ | |
m_json = &(*m_json)[name]; | |
} | |
else | |
{ | |
m_json = nullptr; | |
} | |
return oldValue; | |
} | |
void PopObject(JSON* obj) | |
{ | |
m_json = obj; | |
} | |
JSON* PushArray(std::string_view name, size_t& outSize) | |
{ | |
JSON* oldValue = m_json; | |
if (m_json == nullptr || m_json->is_null()) | |
{ | |
return oldValue; | |
} | |
if (m_json->contains(name)) | |
{ | |
m_json = &(*m_json)[name]; | |
} | |
else | |
{ | |
m_json = nullptr; | |
} | |
if (m_json && m_json->is_array()) | |
{ | |
outSize = m_json->size(); | |
} | |
return oldValue; | |
} | |
void PopArray(JSON* obj) | |
{ | |
m_json = obj; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment