Final attempt to revamp this cursed API

This commit is contained in:
S David 2024-09-02 18:48:20 -04:00
parent ec369bdb89
commit edcba981dd
8 changed files with 124 additions and 203 deletions

View File

@ -17,8 +17,6 @@ namespace IOCore {
struct TomlTable : public toml::table {
private:
using TomlSerializer = IOCore::TOML::Serializer;
public:
TomlTable() = default;
TomlTable(const toml::table& tbl) : toml::table(tbl) {}
@ -26,8 +24,7 @@ struct TomlTable : public toml::table {
TomlTable(TomlTable&& tbl) noexcept : toml::table(std::move(tbl)) {}
template<typename T>
TomlTable(const T& obj)
: toml::table(TomlSerializer::to_table<std::decay_t<T>>(obj))
TomlTable(const T& obj) : toml::table(to_table<std::decay_t<T>>(obj))
{
}
@ -36,7 +33,7 @@ struct TomlTable : public toml::table {
auto operator=(const T& obj) -> TomlTable&
{
using value_t = std::decay_t<T>;
auto value = TomlSerializer::to_table<value_t>(obj);
auto value = to_table<value_t>(obj);
toml::table::operator=(value);
return *this;
}
@ -46,7 +43,7 @@ struct TomlTable : public toml::table {
auto operator=(T&& obj) -> TomlTable&
{
using value_t = std::decay_t<T>;
auto value = TomlSerializer::to_table<value_t>(obj);
auto value = to_table<value_t>(obj);
toml::table::operator=(std::move(value));
return *this;
}
@ -55,7 +52,7 @@ struct TomlTable : public toml::table {
auto get() const -> T
{
T retval;
TomlSerializer::from_table<T>(*this, retval);
from_table<T>(*this, retval);
return retval;
}

53
include/tomlize.hpp Normal file
View File

@ -0,0 +1,53 @@
/* tomlize.hpp
* Copyright © 2024 Saul D. Beniquez
* License: Mozilla Public License v2.0 (MPL2)
*
* 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 <toml++/toml.hpp>
namespace TOML {
using ErrorFlag = bool;
static constexpr ErrorFlag error_t = true;
static constexpr ErrorFlag success_t = false;
template<typename T>
auto to_toml(const T&) -> toml::table;
template<typename T>
auto from_toml(const toml::table&, T&) -> ErrorFlag;
template<typename TField>
void add_toml_field(const TField&, toml::table&);
template<typename TField>
auto extract_toml_field(const toml::table&, TField&) -> ErrorFlag;
#define TOMILIZE_FIELD(FIELD) add_toml_field(FIELD, table);
#define DETOMLIZE_FIELD(FIELD) result |= extract_toml_field(table, FIELD);
#define TOMLIZE_STRUCT(STRUCT, ...) \
template<> \
auto to_toml(const STRUCT& s)->toml::table \
{ \
toml::table table; \
FOREACH_PARAM(TOMLIZE_FIELD, __VA_ARGS__); \
return table; \
} \
template<> \
auto from_toml(const toml::table& table, STRUCT& s)->ErrorFlag \
{ \
auto result = success_t; \
FOREACH_PARAM(DETOMLIZE_FIELD, __VA_ARGS__); \
return result; \
}
} // namespace TOML
// clang-format off
// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :

View File

@ -10,9 +10,11 @@
#pragma once
#include <algorithm>
#include <any>
#include <initializer_list>
#include <string>
#include <type_traits>
#include <typeindex>
#include <utility>
#include <toml++/toml.hpp>
@ -35,41 +37,45 @@ struct TomlException : public Exception {
}
~TomlException() override = default;
};
}
namespace IOCore::TOML {
struct Serializer {
template<typename TClass>
static auto to_table(const TClass& obj) -> toml::table // NOLINT
namespace TOML {
template<typename TClass>
struct AdlSerializer {
static auto to_toml(const TClass& obj)
{
using class_t = std::decay_t<TClass>;
auto result = class_t::to_toml(obj);
return result;
return class_t::to_toml(obj);
}
template<typename TClass>
static void from_table(const toml::table& tbl, TClass& result) // NOLINT
static void from_toml(const toml::table& tbl, TClass& result)
{
using class_t = std::decay_t<TClass>;
class_t::from_toml(tbl, result);
}
template<typename TField>
static void insert_field( // NOLINT
toml::table& tbl, const char* fieldName, const TField& obj
)
static void
insert_field(toml::table& tbl, const char* fieldName, const TField& obj)
{
using value_type = std::decay_t<TField>;
if constexpr (is_toml_type_t<value_type>) {
tbl.insert_or_assign(fieldName, obj);
} else {
auto subtable = to_table(obj);
if constexpr (std::is_enum_v<value_type>) {
AdlSerializer::insert_enum_field<value_type>()
} else {
auto subtable = AdlSerializer<value_type>::to_toml(obj);
tbl.insert_or_assign(fieldName, subtable);
}
}
template<typename TField>
static void extract_field( // NOLINT
static void insert_enum_field(
toml::table& tbl, const char* fieldName, TField obj
);
template<typename TField>
static void extract_field(
const toml::table& tbl, const char* fieldName, TField& output
)
{
@ -84,22 +90,22 @@ struct Serializer {
output = tbl[fieldName].value<value_type>().value();
} else {
auto subtable = *(tbl[fieldName].as_table());
from_table(subtable, output);
AdlSerializer<value_type>::from_toml(subtable, output);
}
}
};
}
}
#define IOCORE_TOML_FIELD(field) \
IOCore::TOML::Serializer::insert_field<decltype(obj.field)>( \
tbl, #field, obj.field \
);
IOCore::TOML::AdlSerializer<decltype(obj \
)>::insert_field<decltype(obj.field)>(tbl, #field, obj.field);
#define IOCORE_TOML_EXTRACT_FIELD(field) \
IOCore::TOML::Serializer::extract_field<decltype(result.field)>( \
tbl, #field, result.field \
);
IOCore::TOML::AdlSerializer<decltype(result \
)>::extract_field<decltype(result.field)>(tbl, #field, result.field);
#define IOCORE_TOML_SERIALIZABLE(CLASS, ...) \
private: \
static constexpr auto _class_name()->const char* \
{ \
return #CLASS; \
@ -116,62 +122,34 @@ struct Serializer {
{ \
FOREACH_PARAM(IOCORE_TOML_EXTRACT_FIELD, __VA_ARGS__) \
} \
friend class IOCore::TOML::Serializer;
friend IOCore::TOML::AdlSerializer<CLASS>; \
;
#define IOCORE_TOML_ENUM(ENUM_TYPE, ...) \
template<> \
inline void IOCore::TOML::Serializer::insert_field<ENUM_TYPE>( \
toml::table & tbl, const char* fieldName, const ENUM_TYPE& obj \
template<typename T> \
void IOCore::TOML::AdlSerializer<T>::insert_field( \
toml::table& tbl, const char* fieldName, ENUM_TYPE obj \
) \
{ \
constexpr bool enum_check = std::is_enum<ENUM_TYPE>::value; \
static_assert(enum_check, #ENUM_TYPE "must be an enum!"); \
\
using pair_t = std::pair<ENUM_TYPE, const char*>; \
static const pair_t _enum_to_string[] = { \
FOREACH_ENUM_PARAM(TOML_ENUM_FIELD, __VA_ARGS__) \
}; \
auto it = std::find_if( \
std::begin(_enum_to_string), \
std::end(_enum_to_string), \
[obj](const auto& pair) -> bool { \
return pair.first == obj; \
} \
); \
tbl.insert_or_assign(fieldName, it->second); \
; \
} \
\
template<> \
inline void IOCore::TOML::Serializer::extract_field<ENUM_TYPE>( \
const toml::table& tbl, const char* fieldName, ENUM_TYPE& obj \
template<typename T> \
void IOCore::TOML::AdlSerializer<T>::extract_field( \
const toml::table& tbl, const char* fieldName, ENUM_TYPE& output \
) \
{ \
constexpr bool enum_check = std::is_enum<ENUM_TYPE>::value; \
static_assert(enum_check, #ENUM_TYPE "must be an enum!"); \
\
using pair_t = std::pair<ENUM_TYPE, const char*>; \
static const pair_t _enum_to_string[] = { \
FOREACH_ENUM_PARAM(TOML_ENUM_FIELD, __VA_ARGS__) \
}; \
auto val = tbl[fieldName].value<std::string>().value(); \
for (const auto& [enum_val, str] : _enum_to_string) { \
if (str == val) { \
obj = enum_val; \
break; \
} \
} \
; \
}
// clang-format off
#define IOCORE_TOML_SERIALIZE_IMPL(CLASS) \
template<> \
auto Serializer::to_table<CLASS>(const CLASS& obj) \
->toml::table
template<> \
auto AdlSerializer<CLASS>::to_table(const CLASS& obj)->toml::table
#define IOCORE_TOML_DESERIALIZE_IMPL(CLASS) \
template<> \
auto Serializer::from_table<CLASS>(const toml::table& tbl, CLASS& result)\
-> void
template<> \
void AdlSerializer<CLASS>::from_table( \
const toml::table& tbl, CLASS& result \
)
// clang-format off
// vim: set foldmethod=marker foldmarker=@{,@} foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :

View File

@ -4,8 +4,8 @@ add_library(IOCore OBJECT
Exception.cpp
FileResource.cpp
debuginfo.cpp
JsonConfigFile.cpp
TomlConfigFile.cpp
#JsonConfigFile.cpp
#TomlConfigFile.cpp
)
set_target_properties(IOCore PROPERTIES

View File

@ -3,12 +3,12 @@ add_executable(test-runner
runtime-tests.cpp
Util.macros.test.cpp
Util.toml.test.cpp
TomlTable.test.cpp
Application.test.cpp
FileResource.test.cpp
Exception.test.cpp
JsonConfigFile.test.cpp
TomlConfigFile.test.cpp
#TomlTable.test.cpp
#Application.test.cpp
#FileResource.test.cpp
#Exception.test.cpp
#JsonConfigFile.test.cpp
#TomlConfigFile.test.cpp
)
target_include_directories(test-runner PRIVATE

View File

@ -14,8 +14,10 @@
#include "../include/TomlTable.hpp"
#include "../include/util/toml.hpp"
namespace impl {
enum Colors { Red, Green, Blue };
IOCORE_TOML_ENUM(Colors, Red, Green, Blue);
}
BEGIN_TEST_SUITE("IOCore::TomlTable")
{

View File

@ -10,7 +10,6 @@
#include "test-utils/common.hpp"
#include "../include/util/macros.hpp"
#include "../include/util/toml.hpp"
#include <iterator>
#include <sstream>

View File

@ -7,44 +7,37 @@
* obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <toml++/toml.h>
#include <unordered_map>
#include "test-utils/common.hpp"
#include "../include/util/toml.hpp"
enum Colors { Red, Green, Blue };
IOCORE_TOML_ENUM(Colors, Red, Green, Blue);
#include "tomlize.hpp"
BEGIN_TEST_SUITE("Util.Toml")
{
enum Colors { Red, Green, Blue };
struct SimpleStruct {
int field1;
int field2;
IOCORE_TOML_SERIALIZABLE(SimpleStruct, field1, field2);
char field2;
};
TOMLIZE_STRUCT(SimpleStruct, field1, field2);
struct SimpleStruct2 {
int fieldA;
IOCORE_TOML_SERIALIZABLE(SimpleStruct2, fieldA);
int fieldB;
};
struct StructWithEnum {
int field1;
int field2;
Colors foreground;
IOCORE_TOML_SERIALIZABLE(
StructWithEnum, field1, field2, foreground
);
};
struct CompositeStruct {
SimpleStruct part1;
SimpleStruct2 part2;
IOCORE_TOML_SERIALIZABLE(CompositeStruct, part1, part2);
};
struct ComplexStruct {
@ -52,115 +45,14 @@ BEGIN_TEST_SUITE("Util.Toml")
SimpleStruct2 part2;
StructWithEnum part3;
Colors background;
IOCORE_TOML_SERIALIZABLE(
ComplexStruct, part1, part2, part3, background
);
};
TEST_CASE("IOCORE_TOML_TO Macro works")
TEST_CASE("SimpleStruct Serailizes")
{
toml::table tbl;
SimpleStruct obj;
SimpleStruct s{ 1 };
toml::table t = TOML::to_toml(s);
IOCORE_TOML_FIELD(field1);
IOCORE_TOML_FIELD(field2);
REQUIRE(tbl.size() == 2);
}
TEST_CASE("IOCORE_TOML_SERIALIZABLE Macro works with SimpleStruct")
{
toml::table table;
SimpleStruct data = { 10, 20 };
SimpleStruct newdest;
table = SimpleStruct::to_toml(data);
SimpleStruct::from_toml(table, newdest);
CHECK(table.size() == 2);
CHECK(table["field1"].value<int>() == 10);
CHECK(table["field2"].value<int>() == 20);
CHECK(newdest.field1 == data.field1);
CHECK(newdest.field2 == data.field2);
}
TEST_CASE("IOCORE_TOML_SERIALIZABLE Macro works with StructWithEnum")
{
toml::table table;
StructWithEnum data = { 11, 22, Green };
StructWithEnum newdest;
table = StructWithEnum::to_toml(data);
StructWithEnum::from_toml(table, newdest);
CHECK(table.size() == 3);
CHECK(table["field1"].value<int>() == 11);
CHECK(table["field2"].value<int>() == 22);
CHECK(table["foreground"].value<std::string>() == "Green");
CHECK(newdest.field1 == data.field1);
CHECK(newdest.field2 == data.field2);
CHECK(newdest.foreground == data.foreground);
}
TEST_CASE("IOCORE_TOML_SERIALIZABLE Macro works with CompositeStruct")
{
toml::table table;
CompositeStruct data = { { 10, 20 }, { 30 } };
CompositeStruct newdest;
table = CompositeStruct::to_toml(data);
CompositeStruct::from_toml(table, newdest);
CHECK(table.size() == 2);
CHECK(table["part1"].as_table()->size() == 2);
CHECK(table["part2"].as_table()->size() == 1);
CHECK(table["part1"]["field1"].value<int>() == 10);
CHECK(table["part1"]["field2"].value<int>() == 20);
CHECK(table["part2"]["fieldA"].value<int>() == 30);
CHECK(newdest.part1.field1 == data.part1.field1);
CHECK(newdest.part1.field2 == data.part1.field2);
CHECK(newdest.part2.fieldA == data.part2.fieldA);
}
TEST_CASE("IOCORE_TOML_SERIALIZABLE Macro works with ComplexStruct")
{
toml::table table;
ComplexStruct data = {
{ 10, 20 }, { 30 }, { 40, 50, Blue }, Red
};
ComplexStruct newdest;
table = ComplexStruct::to_toml(data);
ComplexStruct::from_toml(table, newdest);
CHECK(table.size() == 4);
CHECK(table["part1"].as_table()->size() == 2);
CHECK(table["part2"].as_table()->size() == 1);
CHECK(table["part3"].as_table()->size() == 3);
CHECK(table["part1"]["field1"].value<int>() == 10);
CHECK(table["part1"]["field2"].value<int>() == 20);
CHECK(table["part2"]["fieldA"].value<int>() == 30);
CHECK(table["part3"]["field1"].value<int>() == 40);
CHECK(table["part3"]["field2"].value<int>() == 50);
CHECK(
table["part3"]["foreground"].value<std::string>() == "Blue"
);
CHECK(newdest.part1.field1 == data.part1.field1);
CHECK(newdest.part1.field2 == data.part1.field2);
CHECK(newdest.part2.fieldA == data.part2.fieldA);
CHECK(newdest.part3.field1 == data.part3.field1);
CHECK(newdest.part3.field2 == data.part3.field2);
CHECK(newdest.part3.foreground == data.part3.foreground);
CHECK(newdest.background == data.background);
CHECK(t.at("field1").as_integer() == 1);
}
}
// clang-format off