Merge commit '69fcd7ad1056940166a5d9524b4f03578f680834' as 'deps/oxlib'
This commit is contained in:
Vendored
+45
@@ -0,0 +1,45 @@
|
||||
add_library(
|
||||
OxModel
|
||||
src/desctypes.cpp
|
||||
src/descwrite.cpp
|
||||
src/modelvalue.cpp
|
||||
)
|
||||
|
||||
if(NOT MSVC)
|
||||
target_compile_options(OxModel PRIVATE -Wconversion)
|
||||
target_compile_options(OxModel PRIVATE -Wsign-conversion)
|
||||
endif()
|
||||
|
||||
target_link_libraries(
|
||||
OxModel PUBLIC
|
||||
OxStd
|
||||
)
|
||||
|
||||
if(NOT OX_BARE_METAL)
|
||||
set_property(
|
||||
TARGET
|
||||
OxModel
|
||||
PROPERTY
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(
|
||||
OxModel PUBLIC
|
||||
OxStd
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
OxModel PUBLIC
|
||||
include
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS OxModel
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
||||
if(OX_RUN_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
@@ -0,0 +1,7 @@
|
||||
<Type> : <TypeName><FieldList>
|
||||
<FieldList> : <FieldList> | <FieldList><Field>
|
||||
<Field> : <FieldType><TypeID><FieldName>
|
||||
<TypeID> : <TypeName> | <TypeName><Type>
|
||||
<TypeName> : <string>
|
||||
<FieldType> : <0: single> | <1: list>
|
||||
<FieldName> : <string>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/concepts.hpp>
|
||||
|
||||
// oxModelFwdDecl is necessary because Apple-Clang is broken...
|
||||
#define OX_MODEL_FWD_DECL(modelName) constexpr ox::Error model(auto *io, ox::CommonPtrWith<modelName> auto *o) noexcept
|
||||
#define OX_MODEL_BEGIN(modelName) constexpr ox::Error model(auto *io, [[maybe_unused]] ox::CommonPtrWith<modelName> auto *o) noexcept { OX_RETURN_ERROR(io->template setTypeInfo<modelName>());
|
||||
#define OX_MODEL_END() return {}; }
|
||||
#define OX_MODEL_FIELD(fieldName) OX_RETURN_ERROR(io->field(#fieldName, &o->fieldName));
|
||||
#define OX_MODEL_FIELD_RENAME(objFieldName, serFieldName) OX_RETURN_ERROR(io->field(#serFieldName, &o->objFieldName));
|
||||
#define OX_MODEL_FRIEND(modelName) friend constexpr ox::Error model(auto *io, ox::CommonPtrWith<modelName> auto *o) noexcept
|
||||
@@ -0,0 +1,7 @@
|
||||
<Type> : <TypeName><FieldList>
|
||||
<FieldList> : <FieldList> | <FieldList><Field>
|
||||
<Field> : <FieldType><TypeID><FieldName>
|
||||
<TypeID> : <TypeName> | <TypeName><Type>
|
||||
<TypeName> : <string>
|
||||
<FieldType> : <0: single> | <1: list>
|
||||
<FieldName> : <string>
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "typestore.hpp"
|
||||
#include "desctypes.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename ReaderBase>
|
||||
class TypeDescReader: public ReaderBase {
|
||||
private:
|
||||
TypeStore m_typeStore;
|
||||
|
||||
public:
|
||||
constexpr TypeDescReader(const uint8_t *buff, std::size_t buffLen) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const auto &typeStore() const noexcept;
|
||||
|
||||
};
|
||||
|
||||
template<typename ReaderBase>
|
||||
constexpr TypeDescReader<ReaderBase>::TypeDescReader(const uint8_t *buff, std::size_t buffLen) noexcept:
|
||||
ReaderBase(buff, buffLen) {
|
||||
}
|
||||
|
||||
template<typename ReaderBase>
|
||||
constexpr const auto &TypeDescReader<ReaderBase>::typeStore() const noexcept {
|
||||
return m_typeStore;
|
||||
}
|
||||
|
||||
template<typename ReaderBase, typename T>
|
||||
constexpr int readMCDef(const uint8_t *buff, std::size_t buffLen, T *val) noexcept {
|
||||
TypeDescReader<ReaderBase> reader(buff, buffLen);
|
||||
return model(&reader, val);
|
||||
}
|
||||
|
||||
}
|
||||
+251
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/bit.hpp>
|
||||
#include <ox/std/error.hpp>
|
||||
#include <ox/std/fmt.hpp>
|
||||
#include <ox/std/hashmap.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/typetraits.hpp>
|
||||
#include <ox/std/vector.hpp>
|
||||
|
||||
#include "optype.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typenamecatcher.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
using FieldName = String;
|
||||
|
||||
using TypeParamPack = Vector<String>;
|
||||
|
||||
template<typename T>
|
||||
constexpr auto buildTypeId() noexcept {
|
||||
constexpr auto name = requireModelTypeName<T>();
|
||||
constexpr auto version = requireModelTypeVersion<T>();
|
||||
return ox::sfmt("{};{}", name, version);
|
||||
}
|
||||
|
||||
static constexpr auto buildTypeId(
|
||||
StringViewCR name, int version,
|
||||
const TypeParamPack &typeParams = {}) noexcept {
|
||||
String tp;
|
||||
if (!typeParams.empty()) {
|
||||
tp = "#";
|
||||
for (const auto &p : typeParams) {
|
||||
tp += p + ",";
|
||||
}
|
||||
tp.resize(tp.size() - 1);
|
||||
tp += "#";
|
||||
}
|
||||
return ox::sfmt("{}{};{}", name, tp, version);
|
||||
}
|
||||
|
||||
enum class PrimitiveType: uint8_t {
|
||||
UnsignedInteger = 0,
|
||||
SignedInteger = 1,
|
||||
Bool = 2,
|
||||
// Float = 3, reserved, but not implemented
|
||||
String = 4,
|
||||
Struct = 5,
|
||||
Union = 6,
|
||||
};
|
||||
|
||||
struct Subscript {
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.Subscript";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
enum class SubscriptType: uint32_t {
|
||||
None = 0,
|
||||
Ptr = 1,
|
||||
PtrArray = 2,
|
||||
InlineArray = 3,
|
||||
Vector = 4,
|
||||
};
|
||||
SubscriptType subscriptType = SubscriptType::None;
|
||||
uint64_t length = 0;
|
||||
uint64_t smallSzLen = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, CommonPtrWith<Subscript> auto *type) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<Subscript>());
|
||||
if constexpr(T::opType() == OpType::Reflect) {
|
||||
uint32_t st = 0;
|
||||
OX_RETURN_ERROR(io->field("subscriptType", &st));
|
||||
} else if constexpr(T::opType() == OpType::Write) {
|
||||
auto pt = type ? static_cast<uint8_t>(type->subscriptType) : 0;
|
||||
OX_RETURN_ERROR(io->field("subscriptType", &pt));
|
||||
} else {
|
||||
auto pt = type ? static_cast<uint32_t>(type->subscriptType) : 0;
|
||||
OX_RETURN_ERROR(io->field("subscriptType", &pt));
|
||||
type->subscriptType = static_cast<Subscript::SubscriptType>(pt);
|
||||
}
|
||||
OX_RETURN_ERROR(io->field("length", &type->length));
|
||||
OX_RETURN_ERROR(io->field("smallSzLen", &type->smallSzLen));
|
||||
return {};
|
||||
}
|
||||
|
||||
using SubscriptStack = Vector<Subscript, 3>;
|
||||
|
||||
struct DescriptorField {
|
||||
// order of fields matters
|
||||
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.DescriptorField";
|
||||
static constexpr auto TypeVersion = 3;
|
||||
|
||||
// do not serialize type
|
||||
const struct DescriptorType *type = nullptr;
|
||||
String fieldName;
|
||||
int subscriptLevels = 0;
|
||||
SubscriptStack subscriptStack;
|
||||
String typeId; // gives reference to type for lookup if type is null
|
||||
|
||||
constexpr DescriptorField() noexcept = default;
|
||||
|
||||
constexpr DescriptorField(const DescriptorType *pType, String pFieldName,
|
||||
int pSubscriptLevels,
|
||||
SubscriptStack pSubscriptType,
|
||||
String pTypeId) noexcept:
|
||||
type(pType),
|
||||
fieldName(std::move(pFieldName)),
|
||||
subscriptLevels(pSubscriptLevels),
|
||||
subscriptStack(std::move(pSubscriptType)),
|
||||
typeId(std::move(pTypeId)) {
|
||||
oxAssert(subscriptLevels <= static_cast<int>(subscriptStack.size()), "Subscript level mismatch");
|
||||
}
|
||||
|
||||
constexpr DescriptorField(const DescriptorField &other) noexcept:
|
||||
type(other.type),
|
||||
fieldName(other.fieldName),
|
||||
subscriptLevels(other.subscriptLevels),
|
||||
subscriptStack(other.subscriptStack),
|
||||
typeId(other.typeId) {
|
||||
}
|
||||
|
||||
constexpr DescriptorField(DescriptorField &&other) noexcept:
|
||||
type(other.type),
|
||||
fieldName(std::move(other.fieldName)),
|
||||
subscriptLevels(other.subscriptLevels),
|
||||
subscriptStack(std::move(other.subscriptStack)),
|
||||
typeId(std::move(other.typeId)) {
|
||||
other.type = {};
|
||||
other.subscriptLevels = {};
|
||||
other.subscriptStack = {};
|
||||
}
|
||||
|
||||
constexpr ~DescriptorField() noexcept = default;
|
||||
|
||||
constexpr DescriptorField &operator=(const DescriptorField &other) noexcept = delete;
|
||||
|
||||
constexpr DescriptorField &operator=(DescriptorField &&other) noexcept = delete;
|
||||
|
||||
};
|
||||
|
||||
using FieldList = Vector<DescriptorField>;
|
||||
|
||||
struct DescriptorType {
|
||||
|
||||
static constexpr auto TypeName = "net.drinkingtea.ox.TypeDescriptor";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
|
||||
String typeName;
|
||||
int typeVersion = 0;
|
||||
PrimitiveType primitiveType = PrimitiveType::UnsignedInteger;
|
||||
TypeParamPack typeParams;
|
||||
// fieldList only applies to structs and unions
|
||||
FieldList fieldList;
|
||||
// - number of bytes for integer and float types
|
||||
// - number of fields for structs and lists
|
||||
int64_t length = 0;
|
||||
bool preloadable = false;
|
||||
|
||||
constexpr DescriptorType() noexcept = default;
|
||||
|
||||
constexpr explicit DescriptorType(String tn, int typeVersion, PrimitiveType t, TypeParamPack pTypeParams) noexcept:
|
||||
typeName(std::move(tn)),
|
||||
typeVersion(typeVersion),
|
||||
primitiveType(t),
|
||||
typeParams(std::move(pTypeParams)) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto buildTypeId(const DescriptorType &t) noexcept {
|
||||
return buildTypeId(t.typeName, t.typeVersion, t.typeParams);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, CommonPtrWith<DescriptorType> auto *type) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<DescriptorType>());
|
||||
OX_RETURN_ERROR(io->field("typeName", &type->typeName));
|
||||
OX_RETURN_ERROR(io->field("typeVersion", &type->typeVersion));
|
||||
if constexpr(T::opType() == OpType::Reflect) {
|
||||
uint8_t pt = 0;
|
||||
OX_RETURN_ERROR(io->field("primitiveType", &pt));
|
||||
} else if constexpr(T::opType() == OpType::Write) {
|
||||
auto pt = type ? static_cast<uint8_t>(type->primitiveType) : 0;
|
||||
OX_RETURN_ERROR(io->field("primitiveType", &pt));
|
||||
} else {
|
||||
auto pt = type ? static_cast<uint8_t>(type->primitiveType) : 0;
|
||||
OX_RETURN_ERROR(io->field("primitiveType", &pt));
|
||||
type->primitiveType = static_cast<PrimitiveType>(pt);
|
||||
}
|
||||
OX_RETURN_ERROR(io->field("typeParams", &type->typeParams));
|
||||
OX_RETURN_ERROR(io->field("fieldList", &type->fieldList));
|
||||
OX_RETURN_ERROR(io->field("length", &type->length));
|
||||
OX_RETURN_ERROR(io->field("preloadable", &type->preloadable));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error model(T *io, CommonPtrWith<DescriptorField> auto *field) noexcept {
|
||||
OX_RETURN_ERROR(io->template setTypeInfo<DescriptorField>());
|
||||
OX_RETURN_ERROR(io->field("typeId", &field->typeId));
|
||||
OX_RETURN_ERROR(io->field("fieldName", &field->fieldName));
|
||||
OX_RETURN_ERROR(io->field("subscriptLevels", &field->subscriptLevels));
|
||||
OX_RETURN_ERROR(io->field("subscriptStack", &field->subscriptStack));
|
||||
// defaultValue is unused now, but leave placeholder for backwards compatibility
|
||||
int defaultValue = 0;
|
||||
OX_RETURN_ERROR(io->field("defaultValue", &defaultValue));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename ReaderBase>
|
||||
class TypeDescReader;
|
||||
|
||||
#if 0 // unused right now
|
||||
template<typename T>
|
||||
constexpr Error model(TypeDescReader<T> *io, CommonPtrWith<DescriptorField> auto *field) noexcept {
|
||||
io->template setTypeInfo<DescriptorField>();
|
||||
oxReturnError(io->field("typeName", &field->typeName));
|
||||
oxReturnError(io->field("typeVersion", &field->typeVersion));
|
||||
auto &typeStore = io->typeStore();
|
||||
auto &[type, err] = typeStore->at(field->typeName).value;
|
||||
oxReturnError(err);
|
||||
field->type = type.get();
|
||||
oxReturnError(io->field("fieldName", &field->fieldName));
|
||||
oxReturnError(io->field("subscriptLevels", &field->subscriptLevels));
|
||||
if constexpr(T::opType() != ox::OpType::Reflect) {
|
||||
auto subscriptStack = static_cast<uint32_t>(field->subscriptStack);
|
||||
oxReturnError(io->field("subscriptStack", &subscriptStack));
|
||||
field->subscriptStack = static_cast<DescriptorField::SubscriptType>(subscriptStack);
|
||||
} else {
|
||||
oxReturnError(io->field("subscriptStack", &field->subscriptStack));
|
||||
}
|
||||
// defaultValue is unused now, but placeholder for backwards compatibility
|
||||
int defaultValue = 0;
|
||||
oxReturnError(io->field("defaultValue", &defaultValue));
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
+404
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/byteswap.hpp>
|
||||
#include <ox/std/istring.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/std/vector.hpp>
|
||||
|
||||
#include "desctypes.hpp"
|
||||
#include "fieldcounter.hpp"
|
||||
#include "metadata.hpp"
|
||||
#include "modelhandleradaptor.hpp"
|
||||
#include "optype.hpp"
|
||||
#include "typenamecatcher.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typestore.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
constexpr int indirectionLevels_v = 0;
|
||||
|
||||
template<typename T>
|
||||
constexpr int indirectionLevels_v<T*> = 1 + indirectionLevels_v<T>;
|
||||
|
||||
template<typename T, std::size_t sz>
|
||||
constexpr int indirectionLevels_v<T[sz]> = 1 + indirectionLevels_v<T>;
|
||||
|
||||
template<typename T, std::size_t SmallVecSz>
|
||||
constexpr int indirectionLevels_v<::ox::Vector<T, SmallVecSz>> = 1 + indirectionLevels_v<T>;
|
||||
|
||||
template<typename T>
|
||||
constexpr auto buildSubscriptStack(const T*, SubscriptStack*) noexcept {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr auto buildSubscriptStack(const T**, SubscriptStack *s) noexcept {
|
||||
s->push_back({.subscriptType = Subscript::SubscriptType::Ptr});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr auto buildSubscriptStack(const UPtr<T>*, SubscriptStack *s) noexcept {
|
||||
s->push_back({.subscriptType = Subscript::SubscriptType::Ptr});
|
||||
}
|
||||
|
||||
template<typename T, std::size_t sz>
|
||||
constexpr auto buildSubscriptStack(const Array<T, sz>*, SubscriptStack *s) noexcept {
|
||||
s->push_back({.subscriptType = Subscript::SubscriptType::InlineArray, .length = sz});
|
||||
buildSubscriptStack(static_cast<T*>(nullptr), s);
|
||||
}
|
||||
|
||||
template<typename T, std::size_t SmallVecSz>
|
||||
constexpr auto buildSubscriptStack(const Vector<T, SmallVecSz>*, SubscriptStack *s) noexcept {
|
||||
s->push_back({
|
||||
.subscriptType = Subscript::SubscriptType::Vector,
|
||||
.smallSzLen = SmallVecSz,
|
||||
});
|
||||
buildSubscriptStack(static_cast<T*>(nullptr), s);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr auto buildSubscriptStack(const T*) {
|
||||
SubscriptStack s;
|
||||
buildSubscriptStack(static_cast<const T*>(nullptr), &s);
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TypeDescWriter {
|
||||
|
||||
private:
|
||||
TypeStore *m_typeStore = nullptr;
|
||||
DescriptorType *m_type = nullptr;
|
||||
|
||||
public:
|
||||
explicit constexpr TypeDescWriter(TypeStore *typeStore = nullptr) noexcept;
|
||||
|
||||
constexpr ~TypeDescWriter() noexcept = default;
|
||||
|
||||
template<typename T = std::nullptr_t>
|
||||
constexpr ox::Error setTypeInfo(StringViewCR name = T::TypeName,
|
||||
int version = T::TypeVersion,
|
||||
const TypeParamPack &typeParams = {},
|
||||
std::size_t fields = ModelFieldCount_v<T>) noexcept;
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(
|
||||
StringViewCR name,
|
||||
T const*val,
|
||||
std::size_t valLen,
|
||||
SubscriptStack const&subscriptStack) noexcept;
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(StringViewCR name, T const*val, std::size_t valLen) noexcept;
|
||||
|
||||
template<typename T, bool force>
|
||||
constexpr Error field(StringViewCR name, UnionView<T, force> val) noexcept;
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(StringViewCR name, const T *val) noexcept;
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr Error fieldCString(StringViewCR name, Args&&...) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr DescriptorType *definition() noexcept {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
static constexpr auto opType() noexcept {
|
||||
return OpType::Reflect;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const int8_t *val) const noexcept;
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const int16_t *val) const noexcept;
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const int32_t *val) const noexcept;
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const int64_t *val) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const uint8_t *val) const noexcept;
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const uint16_t *val) const noexcept;
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const uint32_t *val) const noexcept;
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const uint64_t *val) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const bool *val) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const char *val) const noexcept;
|
||||
|
||||
template<std::size_t SmallStrSz>
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const BasicString<SmallStrSz>*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::String;
|
||||
return getType(types::BasicString, 1, PT, 0, {sfmt("{}", SmallStrSz)});
|
||||
}
|
||||
|
||||
template<std::size_t sz>
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const IString<sz> *val) const noexcept;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const T *val) const noexcept;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(const HashMap<String, T> *val) const noexcept;
|
||||
|
||||
template<typename U>
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *type(UnionView<U> val) const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const DescriptorType *getType(StringViewCR tn, int typeVersion, PrimitiveType t, int b,
|
||||
const TypeParamPack &typeParams = {}) const noexcept;
|
||||
|
||||
};
|
||||
|
||||
constexpr TypeDescWriter::TypeDescWriter(TypeStore *typeStore) noexcept: m_typeStore(typeStore) {}
|
||||
|
||||
template<typename T>
|
||||
constexpr ox::Error TypeDescWriter::setTypeInfo(
|
||||
StringViewCR typeName, int typeVersion,
|
||||
const TypeParamPack &typeParams, std::size_t) noexcept {
|
||||
PrimitiveType pt;
|
||||
if constexpr(is_union_v<T>) {
|
||||
pt = PrimitiveType::Union;
|
||||
} else if constexpr(isBasicString_v<T> || isIString_v<T>) {
|
||||
pt = PrimitiveType::String;
|
||||
} else {
|
||||
pt = PrimitiveType::Struct;
|
||||
}
|
||||
m_type = m_typeStore->getInit(typeName, typeVersion, pt, typeParams);
|
||||
m_type->preloadable = preloadable<T>::value;
|
||||
return {};
|
||||
}
|
||||
|
||||
// array handler
|
||||
template<typename T>
|
||||
constexpr Error TypeDescWriter::field(StringViewCR name, T const*, std::size_t, SubscriptStack const&subscriptStack) noexcept {
|
||||
if (m_type) {
|
||||
constexpr typename remove_pointer<T>::type *p = nullptr;
|
||||
const auto t = type(p);
|
||||
oxAssert(t != nullptr, "field(const char *name, T *val, std::size_t): Type not found or generated");
|
||||
m_type->fieldList.emplace_back(t, String(name), detail::indirectionLevels_v<T> + 1, subscriptStack, buildTypeId(*t));
|
||||
return {};
|
||||
}
|
||||
return ox::Error(1);
|
||||
}
|
||||
|
||||
// array handler
|
||||
template<typename T>
|
||||
constexpr Error TypeDescWriter::field(StringViewCR name, T const*, std::size_t) noexcept {
|
||||
if (m_type) {
|
||||
constexpr typename remove_pointer<T>::type *p = nullptr;
|
||||
const auto t = type(p);
|
||||
oxAssert(t != nullptr, "field(const char *name, T *val, std::size_t): Type not found or generated");
|
||||
auto const lvls = detail::indirectionLevels_v<T> + 1;
|
||||
SubscriptStack subscriptStack{lvls};
|
||||
m_type->fieldList.emplace_back(t, String(name), lvls, subscriptStack, buildTypeId(*t));
|
||||
return {};
|
||||
}
|
||||
return ox::Error(1);
|
||||
}
|
||||
|
||||
template<typename T, bool force>
|
||||
constexpr Error TypeDescWriter::field(StringViewCR name, UnionView<T, force> val) noexcept {
|
||||
if (m_type) {
|
||||
const auto t = type(val);
|
||||
oxAssert(t != nullptr, "field(const char *name, T val): Type not found or generated");
|
||||
m_type->fieldList.emplace_back(t, String(name), 0, SubscriptStack{}, ox::String(t->typeName));
|
||||
return {};
|
||||
}
|
||||
return ox::Error(1);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error TypeDescWriter::field(StringViewCR name, const T *val) noexcept {
|
||||
if (m_type) {
|
||||
if constexpr(isVector_v<T> || isArray_v<T>) {
|
||||
typename T::value_type *data = nullptr;
|
||||
return field(name, data, 0, detail::buildSubscriptStack(val));
|
||||
} else if constexpr(isSmartPtr_v<T>) {
|
||||
typename T::value_type *data = nullptr;
|
||||
return field(name, data, 0, detail::buildSubscriptStack(val));
|
||||
} else if constexpr(is_pointer_v<T>) {
|
||||
return field(name, val, 0, detail::buildSubscriptStack(val));
|
||||
} else {
|
||||
const auto t = type(val);
|
||||
oxAssert(t != nullptr, "field(const char *name, T *val): Type not found or generated");
|
||||
m_type->fieldList.emplace_back(t, String(name), 0, SubscriptStack{}, buildTypeId(*t));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return ox::Error(1);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr Error TypeDescWriter::fieldCString(StringViewCR name, Args&&...) noexcept {
|
||||
constexpr auto s = "";
|
||||
const auto t = type(s);
|
||||
m_type->fieldList.emplace_back(t, String(name), 0, SubscriptStack{}, ox::String(t->typeName));
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const T *val) const noexcept {
|
||||
if constexpr(isVector_v<T>) {
|
||||
return type(static_cast<typename T::value_type*>(nullptr));
|
||||
} else {
|
||||
auto [t, err] = m_typeStore->template get<T>();
|
||||
if (!err) {
|
||||
return t;
|
||||
} else {
|
||||
TypeDescWriter dw(m_typeStore);
|
||||
const auto reflectErr = model(&dw, val);
|
||||
oxLogError(reflectErr);
|
||||
oxAssert(reflectErr, "field(const char *name, T val): Type info could not be generated");
|
||||
return dw.m_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const HashMap<String, T>*) const noexcept {
|
||||
return type(static_cast<T*>(nullptr));
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr const DescriptorType *TypeDescWriter::type(UnionView<U> val) const noexcept {
|
||||
const auto t = type(val.get());
|
||||
oxAssert(t != nullptr, "field(const char *name, T val): Type not found or generated");
|
||||
return t;
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const int8_t*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::SignedInteger;
|
||||
constexpr auto Bytes = 1;
|
||||
return getType(types::Int8, 0, PT, Bytes);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const int16_t*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::SignedInteger;
|
||||
constexpr auto Bytes = 2;
|
||||
return getType(types::Int16, 0, PT, Bytes);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const int32_t*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::SignedInteger;
|
||||
constexpr auto Bytes = 4;
|
||||
return getType(types::Int32, 0, PT, Bytes);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const int64_t*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::SignedInteger;
|
||||
constexpr auto Bytes = 8;
|
||||
return getType(types::Int64, 0, PT, Bytes);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const uint8_t*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::UnsignedInteger;
|
||||
constexpr auto Bytes = 1;
|
||||
return getType(types::Uint8, 0, PT, Bytes);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const uint16_t*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::UnsignedInteger;
|
||||
constexpr auto Bytes = 2;
|
||||
return getType(types::Uint16, 0, PT, Bytes);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const uint32_t*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::UnsignedInteger;
|
||||
constexpr auto Bytes = 4;
|
||||
return getType(types::Uint32, 0, PT, Bytes);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const uint64_t*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::UnsignedInteger;
|
||||
constexpr auto Bytes = 8;
|
||||
return getType(types::Uint64, 0, PT, Bytes);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const bool*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::Bool;
|
||||
constexpr auto Bytes = 0;
|
||||
return getType(types::Bool, 0, PT, Bytes);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const char*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::String;
|
||||
return getType(types::String, 0, PT, 0);
|
||||
}
|
||||
|
||||
template<std::size_t sz>
|
||||
constexpr const DescriptorType *TypeDescWriter::type(const IString<sz>*) const noexcept {
|
||||
constexpr auto PT = PrimitiveType::String;
|
||||
return getType(types::IString, 0, PT, 0);
|
||||
}
|
||||
|
||||
constexpr const DescriptorType *TypeDescWriter::getType(StringViewCR tn, int typeVersion, PrimitiveType pt, int b,
|
||||
const TypeParamPack &typeParams) const noexcept {
|
||||
auto t = m_typeStore->get(tn, typeVersion, typeParams);
|
||||
if (!t.error) {
|
||||
auto type = t.value;
|
||||
oxAssert(type != nullptr, "TypeDescWriter::getType returning null DescriptorType");
|
||||
return type;
|
||||
} else {
|
||||
auto dt = ox::make_unique<DescriptorType>(String(tn), typeVersion, pt, typeParams);
|
||||
dt->length = b;
|
||||
const auto out = dt.get();
|
||||
const auto typeId = buildTypeId(tn, typeVersion, typeParams);
|
||||
m_typeStore->set(typeId, std::move(dt));
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Result<DescriptorType*> buildTypeDef(TypeStore &typeStore) noexcept {
|
||||
TypeDescWriter writer(&typeStore);
|
||||
ModelHandlerInterface<TypeDescWriter, ox::OpType::Reflect> handler(writer);
|
||||
if (std::is_constant_evaluated()) {
|
||||
std::allocator<T> a;
|
||||
T *t = a.allocate(1);
|
||||
OX_RETURN_ERROR(model(&handler, t));
|
||||
a.deallocate(t, 1);
|
||||
} else {
|
||||
auto t = ox_malloca(sizeof(T), T);
|
||||
OX_RETURN_ERROR(model(&handler, t.get()));
|
||||
}
|
||||
return writer.definition();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Result<DescriptorType*> buildTypeDef(TypeStore &typeStore, T &val) noexcept {
|
||||
TypeDescWriter writer(&typeStore);
|
||||
ModelHandlerInterface<TypeDescWriter, ox::OpType::Reflect> handler(writer);
|
||||
OX_RETURN_ERROR(model(&handler, &val));
|
||||
return writer.definition();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/assert.hpp>
|
||||
#include <ox/std/bit.hpp>
|
||||
#include <ox/std/error.hpp>
|
||||
|
||||
#include "optype.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename T>
|
||||
class FieldCounter {
|
||||
public:
|
||||
std::size_t fields = 0;
|
||||
|
||||
template<typename U = std::nullptr_t>
|
||||
constexpr ox::Error setTypeInfo(StringViewCR = "", int = 0, const Vector<String>& = {}, std::size_t = 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr ox::Error field(StringViewCR, U) noexcept {
|
||||
++fields;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
constexpr ox::Error field(StringViewCR, U, std::size_t) noexcept {
|
||||
++fields;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, typename Handler>
|
||||
constexpr Error field(StringViewCR, Handler) {
|
||||
++fields;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
constexpr Error fieldCString(Args&&...) noexcept {
|
||||
++fields;
|
||||
return {};
|
||||
}
|
||||
|
||||
static constexpr auto opType() noexcept {
|
||||
return OpType::Reflect;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
consteval auto modelFieldCount() noexcept {
|
||||
auto a = std::allocator<T>();
|
||||
auto t = a.allocate(1);
|
||||
detail::FieldCounter<T> c;
|
||||
const auto err = model(&c, t);
|
||||
oxAssert(err, "Count failed");
|
||||
a.deallocate(t, 1);
|
||||
return c.fields;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr auto ModelFieldCount_v = detail::modelFieldCount<T>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/byteswap.hpp>
|
||||
#include <ox/std/istring.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/std/vector.hpp>
|
||||
|
||||
#include "desctypes.hpp"
|
||||
#include "fieldcounter.hpp"
|
||||
#include "optype.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<bool>
|
||||
struct BoolWrapper {
|
||||
};
|
||||
|
||||
template<int>
|
||||
struct IntWrapper {
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename T, typename = detail::BoolWrapper<true>>
|
||||
struct preloadable: false_type {
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct preloadable<T, detail::BoolWrapper<T::Preloadable>> {
|
||||
static constexpr bool value = T::Preloadable;
|
||||
};
|
||||
|
||||
// cannot be done until C++20
|
||||
//struct PseudoString {
|
||||
// constexpr PseudoString(const char* = "") noexcept {}
|
||||
//};
|
||||
//
|
||||
//template<PseudoString>
|
||||
//struct StringWrapper {
|
||||
//};
|
||||
//
|
||||
//template<typename T, typename = StringWrapper<"">>
|
||||
//struct ModelTypeName {
|
||||
// static constexpr const char *value = "";
|
||||
//};
|
||||
//
|
||||
//template<typename T>
|
||||
//struct ModelTypeName<T, detail::StringWrapper<T::TypeName>> {
|
||||
// static constexpr const char *value = T::TypeName;
|
||||
//};
|
||||
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "def.hpp"
|
||||
#include "descread.hpp"
|
||||
#include "desctypes.hpp"
|
||||
#include "descwrite.hpp"
|
||||
#include "fieldcounter.hpp"
|
||||
#include "modelhandleradaptor.hpp"
|
||||
#include "modelops.hpp"
|
||||
#include "modelvalue.hpp"
|
||||
#include "typenamecatcher.hpp"
|
||||
#include "types.hpp"
|
||||
#include "typestore.hpp"
|
||||
#include "walk.hpp"
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/utility.hpp>
|
||||
|
||||
#include "modelvalue.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename Handler, OpType opType_v = Handler::opType()>
|
||||
class ModelHandlerInterface {
|
||||
private:
|
||||
Handler &m_handler;
|
||||
|
||||
public:
|
||||
constexpr explicit ModelHandlerInterface(Handler &handler) noexcept: m_handler(handler) {
|
||||
}
|
||||
|
||||
template<typename T = std::nullptr_t>
|
||||
constexpr ox::Error setTypeInfo(
|
||||
CString name = T::TypeName,
|
||||
int version = T::TypeVersion,
|
||||
Vector<String> const &typeParams = {}) noexcept {
|
||||
return m_handler.template setTypeInfo<T>(name, version, typeParams, ModelFieldCount_v<T>);
|
||||
}
|
||||
|
||||
template<typename T = std::nullptr_t>
|
||||
constexpr ox::Error setTypeInfo(
|
||||
CString name,
|
||||
int version,
|
||||
Vector<String> const &typeParams,
|
||||
size_t fields) noexcept {
|
||||
return m_handler.template setTypeInfo<T>(name, version, typeParams, fields);
|
||||
}
|
||||
|
||||
template<size_t len>
|
||||
constexpr Error fieldCString(CString name, char val[len]) noexcept {
|
||||
return m_handler.fieldCString(name, &val[0], len);
|
||||
}
|
||||
|
||||
template<size_t len>
|
||||
constexpr Error fieldCString(CString name, char const val[len]) noexcept requires(opType_v != OpType::Read) {
|
||||
if constexpr(opType_v != OpType::Read) {
|
||||
return m_handler.fieldCString(name, &val[0], len);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
constexpr Error fieldCString(CString name, char **val) noexcept {
|
||||
return m_handler.fieldCString(name, val);
|
||||
}
|
||||
|
||||
constexpr Error fieldCString(CString name, char const *const*val) noexcept requires(opType_v != OpType::Read) {
|
||||
// this check looks pointless, but it's to address a Clang bug
|
||||
if constexpr(opType_v != OpType::Read) {
|
||||
return m_handler.fieldCString(name, val);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
constexpr Error fieldCString(CString name, char const **val) noexcept requires(opType_v != OpType::Read) {
|
||||
// this check looks pointless, but it's to address a Clang bug
|
||||
if constexpr(opType_v != OpType::Read) {
|
||||
return m_handler.fieldCString(name, val);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
constexpr Error fieldCString(CString name, char **val, size_t buffLen) noexcept {
|
||||
return m_handler.fieldCString(name, val, buffLen);
|
||||
}
|
||||
|
||||
constexpr Error fieldCString(CString name, char const **val, size_t buffLen) noexcept requires(opType_v != OpType::Read) {
|
||||
// this check looks pointless, but it's to address a Clang bug
|
||||
if constexpr(opType_v != OpType::Read) {
|
||||
return m_handler.fieldCString(name, val, buffLen);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
constexpr Error fieldCString(CString name, char *val, size_t buffLen) noexcept {
|
||||
return m_handler.fieldCString(name, val, buffLen);
|
||||
}
|
||||
|
||||
constexpr Error fieldModelValue(char const *name, CommonPtrWith<ModelValue> auto *v) noexcept {
|
||||
switch (v->type()) {
|
||||
case ModelValue::Type::Undefined:
|
||||
break;
|
||||
case ModelValue::Type::Bool:
|
||||
return m_handler.field(name, &v->template get<bool>());
|
||||
case ModelValue::Type::UnsignedInteger8:
|
||||
return m_handler.field(name, &v->template get<uint8_t>());
|
||||
case ModelValue::Type::UnsignedInteger16:
|
||||
return m_handler.field(name, &v->template get<uint16_t>());
|
||||
case ModelValue::Type::UnsignedInteger32:
|
||||
return m_handler.field(name, &v->template get<uint32_t>());
|
||||
case ModelValue::Type::UnsignedInteger64:
|
||||
return m_handler.field(name, &v->template get<uint64_t>());
|
||||
case ModelValue::Type::SignedInteger8:
|
||||
return m_handler.field(name, &v->template get<int8_t>());
|
||||
case ModelValue::Type::SignedInteger16:
|
||||
return m_handler.field(name, &v->template get<int16_t>());
|
||||
case ModelValue::Type::SignedInteger32:
|
||||
return m_handler.field(name, &v->template get<int32_t>());
|
||||
case ModelValue::Type::SignedInteger64:
|
||||
return m_handler.field(name, &v->template get<int64_t>());
|
||||
case ModelValue::Type::String:
|
||||
return m_handler.field(name, &v->template get<String>());
|
||||
case ModelValue::Type::Object:
|
||||
return m_handler.field(name, &v->template get<ModelObject>());
|
||||
case ModelValue::Type::Union:
|
||||
{
|
||||
auto &u = v->template get<ModelUnion>();
|
||||
if constexpr(opType_v == OpType::Read) {
|
||||
u.setActiveField(whichFieldPresent(m_handler, name, u));
|
||||
return m_handler.field(name, UnionView<ModelUnion, true>(&u, u.unionIdx()));
|
||||
} else {
|
||||
return m_handler.field(name, UnionView<ModelUnion const, true>(&u, u.unionIdx()));
|
||||
}
|
||||
}
|
||||
case ModelValue::Type::Vector:
|
||||
return m_handler.field(name, &v->template get<ModelValueVector>());
|
||||
case ModelValue::Type::InlineArray:
|
||||
return m_handler.field(name, &v->template get<ModelValueArray>());
|
||||
}
|
||||
oxErrf("invalid type: {}: {}\n", name, static_cast<int>(v->type()));
|
||||
ox::panic("invalid type");
|
||||
return ox::Error(1, "invalid type");
|
||||
}
|
||||
|
||||
// array handler, with callback to allow handling individual elements
|
||||
template<typename T, typename Callback>
|
||||
constexpr Error field(CString name, Callback cb) noexcept {
|
||||
return m_handler.template field<T, Callback>(name, cb);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(CString name, const T *v) noexcept {
|
||||
if constexpr(ox::is_same_v<T, ModelValue>) {
|
||||
return fieldModelValue(name, v);
|
||||
} else {
|
||||
return m_handler.field(name, v);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(CString name, T *v) noexcept {
|
||||
if constexpr(ox::is_same_v<T, ModelValue>) {
|
||||
return fieldModelValue(name, v);
|
||||
} else {
|
||||
return m_handler.field(name, v);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U, bool force = false>
|
||||
constexpr Error field(CString name, UnionView<U, force> val) noexcept {
|
||||
return m_handler.field(name, val);
|
||||
}
|
||||
|
||||
constexpr Error field(CString name, auto *val, size_t len) noexcept {
|
||||
return m_handler.field(name, val, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an array length from the current location in the buffer.
|
||||
* @param name
|
||||
* @param pass indicates that the parsing should iterate past the array length
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr auto arrayLength(CString name, bool pass = true) noexcept {
|
||||
return m_handler.arrayLength(name, pass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an string length from the current location in the buffer.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
constexpr auto stringLength(CString name) noexcept {
|
||||
return m_handler.stringLength(name);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static constexpr auto opType() noexcept {
|
||||
return Handler::opType();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto handler() noexcept {
|
||||
return m_handler;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename H>
|
||||
static constexpr int whichFieldPresent(H &h, CString name, ModelUnion const &u) noexcept
|
||||
requires(H::opType() == OpType::Read) {
|
||||
return h.whichFieldPresent(name, u);
|
||||
}
|
||||
|
||||
template<typename H>
|
||||
static constexpr int whichFieldPresent(H&, CString, ModelUnion const&) noexcept
|
||||
requires(H::opType() != OpType::Read) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename Handler, OpType opType_v = Handler::opType()>
|
||||
class ModelHandlerBase {
|
||||
private:
|
||||
ModelHandlerInterface<Handler, opType_v> m_interface{*static_cast<Handler*>(this)};
|
||||
public:
|
||||
[[nodiscard]]
|
||||
constexpr auto interface() noexcept {
|
||||
return &m_interface;
|
||||
}
|
||||
[[nodiscard]]
|
||||
static constexpr OpType opType() noexcept {
|
||||
return opType_v;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
constexpr ox::Error resizeVector(auto &vec, size_t sz) {
|
||||
if constexpr(ox::is_same_v<decltype(vec.resize(0)), ox::Error>) {
|
||||
return vec.resize(sz);
|
||||
} else {
|
||||
vec.resize(sz);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/bit.hpp>
|
||||
#include <ox/std/error.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/std/utility.hpp>
|
||||
|
||||
#include "fieldcounter.hpp"
|
||||
#include "modelvalue.hpp"
|
||||
#include "optype.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class Wrap {
|
||||
public:
|
||||
virtual ~Wrap() = default;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class WrapT: public Wrap {
|
||||
private:
|
||||
T *m_obj = nullptr;
|
||||
|
||||
public:
|
||||
constexpr WrapT(T *obj) noexcept: m_obj(obj) {
|
||||
}
|
||||
constexpr WrapT() = default;
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto obj() noexcept {
|
||||
return m_obj;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<std::size_t size>
|
||||
class MemberList {
|
||||
|
||||
private:
|
||||
std::size_t m_i = 0;
|
||||
|
||||
public:
|
||||
Array<void*, size> vars;
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(const char*, T *v) noexcept {
|
||||
vars[m_i++] = static_cast<void*>(v);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(const char*, T *v, int) noexcept {
|
||||
vars[m_i++] = static_cast<void*>(v);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, bool force = false>
|
||||
constexpr Error field(const char*, UnionView<U, force> u) noexcept {
|
||||
vars[m_i++] = static_cast<void*>(u.get());
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr ox::Error setTypeInfo(
|
||||
const char* = T::TypeName,
|
||||
int = T::TypeVersion,
|
||||
const Vector<String>& = {},
|
||||
std::size_t = ModelFieldCount_v<T>) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static constexpr auto opType() noexcept {
|
||||
return OpType::Reflect;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<std::size_t size>
|
||||
class Copier {
|
||||
|
||||
private:
|
||||
std::size_t m_i = 0;
|
||||
MemberList<size> *m_dst = nullptr;
|
||||
|
||||
public:
|
||||
constexpr explicit Copier(MemberList<size> *dst) noexcept: m_dst(dst) {
|
||||
}
|
||||
|
||||
template<typename FT>
|
||||
constexpr Error field(const char *name, const FT *v) noexcept {
|
||||
if constexpr(isVector_v<FT>) {
|
||||
return field(name, v->data(), v->size());
|
||||
} else {
|
||||
auto &src = *v;
|
||||
auto &dst = *cbit_cast<FT*>(m_dst->vars[m_i]);
|
||||
dst = src;
|
||||
++m_i;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FT>
|
||||
constexpr Error field(const char*, const FT *list, int elements) {
|
||||
for (auto i = 0l; i < elements; ++i) {
|
||||
auto &src = list[i];
|
||||
auto &dst = cbit_cast<FT*>(m_dst->vars[m_i])[i];
|
||||
dst = src;
|
||||
}
|
||||
++m_i;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, bool force = false>
|
||||
constexpr Error field(const char*, UnionView<U, force> u) {
|
||||
auto &dst = *cbit_cast<U*>(m_dst->vars[m_i]);
|
||||
auto &src = *u.get();
|
||||
dst = src;
|
||||
++m_i;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T = void>
|
||||
constexpr ox::Error setTypeInfo(
|
||||
const char* = T::TypeName,
|
||||
int = T::TypeVersion,
|
||||
const Vector<String>& = {},
|
||||
int = ModelFieldCount_v<T>) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static constexpr auto opType() noexcept {
|
||||
return OpType::Read;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<std::size_t size>
|
||||
class Mover {
|
||||
|
||||
private:
|
||||
std::size_t m_i = 0;
|
||||
MemberList<size> *m_dst = nullptr;
|
||||
|
||||
public:
|
||||
constexpr explicit Mover(MemberList<size> *dst) noexcept: m_dst(dst) {
|
||||
}
|
||||
|
||||
template<typename FT>
|
||||
constexpr Error field(const char *name, FT *v) noexcept {
|
||||
if constexpr(isVector_v<FT>) {
|
||||
return field(name, v->data(), v->size());
|
||||
} else {
|
||||
auto &src = *v;
|
||||
auto &dst = *cbit_cast<FT*>(m_dst->vars[m_i]);
|
||||
dst = std::move(src);
|
||||
src = FT{};
|
||||
++m_i;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FT>
|
||||
constexpr Error field(const char*, FT *list, int elements) noexcept {
|
||||
for (auto i = 0l; i < elements; ++i) {
|
||||
auto &src = list[i];
|
||||
auto &dst = cbit_cast<FT*>(m_dst->vars[m_i])[i];
|
||||
dst = std::move(src);
|
||||
src = FT{};
|
||||
}
|
||||
++m_i;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, bool force = false>
|
||||
constexpr Error field(const char*, UnionView<U, force> u) noexcept {
|
||||
auto &dst = *cbit_cast<U*>(m_dst->vars[m_i]);
|
||||
auto &src = *u.get();
|
||||
dst = std::move(src);
|
||||
++m_i;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T = void>
|
||||
constexpr ox::Error setTypeInfo(
|
||||
const char* = T::TypeName,
|
||||
int = T::TypeVersion,
|
||||
const Vector<String>& = {},
|
||||
int = ModelFieldCount_v<T>) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static constexpr auto opType() noexcept {
|
||||
return OpType::Read;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<std::size_t size>
|
||||
class Equals {
|
||||
|
||||
private:
|
||||
std::size_t m_i = 0;
|
||||
MemberList<size> *m_other = nullptr;
|
||||
|
||||
public:
|
||||
bool value = false;
|
||||
|
||||
constexpr Equals(MemberList<size> *other) noexcept: m_other(other) {
|
||||
}
|
||||
|
||||
template<typename FT>
|
||||
constexpr Error field(const char*, const FT *v) noexcept {
|
||||
const auto &src = *v;
|
||||
const auto &dst = std::bit_cast<FT>(*m_other->vars[m_i]);
|
||||
++m_i;
|
||||
if (dst == src) {
|
||||
return {};
|
||||
} else {
|
||||
this->value = false;
|
||||
return ox::Error(1);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename FT>
|
||||
constexpr Error field(const char*, const FT *list, int elements) noexcept {
|
||||
for (auto i = 0l; i < elements; ++i) {
|
||||
const auto &src = list[i];
|
||||
const auto &dst = cbit_cast<FT*>(m_other->vars[m_i])[i];
|
||||
if (!(dst == src)) {
|
||||
this->value = false;
|
||||
return ox::Error(1);
|
||||
}
|
||||
}
|
||||
++m_i;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename U, bool force = false>
|
||||
constexpr Error field(const char*, UnionView<U, force> u) noexcept {
|
||||
const auto &dst = *cbit_cast<U*>(m_other->vars[m_i]);
|
||||
const auto &src = *u.get();
|
||||
++m_i;
|
||||
if (dst == src) {
|
||||
return {};
|
||||
} else {
|
||||
this->value = false;
|
||||
return ox::Error(1);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static constexpr auto opType() noexcept {
|
||||
return OpType::Read;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void moveModel(T *dst, T *src) noexcept {
|
||||
constexpr auto size = ModelFieldCount_v<T>;
|
||||
detail::MemberList<size> dstFields;
|
||||
detail::Mover<size> mover(&dstFields);
|
||||
std::ignore = model(&dstFields, dst);
|
||||
std::ignore = model(&mover, src);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr void copyModel(T *dst, const T *src) noexcept {
|
||||
constexpr auto size = ModelFieldCount_v<T>;
|
||||
detail::MemberList<size> dstFields;
|
||||
detail::Copier<size> copier(&dstFields);
|
||||
std::ignore = model(&dstFields, dst);
|
||||
std::ignore = model(&copier, src);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr bool equalsModel(T *a, T *b) noexcept {
|
||||
constexpr auto size = T::Fields;
|
||||
detail::MemberList<size> aFields;
|
||||
detail::Equals<size> equals(&aFields);
|
||||
std::ignore = model(&aFields, a);
|
||||
std::ignore = model(&equals, b);
|
||||
return equals.value;
|
||||
}
|
||||
|
||||
}
|
||||
+1353
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/stringview.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
enum class OpType {
|
||||
Read = 1,
|
||||
Write,
|
||||
Reflect,
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
|
||||
#include "fieldcounter.hpp"
|
||||
#include "optype.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
struct TypeInfoCatcher {
|
||||
|
||||
CString name = "";
|
||||
int version = 0;
|
||||
|
||||
template<typename T = std::nullptr_t>
|
||||
constexpr Error setTypeInfo(
|
||||
CString const n = T::TypeName,
|
||||
int const v = T::TypeVersion,
|
||||
Vector<String> const& = {},
|
||||
size_t = 0) noexcept {
|
||||
this->name = n;
|
||||
this->version = v;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(CString, T*, size_t) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error field(CString, T const&) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Error fieldCString(CString, T const&) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
static constexpr auto opType() noexcept {
|
||||
return OpType::Reflect;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr int getModelTypeVersion(T *val) noexcept {
|
||||
TypeInfoCatcher nc;
|
||||
std::ignore = model(&nc, val);
|
||||
return nc.version;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
constexpr int getModelTypeVersion() noexcept {
|
||||
std::allocator<T> a;
|
||||
const auto t = a.allocate(1);
|
||||
const auto out = getModelTypeVersion(t);
|
||||
a.deallocate(t, 1);
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
consteval int requireModelTypeVersion() noexcept {
|
||||
constexpr auto version = getModelTypeVersion<T>();
|
||||
static_assert(version != 0, "TypeVersion is required");
|
||||
return version;
|
||||
}
|
||||
|
||||
template<typename T, typename Str = const char*>
|
||||
[[nodiscard]]
|
||||
constexpr Str getModelTypeName(T *val) noexcept {
|
||||
TypeInfoCatcher nc;
|
||||
std::ignore = model(&nc, val);
|
||||
return nc.name;
|
||||
}
|
||||
|
||||
template<typename T, typename Str = const char*>
|
||||
[[nodiscard]]
|
||||
consteval Str getModelTypeName() noexcept {
|
||||
std::allocator<T> a;
|
||||
auto t = a.allocate(1);
|
||||
auto out = getModelTypeName(t);
|
||||
a.deallocate(t, 1);
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T, typename Str = StringLiteral>
|
||||
[[nodiscard]]
|
||||
consteval auto requireModelTypeName() noexcept {
|
||||
constexpr auto name = getModelTypeName<T, Str>();
|
||||
static_assert(StringView{name}.size(), "Type lacks required TypeName");
|
||||
return name;
|
||||
}
|
||||
|
||||
template<typename T, typename Str = StringLiteral>
|
||||
constexpr auto ModelTypeName_v = requireModelTypeName<T, Str>();
|
||||
|
||||
template<typename T, typename Str = const char*>
|
||||
constexpr auto ModelTypeVersion_v = requireModelTypeVersion<T>();
|
||||
|
||||
template<typename T>
|
||||
constexpr auto ModelTypeId_v = [] {
|
||||
constexpr auto name = ModelTypeName_v<T, StringView>;
|
||||
constexpr auto version = ModelTypeVersion_v<T>;
|
||||
constexpr auto versionStr = ox::sfmt<IString<19>>("{}", version);
|
||||
return ox::sfmt<IString<name.size() + versionStr.size() + 1>>("{};{}", name, versionStr);
|
||||
}();
|
||||
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if __has_include(<vector>)
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#if __has_include(<array>)
|
||||
#include <array>
|
||||
#endif
|
||||
|
||||
#if __has_include(<QVector>)
|
||||
#include <QVector>
|
||||
#endif
|
||||
|
||||
#include <ox/std/array.hpp>
|
||||
#include <ox/std/istring.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/strops.hpp>
|
||||
#include <ox/std/types.hpp>
|
||||
#include <ox/std/typetraits.hpp>
|
||||
#include <ox/std/vector.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
namespace types {
|
||||
constexpr StringLiteral BasicString = "net.drinkingtea.ox.BasicString";
|
||||
constexpr StringLiteral IString = "net.drinkingtea.ox.IString";
|
||||
constexpr StringLiteral String = "B.string";
|
||||
constexpr StringLiteral Bool = "B.bool";
|
||||
constexpr StringLiteral Uint8 = "B.uint8";
|
||||
constexpr StringLiteral Uint16 = "B.uint16";
|
||||
constexpr StringLiteral Uint32 = "B.uint32";
|
||||
constexpr StringLiteral Uint64 = "B.uint64";
|
||||
constexpr StringLiteral Int8 = "B.int8";
|
||||
constexpr StringLiteral Int16 = "B.int16";
|
||||
constexpr StringLiteral Int32 = "B.int32";
|
||||
constexpr StringLiteral Int64 = "B.int64";
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
consteval bool isBasicString(const T*) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<std::size_t SmallVecSize>
|
||||
consteval bool isBasicString(const BasicString<SmallVecSize>*) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isBasicString_v = isBasicString(static_cast<const T*>(nullptr));
|
||||
|
||||
static_assert(isBasicString_v<ox::BasicString<0ul>>);
|
||||
static_assert(isBasicString_v<ox::BasicString<8ul>>);
|
||||
static_assert(isBasicString_v<ox::String>);
|
||||
|
||||
template<typename T>
|
||||
consteval bool isIString(const T*) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<std::size_t SmallVecSize>
|
||||
consteval bool isIString(const BasicString<SmallVecSize>*) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isIString_v = isIString(static_cast<const T*>(nullptr));
|
||||
|
||||
static_assert(isBasicString_v<ox::BasicString<0ul>>);
|
||||
static_assert(isBasicString_v<ox::BasicString<8ul>>);
|
||||
static_assert(isBasicString_v<ox::String>);
|
||||
|
||||
template<typename T>
|
||||
consteval bool isOxVector(const T*) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t SmallVecSize>
|
||||
consteval bool isOxVector(const Vector<T, SmallVecSize>*) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isOxVector_v = isVector(static_cast<const T*>(nullptr));
|
||||
|
||||
template<typename T>
|
||||
consteval bool isVector(const T*) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t SmallVecSize>
|
||||
consteval bool isVector(const Vector<T, SmallVecSize>*) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if __has_include(<vector>)
|
||||
template<typename T>
|
||||
consteval bool isVector(const std::vector<T>*) noexcept {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __has_include(<QVector>)
|
||||
template<typename T>
|
||||
constexpr bool isVector(const QVector<T>*) noexcept {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isVector_v = isVector(static_cast<const T*>(nullptr));
|
||||
|
||||
static_assert(isVector_v<ox::Vector<unsigned int, 0ul>>);
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isBareArray_v = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isBareArray_v<T[]> = true;
|
||||
|
||||
template<typename T, std::size_t sz>
|
||||
constexpr bool isBareArray_v<T[sz]> = true;
|
||||
|
||||
template<typename T>
|
||||
consteval bool isArray(T*) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isArray_v = isArray(static_cast<T*>(nullptr));
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isArray_v<T[]> = true;
|
||||
|
||||
template<typename T, std::size_t sz>
|
||||
constexpr bool isArray_v<T[sz]> = true;
|
||||
|
||||
template<typename T, std::size_t sz>
|
||||
constexpr bool isArray_v<Array<T, sz>> = true;
|
||||
|
||||
template<typename T, std::size_t sz>
|
||||
consteval bool isArray(T[sz]) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t sz>
|
||||
consteval bool isArray(Array<T, sz>) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isSmartPtr_v = false;
|
||||
|
||||
template<typename T>
|
||||
constexpr bool isSmartPtr_v<::ox::UPtr<T>> = true;
|
||||
|
||||
#if __has_include(<array>)
|
||||
template<typename T>
|
||||
constexpr bool isSmartPtr_v<::std::unique_ptr<T>> = true;
|
||||
#endif
|
||||
|
||||
|
||||
template<typename Union, bool force = false> requires(force || is_union_v<Union>)
|
||||
class UnionView {
|
||||
|
||||
protected:
|
||||
int m_idx = -1;
|
||||
Union *m_union = nullptr;
|
||||
|
||||
public:
|
||||
constexpr UnionView(Union *u, int idx) noexcept: m_idx(idx), m_union(u) {
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto idx() const noexcept {
|
||||
return m_idx;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr const Union *get() const noexcept {
|
||||
return m_union;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr Union *get() noexcept {
|
||||
return m_union;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/fmt.hpp>
|
||||
#include <ox/std/hashmap.hpp>
|
||||
#include <ox/std/memory.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
#include <ox/std/typetraits.hpp>
|
||||
|
||||
#include "typenamecatcher.hpp"
|
||||
#include "desctypes.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
class TypeStore {
|
||||
private:
|
||||
HashMap<String, UPtr<DescriptorType>> m_cache;
|
||||
|
||||
public:
|
||||
constexpr TypeStore() noexcept = default;
|
||||
|
||||
constexpr virtual ~TypeStore() noexcept = default;
|
||||
|
||||
constexpr Result<const DescriptorType*> get(const auto &name, int typeVersion,
|
||||
const TypeParamPack &typeParams) const noexcept {
|
||||
const auto typeId = buildTypeId(name, typeVersion, typeParams);
|
||||
OX_REQUIRE(out, m_cache.at(typeId));
|
||||
return out->get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Result<const DescriptorType*> get() const noexcept {
|
||||
constexpr auto typeName = ModelTypeName_v<T>;
|
||||
constexpr auto typeVersion = ModelTypeVersion_v<T>;
|
||||
const auto typeId = buildTypeId(typeName, typeVersion, {});
|
||||
OX_REQUIRE(out, m_cache.at(typeId));
|
||||
return out->get();
|
||||
}
|
||||
|
||||
constexpr DescriptorType *getInit(StringViewCR typeName, int typeVersion, PrimitiveType pt,
|
||||
const TypeParamPack &typeParams) noexcept {
|
||||
const auto typeId = buildTypeId(typeName, typeVersion, typeParams);
|
||||
auto &out = m_cache[typeId];
|
||||
out = ox::make_unique<DescriptorType>(String(typeName), typeVersion, pt, typeParams);
|
||||
return out.get();
|
||||
}
|
||||
|
||||
constexpr Result<const DescriptorType*> getLoad(const auto &typeId) noexcept {
|
||||
auto [val, err] = m_cache.at(typeId);
|
||||
if (err) {
|
||||
if (!std::is_constant_evaluated()) {
|
||||
OX_REQUIRE_M(dt, loadDescriptor(typeId));
|
||||
for (auto &f : dt->fieldList) {
|
||||
if (typeId == f.typeId) {
|
||||
f.type = dt.get();
|
||||
} else {
|
||||
OX_RETURN_ERROR(this->getLoad(f.typeId).moveTo(f.type));
|
||||
}
|
||||
}
|
||||
auto &out = m_cache[typeId];
|
||||
out = std::move(dt);
|
||||
return out.get();
|
||||
} else {
|
||||
return ox::Error(1, "Type not available");
|
||||
}
|
||||
}
|
||||
return val->get();
|
||||
}
|
||||
|
||||
constexpr Result<const DescriptorType*> getLoad(const auto &typeName, auto typeVersion,
|
||||
const TypeParamPack &typeParams = {}) noexcept {
|
||||
return getLoad(buildTypeId(typeName, typeVersion, typeParams));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr Result<const DescriptorType*> getLoad() noexcept {
|
||||
constexpr auto typeName = requireModelTypeName<T>();
|
||||
constexpr auto typeVersion = requireModelTypeVersion<T>();
|
||||
return getLoad(typeName, typeVersion);
|
||||
}
|
||||
|
||||
constexpr void set(const auto &typeId, UPtr<DescriptorType> dt) noexcept {
|
||||
m_cache[typeId] = std::move(dt);
|
||||
}
|
||||
|
||||
constexpr void set(const auto &typeId, DescriptorType *dt) noexcept {
|
||||
m_cache[typeId] = UPtr<DescriptorType>(dt);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr auto typeList() const noexcept {
|
||||
const auto &keys = m_cache.keys();
|
||||
ox::Vector<DescriptorType*> descs;
|
||||
for (const auto &k : keys) {
|
||||
descs.emplace_back(m_cache.at(k).unwrap()->get());
|
||||
}
|
||||
return descs;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual Result<UPtr<DescriptorType>> loadDescriptor(ox::StringView) noexcept {
|
||||
return ox::Error(1);
|
||||
}
|
||||
|
||||
Result<UPtr<DescriptorType>> loadDescriptor(ox::StringViewCR name, int version,
|
||||
const ox::TypeParamPack &typeParams) noexcept {
|
||||
const auto typeId = buildTypeId(name, version, typeParams);
|
||||
return loadDescriptor(typeId);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/std/error.hpp>
|
||||
|
||||
#include "desctypes.hpp"
|
||||
|
||||
namespace ox {
|
||||
|
||||
template<typename Reader, typename T>
|
||||
class DataWalker {
|
||||
template<typename ReaderBase, typename FH>
|
||||
friend constexpr Error parseField(const DescriptorField &field, ReaderBase *rdr, DataWalker<ReaderBase, FH> *walker) noexcept;
|
||||
|
||||
private:
|
||||
Vector<const DescriptorType*> m_typeStack;
|
||||
T m_fieldHandler;
|
||||
Vector<FieldName> m_path;
|
||||
Vector<String> m_typePath;
|
||||
|
||||
public:
|
||||
constexpr DataWalker(DescriptorType *type, T fieldHandler) noexcept;
|
||||
|
||||
constexpr Result<const DescriptorType*> type() const noexcept;
|
||||
|
||||
constexpr Error read(const DescriptorField&, Reader *rdr) noexcept;
|
||||
|
||||
protected:
|
||||
constexpr void pushNamePath(const FieldName &fn) noexcept;
|
||||
|
||||
constexpr void popNamePath() noexcept;
|
||||
|
||||
constexpr void pushType(const DescriptorType *type) noexcept;
|
||||
|
||||
constexpr void popType() noexcept;
|
||||
|
||||
};
|
||||
|
||||
template<typename Reader, typename T>
|
||||
constexpr DataWalker<Reader, T>::DataWalker(DescriptorType *type, T fieldHandler) noexcept: m_fieldHandler(fieldHandler) {
|
||||
m_typeStack.push_back(type);
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
constexpr Result<const DescriptorType*> DataWalker<Reader, T>::type() const noexcept {
|
||||
OX_REQUIRE(out, m_typeStack.back());
|
||||
return *out;
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
constexpr Error DataWalker<Reader, T>::read(const DescriptorField &f, Reader *rdr) noexcept {
|
||||
// get const ref of paths
|
||||
const auto &pathCr = m_path;
|
||||
const auto &typePathCr = m_typePath;
|
||||
return m_fieldHandler(pathCr, typePathCr, f, rdr);
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
constexpr void DataWalker<Reader, T>::pushNamePath(const FieldName &fn) noexcept {
|
||||
m_path.emplace_back(fn);
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
constexpr void DataWalker<Reader, T>::popNamePath() noexcept {
|
||||
m_path.pop_back();
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
constexpr void DataWalker<Reader, T>::pushType(const DescriptorType *type) noexcept {
|
||||
m_typeStack.push_back(type);
|
||||
}
|
||||
|
||||
template<typename Reader, typename T>
|
||||
constexpr void DataWalker<Reader, T>::popType() noexcept {
|
||||
m_typeStack.pop_back();
|
||||
}
|
||||
|
||||
template<typename Reader, typename FH>
|
||||
static constexpr Error parseField(const DescriptorField &field, Reader *rdr, DataWalker<Reader, FH> *walker) noexcept {
|
||||
walker->pushNamePath(field.fieldName);
|
||||
if (field.subscriptLevels) {
|
||||
// add array handling
|
||||
OX_REQUIRE(arrayLen, rdr->arrayLength(field.fieldName.c_str(), true));
|
||||
auto child = rdr->child(field.fieldName.c_str());
|
||||
OX_RETURN_ERROR(child.setTypeInfo(field.type->typeName.c_str(), field.type->typeVersion, field.type->typeParams, arrayLen));
|
||||
DescriptorField f(field); // create mutable copy
|
||||
--f.subscriptLevels;
|
||||
String subscript;
|
||||
for (std::size_t i = 0; i < arrayLen; i++) {
|
||||
subscript = "[";
|
||||
subscript += static_cast<uint64_t>(i);
|
||||
subscript += "]";
|
||||
walker->pushNamePath(subscript);
|
||||
OX_RETURN_ERROR(parseField(f, &child, walker));
|
||||
walker->popNamePath();
|
||||
}
|
||||
rdr->nextField();
|
||||
} else {
|
||||
switch (field.type->primitiveType) {
|
||||
case PrimitiveType::UnsignedInteger:
|
||||
case PrimitiveType::SignedInteger:
|
||||
case PrimitiveType::Bool:
|
||||
case PrimitiveType::String:
|
||||
OX_RETURN_ERROR(walker->read(field, rdr));
|
||||
break;
|
||||
case PrimitiveType::Struct:
|
||||
case PrimitiveType::Union:
|
||||
if (rdr->fieldPresent(field.fieldName.c_str())) {
|
||||
auto child = rdr->child(field.fieldName.c_str());
|
||||
walker->pushType(field.type);
|
||||
OX_RETURN_ERROR(model(&child, walker));
|
||||
walker->popType();
|
||||
rdr->nextField();
|
||||
} else {
|
||||
// skip and discard absent field
|
||||
int discard;
|
||||
OX_RETURN_ERROR(rdr->field(field.fieldName.c_str(), &discard));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
walker->popNamePath();
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Reader, typename FH>
|
||||
constexpr Error model(Reader *rdr, DataWalker<Reader, FH> *walker) noexcept {
|
||||
OX_REQUIRE(type, walker->type());
|
||||
auto typeName = type->typeName.c_str();
|
||||
auto typeVersion = type->typeVersion;
|
||||
auto typeParams = type->typeParams;
|
||||
auto &fields = type->fieldList;
|
||||
OX_RETURN_ERROR(rdr->setTypeInfo(typeName, typeVersion, typeParams, fields.size()));
|
||||
for (const auto &field : fields) {
|
||||
OX_RETURN_ERROR(parseField(field, rdr, walker));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename Reader, typename Handler>
|
||||
constexpr Error walkModel(DescriptorType *type, Reader_c auto &reader, Handler handler) noexcept {
|
||||
DataWalker<Reader, Handler> walker(type, handler);
|
||||
Reader rdr(reader);
|
||||
return model(&rdr, &walker);
|
||||
}
|
||||
|
||||
template<typename Reader, typename Handler>
|
||||
constexpr Error walkModel(DescriptorType *type, const char *data, std::size_t dataLen, Handler handler) noexcept {
|
||||
DataWalker<Reader, Handler> walker(type, handler);
|
||||
Reader rdr(reinterpret_cast<const uint8_t*>(data), dataLen);
|
||||
return model(&rdr, &walker);
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <ox/model/desctypes.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <ox/model/descwrite.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct preloadable_type {
|
||||
static constexpr auto Preloadable = true;
|
||||
};
|
||||
|
||||
struct non_preloadable_type {
|
||||
static constexpr auto Preloadable = false;
|
||||
};
|
||||
|
||||
struct non_preloadable_type2 {
|
||||
};
|
||||
|
||||
static_assert(preloadable<preloadable_type>::value);
|
||||
static_assert(!preloadable<non_preloadable_type>::value);
|
||||
static_assert(!preloadable<non_preloadable_type2>::value);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static_assert([] {
|
||||
return detail::indirectionLevels_v<int> == 0;
|
||||
}(), "indirectionLevels broken: indirectionLevels(int)");
|
||||
|
||||
static_assert([] {
|
||||
return detail::indirectionLevels_v<int*> == 1;
|
||||
}(), "indirectionLevels broken: indirectionLevels(int*)");
|
||||
|
||||
static_assert([] {
|
||||
return detail::indirectionLevels_v<int[2]> == 1;
|
||||
}(), "indirectionLevels broken: indirectionLevels(int[])");
|
||||
|
||||
static_assert([] {
|
||||
return detail::indirectionLevels_v<int**> == 2;
|
||||
}(), "indirectionLevels broken: indirectionLevels(int[])");
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <ox/std/hashmap.hpp>
|
||||
|
||||
#include <ox/model/modelvalue.hpp>
|
||||
|
||||
namespace ox {
|
||||
|
||||
static_assert([]() -> Error {
|
||||
ModelValue v;
|
||||
OX_RETURN_ERROR(v.setType<int32_t>());
|
||||
if (v.type() != ModelValue::Type::SignedInteger32) {
|
||||
return Error(1, "type is wrong");
|
||||
}
|
||||
//oxReturnError(v.set<int32_t>(5));
|
||||
return {};
|
||||
}() == Error{});
|
||||
|
||||
// a dummy function to prevent linker errors in a library that has no other symbols
|
||||
void modelDummyFunc() noexcept {}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
add_executable(
|
||||
ModelTest
|
||||
tests.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
ModelTest
|
||||
OxModel
|
||||
)
|
||||
|
||||
add_test("[ox/model] ModelValue" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ModelTest ModelValue)
|
||||
add_test("[ox/model] getModelTypeName" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ModelTest getModelTypeName)
|
||||
add_test("[ox/model] getModelTypeVersion" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ModelTest getModelTypeVersion)
|
||||
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2015 - 2025 gary@drinkingtea.net
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#undef NDEBUG
|
||||
|
||||
#include <map>
|
||||
#include <ox/model/model.hpp>
|
||||
#include <ox/std/std.hpp>
|
||||
|
||||
struct TestType {
|
||||
static constexpr auto TypeName = "net.drinkingtea.model.test.TestType";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
};
|
||||
|
||||
OX_MODEL_BEGIN(TestType)
|
||||
OX_MODEL_END()
|
||||
|
||||
struct TestType2 {
|
||||
};
|
||||
|
||||
template<typename Str = ox::StringView>
|
||||
constexpr auto getModelTypeName(TestType2*) noexcept {
|
||||
return "net.drinkingtea.model.test.TestType2";
|
||||
}
|
||||
|
||||
constexpr auto getModelTypeVersion(TestType2*) noexcept {
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::map<ox::StringView, ox::Error(*)()> tests = {
|
||||
{
|
||||
{
|
||||
"ModelValue",
|
||||
[] {
|
||||
ox::ModelValue v;
|
||||
OX_RETURN_ERROR(v.setType<int32_t>());
|
||||
//v.m_type = ox::ModelValue::getType<int32_t>();
|
||||
if (v.type() != ox::ModelValue::Type::SignedInteger32) {
|
||||
return ox::Error(1, "type is wrong");
|
||||
}
|
||||
OX_RETURN_ERROR(v.set<int32_t>(5));
|
||||
return ox::Error{};
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"getModelTypeName",
|
||||
[] {
|
||||
oxAssert(ox::getModelTypeName<TestType>() == TestType::TypeName, "getModelTypeName call failed");
|
||||
oxAssert(ox::getModelTypeName<TestType2>() == ox::StringView("net.drinkingtea.model.test.TestType2"), "getModelTypeName call failed");
|
||||
return ox::Error{};
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"getModelTypeVersion",
|
||||
[] {
|
||||
oxAssert(ox::getModelTypeVersion<TestType>() == TestType::TypeVersion, "getModelTypeVersion call failed");
|
||||
oxAssert(ox::getModelTypeVersion<TestType2>() == 2, "getModelTypeVersion call failed");
|
||||
return ox::Error{};
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, const char **args) {
|
||||
if (argc < 2) {
|
||||
oxError("Must specify test to run");
|
||||
}
|
||||
auto const testName = args[1];
|
||||
auto const func = tests.find(testName);
|
||||
if (func != tests.end()) {
|
||||
oxAssert(func->second(), "Test returned Error");
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
Reference in New Issue
Block a user