Skip to content

Commit

Permalink
Add json traits. Refactor rapid/boost dom parsing (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
sabudilovskiy authored Oct 17, 2024
1 parent d913634 commit d80fa83
Show file tree
Hide file tree
Showing 15 changed files with 550 additions and 340 deletions.
1 change: 1 addition & 0 deletions gbench/files.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "types.hpp"
#include <tgbm/api/types_all.hpp>
#include <tgbm/tools/json_tools/all.hpp>
#include <tgbm/tools/json_tools/parse_dom/all.hpp>

template <typename T>
void execute_handler_rapid(std::string_view json) {
Expand Down
1 change: 1 addition & 0 deletions include/tgbm/tools/json_tools/all.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <tgbm/tools/json_tools/boost_parse_handler.hpp>
#include <tgbm/tools/json_tools/generator_parser/all.hpp>
#include <tgbm/tools/json_tools/handler_parser/all.hpp>
#include <tgbm/tools/json_tools/parse_dom/all.hpp>
#include <tgbm/tools/json_tools/rapid_parse_handler.hpp>
#include <tgbm/tools/json_tools/rapid_parse_dom.hpp>
#include <tgbm/tools/json_tools/exceptions.hpp>
230 changes: 77 additions & 153 deletions include/tgbm/tools/json_tools/boost_parse_dom.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#pragma once

#include <boost/json.hpp>
#include "boost/pfr/core_name.hpp"
#include "boost/pfr/tuple_size.hpp"
#include <tgbm/tools/box.hpp>
#include <tgbm/tools/pfr_extension.hpp>
#include <tgbm/tools/traits.hpp>
Expand All @@ -12,199 +10,125 @@
#include <boost/container/string.hpp>
#include <tgbm/tools/api_utils.hpp>
#include <tgbm/tools/json_tools/exceptions.hpp>
#include <tgbm/tools/json_tools/json_traits.hpp>
#include <tgbm/tools/json_tools/parse_dom/basic.hpp>

namespace boost::json {
namespace tgbm::json {
struct boost_json_traits {
using type = ::boost::json::value;

template <tgbm::aggregate T>
T tag_invoke(const value_to_tag<T>&, const value& jv) {
if (!jv.is_object()) {
TGBM_JSON_PARSE_ERROR;
static bool is_bool(const type& json) {
return json.is_bool();
}
auto& object = jv.as_object();

auto construct_field = [&]<std::size_t I>() {
using Field = pfr::tuple_element_t<I, T>;
auto it = object.find(pfr_extension::get_name<I, T>());
boost::json::value null(nullptr);
return boost::json::value_to<Field>(it != object.end() ? *it : null);
};

return [&]<std::size_t... I>(std::index_sequence<I...>) {
return T{construct_field.template operator()<I>()...};
}(std::make_index_sequence<pfr_extension::tuple_size_v<T>>{});
}

namespace details {

template <std::size_t I, typename T>
auto construct_field(const boost::json::object& object) {
using Field = pfr::tuple_element_t<I, T>;
constexpr std::string_view name = pfr_extension::get_name<I, T>();
auto it = object.find(name);
if (it == object.end()) {
if constexpr (T::is_optional_field(name)) {
return Field{};
} else {
TGBM_JSON_PARSE_ERROR;
}
static bool is_integer(const type& json) {
return json.is_int64();
}
return boost::json::value_to<Field>(it->value());
}

template <std::size_t I, typename T>
auto construct_req_field(const boost::json::object& object) {
using Field = pfr::tuple_element_t<I, T>;
constexpr std::string_view name = pfr_extension::element_name_v<I, T>;
auto it = object.find(name);
if (it == object.end()) {
TGBM_JSON_PARSE_ERROR;
static bool is_uinteger(const type& json) {
return json.is_uint64();
}
return boost::json::value_to<Field>(it->value());
}

} // namespace details

template <tgbm::common_api_type T>
T tag_invoke(const value_to_tag<T>&, const value& jv) {
if (!jv.is_object()) {
TGBM_JSON_PARSE_ERROR;
static bool is_floating(const type& json) {
return json.is_double();
}
auto& object = jv.as_object();

return [&]<std::size_t... I>(std::index_sequence<I...>) {
return T(details::construct_field<I, T>(object)...);
}(std::make_index_sequence<pfr_extension::tuple_size_v<T>>{});
}

inline tgbm::api::Integer tag_invoke(const value_to_tag<tgbm::api::Integer>&, const value& jv) {
tgbm::api::Integer result;
if (!jv.is_int64()) {
TGBM_JSON_PARSE_ERROR;
static bool is_string(const type& json) {
return json.is_string();
}
auto data = jv.as_int64();
if (data > result.max) {
TGBM_JSON_PARSE_ERROR;

static bool is_object(const type& json) {
return json.is_object();
}
return data;
}

template <tgbm::discriminated_api_type T>
T tag_invoke(const value_to_tag<T>&, const value& jv) {
constexpr std::string_view discriminator = T::discriminator;
if (!jv.is_object()) {
TGBM_JSON_PARSE_ERROR;
static bool is_array(const type& json) {
return json.is_array();
}
auto& object = jv.as_object();
auto d_value = object.find(discriminator);
if (d_value == object.end()) [[unlikely]] {
TGBM_JSON_PARSE_ERROR;

static bool is_null(const type& json) {
return json.is_null();
}
if (!d_value->value().is_string()) [[unlikely]] {
TGBM_JSON_PARSE_ERROR;

// Получение значений
static bool get_bool(const type& json) {
return json.as_bool();
}
return T::discriminate(d_value->value().as_string(), [&]<typename U>() -> T {
if constexpr (std::same_as<U, void>) {
TGBM_JSON_PARSE_ERROR;
} else {
return T{boost::json::value_to<U>(jv)};
}
});
}

template <typename T>
tgbm::box<T> tag_invoke(const value_to_tag<tgbm::box<T>>&, const value& jv) {
if (jv.is_null()) [[unlikely]] {
return nullptr;
static std::int64_t get_integer(const type& json) {
return json.as_int64();
}
return tgbm::box<T>{std::in_place, boost::json::value_to<T>(jv)};
}

template <typename T>
std::vector<T> tag_invoke(const value_to_tag<std::vector<T>>&, const value& jv) {
if (!jv.is_array()) {
TGBM_JSON_PARSE_ERROR;
static std::uint64_t get_uinteger(const type& json) {
return json.as_uint64();
}
auto ja = jv.as_array();
std::vector<T> result;
result.reserve(ja.size());
for (auto& elem : ja) {
result.emplace_back(boost::json::value_to<T>(elem));

static double get_floating(const type& json) {
return json.as_double();
}
return result;
}

template <typename T>
std::optional<T> tag_invoke(const value_to_tag<std::optional<T>>&, const value& jv) {
if (jv.is_null()) [[unlikely]] {
return std::nullopt;
static std::string_view get_string(const type& json) {
return std::string_view{json.as_string().data(), json.as_string().size()};
}
return std::optional<T>{boost::json::value_to<T>(jv)};
}

template <typename T>
tgbm::api::optional<T> tag_invoke(const value_to_tag<tgbm::api::optional<T>>&, const value& jv) {
if (jv.is_null()) [[unlikely]] {
return std::nullopt;
// Обработка ошибок
static void on_error() {
throw std::runtime_error("JSON Error");
}
return tgbm::api::optional<T>{boost::json::value_to<T>(jv)};
}

inline tgbm::api::Double tag_invoke(const value_to_tag<tgbm::api::Double>&, const value& jv) {
if (!jv.is_double()) {
TGBM_JSON_PARSE_ERROR;
// Поиск поля в объекте
static const type* find_field(const type& json, std::string_view key) {
assert(json.is_object());
auto& obj = json.as_object();
auto it = obj.find(key);
if (it != obj.end()) {
return &it->value();
}
return nullptr;
}
return jv.as_double();
}

inline tgbm::api::True tag_invoke(const value_to_tag<tgbm::api::True>&, const value& jv) {
if (!jv.is_bool() || !jv.as_bool()) {
TGBM_JSON_PARSE_ERROR;
// Доступ к элементу по индексу
static const type& element_by_index(const type& json, std::size_t index) {
assert(json.is_array());
auto& arr = json.as_array();
if (index >= arr.size()) {
on_error();
}
return arr[index];
}
return {};
}

inline tgbm::nothing_t tag_invoke(const value_to_tag<tgbm::nothing_t>&, const value& jv) {
if (!jv.is_object()) {
TGBM_JSON_PARSE_ERROR;
// Размер объекта или массива
static std::size_t size(const type& json) {
assert(json.is_object() || json.is_array());
if (json.is_object()) {
return json.as_object().size();
} else {
return json.as_array().size();
}
unreachable();
}
return {};
}

template <tgbm::oneof_field_api_type T>
T tag_invoke(const value_to_tag<T>&, const value& jv) {
if (!jv.is_object()) {
TGBM_JSON_PARSE_ERROR;
}
auto& jv_obj = jv.as_object();
constexpr std::size_t N = tgbm::oneof_field_utils::N<T>;

using Data = decltype(T::data);
auto construct_data_field = [&]() {
Data data;
for (auto&& [key, value] : jv_obj) {
if (!tgbm::oneof_field_utils::is_required<T>(key)) {
tgbm::oneof_field_utils::emplace_field<T, void>(
data, key, [&]<typename Field>(Field& field) { field = boost::json::value_to<Field>(value); },
[] { TGBM_JSON_PARSE_ERROR; }, [] {});
}
// Диапазон членов объекта
static auto& member_range(const type& json) {
if (!json.is_object()) {
on_error();
}
return data;
};

return tgbm::call_on_indexes<N>([&]<std::size_t... I> {
return T{details::construct_req_field<I, T>(jv_obj)..., construct_data_field()};
});
}
return json.as_object();
}
};

} // namespace boost::json
static_assert(json_traits<boost_json_traits, ::boost::json::value>);
} // namespace tgbm::json

namespace tgbm::json::boost {

template <typename T>
T parse_dom(std::string_view val) {
using namespace ::boost::json;
value j = parse(val);
return value_to<T>(j);
T out;
parse_dom::parser<T>::template parse<::boost::json::value, boost_json_traits>(j, out);
return out;
}

} // namespace tgbm::json::boost
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once
#include <tgbm/tools/json_tools/generator_parser/basic_parser.hpp>
#include "tgbm/tools/json_tools/generator_parser/ignore_parser.hpp"
#include "tgbm/tools/traits.hpp"

namespace tgbm::generator_parser {

Expand Down
54 changes: 54 additions & 0 deletions include/tgbm/tools/json_tools/json_traits.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma once

#include <concepts>
#include <cstdint>
#include <string_view>
#include <ranges>

namespace tgbm::json {
template <typename Traits, typename Json = typename Traits::type>
concept json_traits = requires(const Json& json, std::string_view str, std::size_t sz) {
requires std::same_as<typename Traits::type, Json>;

{ Traits::is_bool(json) } -> std::same_as<bool>;

{ Traits::is_integer(json) } -> std::same_as<bool>;

{ Traits::is_uinteger(json) } -> std::same_as<bool>;

{ Traits::is_floating(json) } -> std::same_as<bool>;

{ Traits::is_string(json) } -> std::same_as<bool>;

{ Traits::is_object(json) } -> std::same_as<bool>;

{ Traits::is_array(json) } -> std::same_as<bool>;

{ Traits::is_object(json) } -> std::same_as<bool>;

{ Traits::is_null(json) } -> std::same_as<bool>;

{ Traits::get_bool(json) } -> std::same_as<bool>;

{ Traits::on_error() };

{ Traits::get_integer(json) } -> std::same_as<std::int64_t>;

{ Traits::get_uinteger(json) } -> std::same_as<std::uint64_t>;

{ Traits::get_string(json) } -> std::convertible_to<std::string_view>;

{ Traits::get_floating(json) } -> std::same_as<double>;

{ Traits::find_field(json, str) };

{ Traits::element_by_index(json, sz) } -> std::same_as<const Json&>;

{ Traits::size(json) } -> std::same_as<std::size_t>;

{ Traits::member_range(json) } -> std::ranges::range;
};

template <typename T>
struct default_json_traits {};
} // namespace tgbm::json
9 changes: 9 additions & 0 deletions include/tgbm/tools/json_tools/parse_dom/all.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

#include <tgbm/tools/json_tools/parse_dom/array.hpp>
#include <tgbm/tools/json_tools/parse_dom/basic.hpp>
#include <tgbm/tools/json_tools/parse_dom/common_api.hpp>
#include <tgbm/tools/json_tools/parse_dom/discriminated_api.hpp>
#include <tgbm/tools/json_tools/parse_dom/fundamental.hpp>
#include <tgbm/tools/json_tools/parse_dom/oneof_field_api_type.hpp>
#include <tgbm/tools/json_tools/parse_dom/optional.hpp>
23 changes: 23 additions & 0 deletions include/tgbm/tools/json_tools/parse_dom/array.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once
#include <tgbm/tools/json_tools/parse_dom/basic.hpp>
#include <tgbm/tools/json_tools/json_traits.hpp>
#include <vector>

namespace tgbm::json::parse_dom {

template <typename T>
struct parser<std::vector<T>> {
template <typename Json, json::json_traits Traits>
static void parse(const Json& json, std::vector<T>& out) {
if (!Traits::is_array(json)) {
Traits::on_error();
}
std::size_t size = Traits::size(json);
out.reserve(size);
for (std::size_t i = 0; i < size; i++) {
auto& element_node = Traits::element_by_index(json, i);
parser<T>::template parse<Json, Traits>(element_node, out.emplace_back());
}
}
};
} // namespace tgbm::json::parse_dom
Loading

0 comments on commit d80fa83

Please sign in to comment.