Skip to content

Commit

Permalink
Worked out a solution that works in principle and wrote a POC for JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
liuzicheng1987 committed Dec 27, 2023
1 parent 8e63233 commit b811434
Show file tree
Hide file tree
Showing 25 changed files with 311 additions and 182 deletions.
56 changes: 41 additions & 15 deletions include/rfl/json/Writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ class Writer {

~Writer() = default;

OutputArrayType array_as_root(const size_t _size) const noexcept {
const auto arr = yyjson_mut_arr(doc_);
yyjson_mut_doc_set_root(doc_, arr);
return OutputArrayType(arr);
}

OutputObjectType object_as_root(const size_t _size) const noexcept {
const auto obj = yyjson_mut_obj(doc_);
yyjson_mut_doc_set_root(doc_, obj);
return OutputObjectType(obj);
}

template <class T>
OutputVarType value_as_root(const T& _var) const noexcept {
const auto val = from_basic_type(_var);
yyjson_mut_doc_set_root(doc_, val.val_);
return OutputVarType(val);
}

OutputArrayType add_array_to_array(const size_t _size,
OutputArrayType* _parent) const noexcept {
const auto arr = yyjson_mut_arr(doc_);
Expand All @@ -64,36 +83,43 @@ class Writer {
return OutputArrayType(arr);
}

OutputArrayType add_array_to_array(const size_t _size,
OutputArrayType* _parent) const noexcept {
const auto arr = yyjson_mut_arr(doc_);
yyjson_mut_arr_add_val(_parent->val_, arr);
return OutputArrayType(arr);
OutputObjectType add_object_to_array(
const size_t _size, OutputArrayType* _parent) const noexcept {
const auto obj = yyjson_mut_obj(doc_);
yyjson_mut_arr_add_val(_parent->val_, obj);
return OutputObjectType(obj);
}

OutputArrayType add_array_to_object(
OutputObjectType add_object_to_object(
const std::string& _name, const size_t _size,
OutputObjectType* _parent) const noexcept {
const auto arr = yyjson_mut_arr(doc_);
const auto obj = yyjson_mut_obj(doc_);
yyjson_mut_obj_add(_parent->val_, yyjson_mut_strcpy(doc_, _name.c_str()),
arr);
return OutputArrayType(arr);
obj);
return OutputObjectType(obj);
}

template <class T>
void add_value_to_array(const T& _var,
OutputArrayType* _parent) const noexcept {
OutputVarType add_value_to_array(const T& _var,
OutputArrayType* _parent) const noexcept {
const auto val = from_basic_type(_var);
yyjson_mut_arr_add_val(_parent->val_, val);
yyjson_mut_arr_add_val(_parent->val_, val.val_);
return OutputVarType(val);
}

template <class T>
void add_value_to_object(const std::string& _name, const T& _var,
OutputObjectType* _parent) const noexcept {
OutputVarType add_value_to_object(const std::string& _name, const T& _var,
OutputObjectType* _parent) const noexcept {
const auto val = from_basic_type(_var);
yyjson_mut_obj_add(_parent->val_, yyjson_mut_strcpy(doc_, _name.c_str()),
val);
val.val_);
return OutputVarType(val);
}

void end_array(OutputArrayType* _arr) const noexcept {}

void end_object(OutputObjectType* _obj) const noexcept {}

OutputVarType empty_var() const noexcept {
return OutputVarType(yyjson_mut_null(doc_));
}
Expand Down
9 changes: 5 additions & 4 deletions include/rfl/json/write.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
#include <string>

#include "rfl/json/Parser.hpp"
#include "rfl/parsing/Parent.hpp"

namespace rfl {
namespace json {

/// Returns a JSON string.
template <class T>
std::string write(const T& _obj) {
using ParentType = parsing::Parent<Writer>;
auto w = Writer(yyjson_mut_doc_new(NULL));
const auto json_obj = Parser<T>::write(w, _obj);
yyjson_mut_doc_set_root(w.doc_, json_obj.val_);
Parser<T>::write(w, _obj, typename ParentType::Root{});
const char* json_c_str = yyjson_mut_write(w.doc_, 0, NULL);
const auto json_str = std::string(json_c_str);
free((void*)json_c_str);
Expand All @@ -28,9 +29,9 @@ std::string write(const T& _obj) {
/// Writes a JSON into an ostream.
template <class T>
std::ostream& write(const T& _obj, std::ostream& _stream) {
using ParentType = parsing::Parent<Writer>;
auto w = Writer(yyjson_mut_doc_new(NULL));
const auto json_obj = Parser<T>::write(w, _obj);
yyjson_mut_doc_set_root(w.doc_, json_obj.val_);
Parser<T>::write(w, _obj, typename ParentType::Root{});
const char* json_c_str = yyjson_mut_write(w.doc_, 0, NULL);
_stream << json_c_str;
free((void*)json_c_str);
Expand Down
50 changes: 26 additions & 24 deletions include/rfl/parsing/CustomParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,33 @@ namespace parsing {
template <class ReaderType, class WriterType, class OriginalClass,
class HelperStruct>
struct CustomParser {
static Result<OriginalClass> read(const ReaderType& _r,
const auto& _var) noexcept {
const auto to_class = [](auto&& _h) -> Result<OriginalClass> {
try {
if constexpr (internal::has_to_class_method_v<HelperStruct>) {
return _h.to_class();
} else {
auto ptr_field_tuple = internal::to_ptr_field_tuple(_h);
const auto class_from_ptrs = [](auto&... _ptrs) {
return OriginalClass(std::move(*_ptrs.value_)...);
};
return std::apply(class_from_ptrs, ptr_field_tuple);
}
} catch (std::exception& e) {
return Error(e.what());
}
};
return Parser<ReaderType, WriterType, HelperStruct>::read(_r, _var)
.and_then(to_class);
}
static Result<OriginalClass> read(const ReaderType& _r,
const auto& _var) noexcept {
const auto to_class = [](auto&& _h) -> Result<OriginalClass> {
try {
if constexpr (internal::has_to_class_method_v<HelperStruct>) {
return _h.to_class();
} else {
auto ptr_field_tuple = internal::to_ptr_field_tuple(_h);
const auto class_from_ptrs = [](auto&... _ptrs) {
return OriginalClass(std::move(*_ptrs.value_)...);
};
return std::apply(class_from_ptrs, ptr_field_tuple);
}
} catch (std::exception& e) {
return Error(e.what());
}
};
return Parser<ReaderType, WriterType, HelperStruct>::read(_r, _var)
.and_then(to_class);
}

static auto write(const WriterType& _w, const OriginalClass& _p) noexcept {
return Parser<ReaderType, WriterType, HelperStruct>::write(
_w, HelperStruct::from_class(_p));
}
template <class P>
static auto write(const WriterType& _w, const OriginalClass& _p,
const P& _parent) noexcept {
Parser<ReaderType, WriterType, HelperStruct>::write(
_w, HelperStruct::from_class(_p), _parent);
}
};

} // namespace parsing
Expand Down
9 changes: 5 additions & 4 deletions include/rfl/parsing/FieldVariantParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ struct FieldVariantParser {
return _r.to_object(_var).transform(to_map).and_then(to_result);
}

static OutputVarType write(const W& _w,
const std::variant<FieldTypes...>& _v) noexcept {
template <class P>
static void write(const W& _w, const std::variant<FieldTypes...>& _v,
const P& _parent) noexcept {
static_assert(
internal::no_duplicate_field_names<std::tuple<FieldTypes...>>(),
"Externally tagged variants cannot have duplicate field "
Expand All @@ -61,10 +62,10 @@ struct FieldVariantParser {
const auto handle = [&](const auto& _field) {
const auto named_tuple = make_named_tuple(internal::to_ptr_field(_field));
using NamedTupleType = std::decay_t<decltype(named_tuple)>;
return Parser<R, W, NamedTupleType>::write(_w, named_tuple);
Parser<R, W, NamedTupleType>::write(_w, named_tuple, _parent);
};

return std::visit(handle, _v);
std::visit(handle, _v);
}

private:
Expand Down
30 changes: 1 addition & 29 deletions include/rfl/parsing/IsWriter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,7 @@ concept IsWriter = requires(W w, T t, std::string name,
typename W::OutputArrayType arr,
typename W::OutputObjectType obj,
typename W::OutputVarType var) {
/// Any Writer needs to define the following:
///
/// 1) An OutputArrayType, which must be an array-like data structure.
/// 2) An OutputObjectType, which must contain key-value pairs.
/// 3) An OutputVarType, which must be able to represent either
/// OutputArrayType, OutputObjectType or a basic type (bool, integral,
/// floating point, std::string).

/// Appends var to the end of arr, thus mutating it.
{ w.add(var, &arr) } -> std::same_as<void>;

/// Returns an empty var (usually the NULL type).
{ w.empty_var() } -> std::same_as<typename W::OutputVarType>;

/// Generates a var from a basic type (std::string, bool, floating point or
/// integral).
{ w.from_basic_type(t) } -> std::same_as<typename W::OutputVarType>;

/// Generates a new, empty array.
{ w.new_array() } -> std::same_as<typename W::OutputArrayType>;

/// Generates a new, empty object.
{ w.new_object() } -> std::same_as<typename W::OutputObjectType>;

/// Determines whether the var is empty (the NULL type).
{ w.is_empty(var) } -> std::same_as<bool>;

/// Adds a new field to obj, thus mutating it.
{ w.set_field(name, var, &obj) } -> std::same_as<void>;
{ w.end_array(&arr) } -> std::same_as<void>;
};

} // namespace parsing
Expand Down
27 changes: 18 additions & 9 deletions include/rfl/parsing/MapParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "rfl/Result.hpp"
#include "rfl/always_false.hpp"
#include "rfl/parsing/Parent.hpp"
#include "rfl/parsing/Parser_base.hpp"

namespace rfl {
Expand All @@ -25,34 +26,42 @@ struct MapParser {
using KeyType = std::decay_t<typename MapType::value_type::first_type>;
using ValueType = std::decay_t<typename MapType::value_type::second_type>;

using ParentType = Parent<W>;

static Result<MapType> read(const R& _r, const InputVarType& _var) noexcept {
const auto to_map = [&](const auto& _obj) { return make_map(_r, _obj); };
return _r.to_object(_var).and_then(to_map);
}

static OutputVarType write(const W& _w, const MapType& _m) noexcept {
auto obj = _w.new_object();
template <class P>
static void write(const W& _w, const MapType& _m, const P& _parent) noexcept {
auto obj = ParentType::add_object(_w, _m.size(), _parent);
for (const auto& [k, v] : _m) {
auto parsed_val = Parser<R, W, std::decay_t<ValueType>>::write(_w, v);
if constexpr (internal::has_reflection_type_v<KeyType>) {
using ReflT = typename KeyType::ReflectionType;

if constexpr (std::is_integral_v<ReflT> ||
std::is_floating_point_v<ReflT>) {
_w.set_field(std::to_string(k.reflection()), std::move(parsed_val),
&obj);
const auto new_parent =
typename ParentType::Object{std::to_string(k.reflection()), &obj};
Parser<R, W, std::decay_t<ValueType>>::write(_w, v, new_parent);
} else {
_w.set_field(k.reflection(), std::move(parsed_val), &obj);
const auto new_parent =
typename ParentType::Object{k.reflection(), &obj};
Parser<R, W, std::decay_t<ValueType>>::write(_w, v, new_parent);
}

} else if constexpr (std::is_integral_v<KeyType> ||
std::is_floating_point_v<KeyType>) {
_w.set_field(std::to_string(k), std::move(parsed_val), &obj);
const auto new_parent =
typename ParentType::Object{std::to_string(k), &obj};
Parser<R, W, std::decay_t<ValueType>>::write(_w, v, new_parent);
} else {
_w.set_field(k, std::move(parsed_val), &obj);
const auto new_parent = typename ParentType::Object{k, &obj};
Parser<R, W, std::decay_t<ValueType>>::write(_w, v, new_parent);
}
}
return obj;
_w.end_object(&obj);
}

private:
Expand Down
23 changes: 13 additions & 10 deletions include/rfl/parsing/NamedTupleParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "rfl/always_false.hpp"
#include "rfl/internal/is_basic_type.hpp"
#include "rfl/internal/strings/replace_all.hpp"
#include "rfl/parsing/Parent.hpp"
#include "rfl/parsing/is_required.hpp"

namespace rfl {
Expand All @@ -24,8 +25,9 @@ struct NamedTupleParser {
using OutputObjectType = typename W::OutputObjectType;
using OutputVarType = typename W::OutputVarType;

using ParentType = Parent<W>;

public:
/// Generates a NamedTuple from a JSON Object.
static Result<NamedTuple<FieldTypes...>> read(
const R& _r, const InputVarType& _var) noexcept {
const auto& indices = field_indices();
Expand All @@ -42,12 +44,12 @@ struct NamedTupleParser {
return _r.to_object(_var).transform(to_fields_array).and_then(build);
}

/// Transforms a NamedTuple into a JSON object.
static OutputVarType write(const W& _w,
const NamedTuple<FieldTypes...>& _tup) noexcept {
auto obj = _w.new_object();
template <class P>
static void write(const W& _w, const NamedTuple<FieldTypes...>& _tup,
const P& _parent) noexcept {
auto obj = ParentType::add_object(_w, _tup.size(), _parent);
build_object_recursively(_w, _tup, &obj);
return OutputVarType(obj);
_w.end_object(&obj);
}

private:
Expand Down Expand Up @@ -157,14 +159,15 @@ struct NamedTupleParser {
using FieldType =
typename std::tuple_element<_i, std::tuple<FieldTypes...>>::type;
using ValueType = std::decay_t<typename FieldType::Type>;
auto value = Parser<R, W, ValueType>::write(_w, rfl::get<_i>(_tup));
const auto& value = rfl::get<_i>(_tup);
const auto name = FieldType::name_.str();
const auto new_parent = typename ParentType::Object{name, _ptr};
if constexpr (!is_required<ValueType, _ignore_empty_containers>()) {
if (!_w.is_empty(value)) {
_w.set_field(name, value, _ptr);
if (value) {
Parser<R, W, ValueType>::write(_w, value, new_parent);
}
} else {
_w.set_field(name, value, _ptr);
Parser<R, W, ValueType>::write(_w, value, new_parent);
}
return build_object_recursively<_i + 1>(_w, _tup, _ptr);
}
Expand Down
Loading

0 comments on commit b811434

Please sign in to comment.