Skip to content

Commit

Permalink
[struct_json] support user-defined serialize/deserialize (#619)
Browse files Browse the repository at this point in the history
  • Loading branch information
poor-circle authored Mar 5, 2024
1 parent 14d1323 commit 244c92a
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 103 deletions.
120 changes: 66 additions & 54 deletions include/ylt/thirdparty/iguana/json_reader.hpp

Large diffs are not rendered by default.

76 changes: 39 additions & 37 deletions include/ylt/thirdparty/iguana/json_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,33 @@ namespace iguana {
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json(T &&t, Stream &s);

namespace detail {
template <bool Is_writing_escape = true, typename Stream, typename T>
IGUANA_INLINE void render_json_value(Stream &ss, std::optional<T> &val);
IGUANA_INLINE void to_json_impl(Stream &ss, std::optional<T> &val);

template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<fixed_array_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &ss, const T &t);
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t);

template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &ss, const T &v);
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);

template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &ss, const T &v);
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);

template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &ss, const T &o);
IGUANA_INLINE void to_json_impl(Stream &ss, const T &o);

template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &s, T &&t);
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);

template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<variant_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &s, T &&t);
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);

template <typename Stream, typename InputIt, typename T, typename F>
IGUANA_INLINE void join(Stream &ss, InputIt first, InputIt last, const T &delim,
Expand All @@ -52,39 +52,39 @@ IGUANA_INLINE void join(Stream &ss, InputIt first, InputIt last, const T &delim,
}

template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void render_json_value(Stream &ss, std::nullptr_t) {
IGUANA_INLINE void to_json_impl(Stream &ss, std::nullptr_t) {
ss.append("null");
}

template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void render_json_value(Stream &ss, bool b) {
IGUANA_INLINE void to_json_impl(Stream &ss, bool b) {
ss.append(b ? "true" : "false");
};

template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void render_json_value(Stream &ss, char value) {
IGUANA_INLINE void to_json_impl(Stream &ss, char value) {
ss.append("\"");
ss.push_back(value);
ss.append("\"");
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &ss, T value) {
IGUANA_INLINE void to_json_impl(Stream &ss, T value) {
char temp[65];
auto p = detail::to_chars(temp, value);
ss.append(temp, p - temp);
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<numeric_str_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &ss, T v) {
IGUANA_INLINE void to_json_impl(Stream &ss, T v) {
ss.append(v.value().data(), v.value().size());
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &ss, T &&t) {
IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) {
ss.push_back('"');
if constexpr (Is_writing_escape) {
write_string_with_escape(t.data(), t.size(), ss);
Expand All @@ -99,36 +99,36 @@ template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void render_key(Stream &ss, T &t) {
ss.push_back('"');
render_json_value<Is_writing_escape>(ss, t);
to_json_impl<Is_writing_escape>(ss, t);
ss.push_back('"');
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void render_key(Stream &ss, T &&t) {
render_json_value<Is_writing_escape>(ss, std::forward<T>(t));
to_json_impl<Is_writing_escape>(ss, std::forward<T>(t));
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<refletable_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &ss, T &&t) {
IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) {
to_json(std::forward<T>(t), ss);
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<enum_v<T>, int> = 0>
IGUANA_INLINE void render_json_value(Stream &ss, T val) {
IGUANA_INLINE void to_json_impl(Stream &ss, T val) {
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
render_json_value<Is_writing_escape>(
to_json_impl<Is_writing_escape>(
ss, static_cast<std::underlying_type_t<T>>(val));
}
else {
auto it = enum_to_str.find(val);
if (it != enum_to_str.end())
IGUANA_LIKELY {
auto str = it->second;
render_json_value<Is_writing_escape>(
to_json_impl<Is_writing_escape>(
ss, std::string_view(str.data(), str.size()));
}
else {
Expand All @@ -140,12 +140,12 @@ IGUANA_INLINE void render_json_value(Stream &ss, T val) {
}

template <bool Is_writing_escape, typename Stream, typename T>
IGUANA_INLINE void render_json_value(Stream &ss, std::optional<T> &val) {
IGUANA_INLINE void to_json_impl(Stream &ss, std::optional<T> &val) {
if (!val) {
ss.append("null");
}
else {
render_json_value<Is_writing_escape>(ss, *val);
to_json_impl<Is_writing_escape>(ss, *val);
}
}

Expand All @@ -154,14 +154,14 @@ IGUANA_INLINE void render_array(Stream &ss, const T &v) {
ss.push_back('[');
join(ss, std::begin(v), std::end(v), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
render_json_value<Is_writing_escape>(ss, jsv);
to_json_impl<Is_writing_escape>(ss, jsv);
});
ss.push_back(']');
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<fixed_array_v<T>, int>>
IGUANA_INLINE void render_json_value(Stream &ss, const T &t) {
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t) {
if constexpr (std::is_same_v<char, std::remove_reference_t<
decltype(std::declval<T>()[0])>>) {
constexpr size_t n = sizeof(T) / sizeof(decltype(std::declval<T>()[0]));
Expand All @@ -184,24 +184,24 @@ IGUANA_INLINE void render_json_value(Stream &ss, const T &t) {

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int>>
IGUANA_INLINE void render_json_value(Stream &ss, const T &o) {
IGUANA_INLINE void to_json_impl(Stream &ss, const T &o) {
ss.push_back('{');
join(ss, o.cbegin(), o.cend(), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
render_key<Is_writing_escape>(ss, jsv.first);
ss.push_back(':');
render_json_value<Is_writing_escape>(ss, jsv.second);
to_json_impl<Is_writing_escape>(ss, jsv.second);
});
ss.push_back('}');
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int>>
IGUANA_INLINE void render_json_value(Stream &ss, const T &v) {
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
ss.push_back('[');
join(ss, v.cbegin(), v.cend(), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
render_json_value<Is_writing_escape>(ss, jsv);
to_json_impl<Is_writing_escape>(ss, jsv);
});
ss.push_back(']');
}
Expand All @@ -217,9 +217,9 @@ constexpr auto write_json_key = [](auto &s, auto i,

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int>>
IGUANA_INLINE void render_json_value(Stream &ss, const T &v) {
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
if (v) {
render_json_value<Is_writing_escape>(ss, *v);
to_json_impl<Is_writing_escape>(ss, *v);
}
else {
ss.append("null");
Expand All @@ -228,13 +228,13 @@ IGUANA_INLINE void render_json_value(Stream &ss, const T &v) {

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int>>
IGUANA_INLINE void render_json_value(Stream &s, T &&t) {
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
using U = typename std::decay_t<T>;
s.push_back('[');
constexpr size_t size = std::tuple_size_v<U>;
for_each(std::forward<T>(t),
[&s, size](auto &v, auto i) IGUANA__INLINE_LAMBDA {
render_json_value<Is_writing_escape>(s, v);
to_json_impl<Is_writing_escape>(s, v);

if (i != size - 1)
IGUANA_LIKELY { s.push_back(','); }
Expand All @@ -244,17 +244,18 @@ IGUANA_INLINE void render_json_value(Stream &s, T &&t) {

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<variant_v<T>, int>>
IGUANA_INLINE void render_json_value(Stream &s, T &&t) {
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
std::visit(
[&s](auto value) {
render_json_value<Is_writing_escape>(s, value);
to_json_impl<Is_writing_escape>(s, value);
},
t);
}

} // namespace detail
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<refletable_v<T>, int>>
IGUANA_INLINE void to_json(T &&t, Stream &s) {
using namespace detail;
s.push_back('{');
for_each(std::forward<T>(t),
[&t, &s](const auto &v, auto i) IGUANA__INLINE_LAMBDA {
Expand All @@ -265,7 +266,7 @@ IGUANA_INLINE void to_json(T &&t, Stream &s) {

write_json_key(s, i, t);
s.push_back(':');
render_json_value<Is_writing_escape>(s, t.*v);
to_json_impl<Is_writing_escape>(s, t.*v);
if (Idx < Count - 1)
IGUANA_LIKELY { s.push_back(','); }
});
Expand All @@ -275,7 +276,8 @@ IGUANA_INLINE void to_json(T &&t, Stream &s) {
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<non_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json(T &&t, Stream &s) {
render_json_value<Is_writing_escape>(s, t);
using namespace detail;
to_json_impl<Is_writing_escape>(s, t);
}

} // namespace iguana
Expand Down
1 change: 0 additions & 1 deletion include/ylt/thirdparty/iguana/reflection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include <variant>
#include <vector>

#include "detail/itoa.hpp"
#include "detail/string_stream.hpp"
#include "detail/traits.hpp"
#include "frozen/string.h"
Expand Down
9 changes: 3 additions & 6 deletions include/ylt/thirdparty/iguana/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,12 @@ struct is_variant<std::variant<T...>> : std::true_type {};
template <typename T>
constexpr inline bool variant_v = is_variant<std::remove_cvref_t<T>>::value;

template <class T>
constexpr inline bool non_refletable_v =
container_v<T> || c_array_v<T> || tuple_v<T> || optional_v<T> ||
smart_ptr_v<T> || std::is_fundamental_v<std::remove_cvref_t<T>> ||
variant_v<T>;

template <typename T>
constexpr inline bool refletable_v = is_reflection_v<std::remove_cvref_t<T>>;

template <class T>
constexpr inline bool non_refletable_v = !refletable_v<T>;

template <typename T>
constexpr inline bool plain_v =
string_container_v<T> || num_v<T> || char_v<T> || bool_v<T> || enum_v<T>;
Expand Down
5 changes: 1 addition & 4 deletions src/struct_json/examples/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
cc_binary(
name = "struct_json_example",
srcs =
[
"main.cpp",
],
srcs = glob(["*.cpp"]),
copts = [
"-std=c++20",
],
Expand Down
2 changes: 1 addition & 1 deletion src/struct_json/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ else()
# include_directories(include)
# include_directories(include/ylt/thirdparty)
endif()
add_executable(struct_json_example main.cpp)
add_executable(struct_json_example main.cpp user_defined_struct.cpp)
5 changes: 5 additions & 0 deletions src/struct_json/examples/main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#include <cassert>
#include <iguana/json_reader.hpp>
#include <iguana/json_writer.hpp>
#include <iostream>

#include "ylt/struct_json/json_reader.h"
#include "ylt/struct_json/json_writer.h"

void test_user_defined_struct();

struct person {
std::string name;
int age;
Expand Down Expand Up @@ -90,4 +94,5 @@ int main() {
test_inner_object();
use_smart_pointer();
test_escape_serialize();
test_user_defined_struct();
}
59 changes: 59 additions & 0 deletions src/struct_json/examples/user_defined_struct.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <ylt/struct_json/json_reader.h>
#include <ylt/struct_json/json_writer.h>

#include <string>

#include "iguana/json_writer.hpp"

namespace my_space {
struct my_struct {
int x, y, z;
bool operator==(const my_struct& o) const {
return x == o.x && y == o.y && z == o.z;
}
};

template <bool Is_writing_escape, typename Stream>
inline void to_json_impl(Stream& s, const my_struct& t) {
struct_json::to_json<Is_writing_escape>(*(int(*)[3]) & t, s);
}

template <typename It>
IGUANA_INLINE void from_json_impl(my_struct& value, It&& it, It&& end) {
struct_json::from_json(*(int(*)[3]) & value, it, end);
}

} // namespace my_space

struct nest {
std::string name;
my_space::my_struct value;
bool operator==(const nest& o) const {
return name == o.name && value == o.value;
}
};

REFLECTION(nest, name, value);

void example1() {
my_space::my_struct v{1, 2, 3}, v2;
std::string s;
struct_json::to_json(v, s);
std::cout << s << std::endl;
struct_json::from_json(v2, s);
assert(v == v2);
};

void example2() {
nest v{"Hi", {1, 2, 3}}, v2;
std::string s;
struct_json::to_json(v, s);
std::cout << s << std::endl;
struct_json::from_json(v2, s);
assert(v == v2);
};

void test_user_defined_struct() {
example1();
example2();
}

0 comments on commit 244c92a

Please sign in to comment.