Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aggregate to yaml and refactor yaml_writer #325

Merged
merged 2 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions iguana/yaml_util.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#pragma once

#include "detail/pb_type.hpp"
#include "ylt/reflection/template_string.hpp"
#include "util.hpp"
#include "ylt/reflection/member_names.hpp"

namespace iguana {

Expand Down
192 changes: 86 additions & 106 deletions iguana/yaml_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,86 +4,59 @@
#include "yaml_util.hpp"

namespace iguana {

template <bool Is_writing_escape = false, typename Stream, typename T,
std::enable_if_t<yaml_not_support_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces = 0) {
throw std::bad_function_call();
}

template <bool Is_writing_escape = false, typename Stream, typename T,
std::enable_if_t<refletable_v<T>, int> = 0>
IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces = 0);

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<refletable_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) {
ss.push_back('\n');
to_yaml<Is_writing_escape>(std::forward<T>(t), ss, min_spaces);
}

template <bool Is_writing_escape, bool appendLf = true, typename Stream,
typename T, std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) {
std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, char c = '\n') {
if constexpr (Is_writing_escape) {
ss.push_back('"');
write_string_with_escape(t.data(), t.size(), ss);
ss.push_back('"');
}
else {
} else {
ss.append(t.data(), t.size());
}
if constexpr (appendLf)
ss.push_back('\n');
ss.push_back(c);
}

template <bool Is_writing_escape, bool appendLf = true, typename Stream,
typename T, std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, char c = '\n') {
char temp[65];
auto p = detail::to_chars(temp, value);
ss.append(temp, p - temp);
if constexpr (appendLf)
ss.push_back('\n');
ss.push_back(c);
}

template <bool Is_writing_escape, bool appendLf = true, typename Stream,
typename T, std::enable_if_t<is_pb_type_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
render_yaml_value<Is_writing_escape>(ss, value.val, min_spaces);
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<is_pb_type_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, char c = '\n') {
render_yaml_value<Is_writing_escape>(ss, value.val, c);
}

template <bool Is_writing_escape, bool appendLf = true, typename Stream>
IGUANA_INLINE void render_yaml_value(Stream &ss, char value,
size_t min_spaces) {
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void render_yaml_value(Stream &ss, char value, char c = '\n') {
ss.push_back(value);
if constexpr (appendLf)
ss.push_back('\n');
ss.push_back(c);
}

template <bool Is_writing_escape, bool appendLf = true, typename Stream>
IGUANA_INLINE void render_yaml_value(Stream &ss, bool value,
size_t min_spaces) {
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void render_yaml_value(Stream &ss, bool value, char c = '\n') {
ss.append(value ? "true" : "false");
if constexpr (appendLf)
ss.push_back('\n');
ss.push_back(c);
}

template <bool Is_writing_escape, bool appendLf = true, typename Stream,
typename T, std::enable_if_t<enum_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<enum_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T value, char c = '\n') {
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
render_yaml_value<Is_writing_escape>(
ss, static_cast<std::underlying_type_t<T>>(value), min_spaces);
}
else {
ss, static_cast<std::underlying_type_t<T>>(value), c);
} else {
auto it = enum_to_str.find(value);
if (it != enum_to_str.end())
IGUANA_LIKELY {
if (it != enum_to_str.end()) IGUANA_LIKELY {
auto str = it->second;
render_yaml_value<Is_writing_escape>(
ss, std::string_view(str.data(), str.size()), min_spaces);
ss, std::string_view(str.data(), str.size()), c);
}
else {
throw std::runtime_error(
Expand All @@ -93,114 +66,121 @@ IGUANA_INLINE void render_yaml_value(Stream &ss, T value, size_t min_spaces) {
}
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<optional_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
size_t min_spaces);

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
size_t min_spaces);
template <bool Is_writing_escape = false, typename Stream, typename T>
IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces = 0);

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t,
size_t min_spaces) {
IGUANA_INLINE void render_yaml_wrapper(Stream &ss, const T &t,
size_t min_spaces) {
ss.push_back('\n');
for (const auto &v : t) {
ss.append(min_spaces, ' ');
ss.append("- ");
render_yaml_value<Is_writing_escape>(ss, v, min_spaces + 1);
to_yaml<Is_writing_escape>(v, ss, min_spaces + 1);
}
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, T &&t, size_t min_spaces) {
IGUANA_INLINE void render_yaml_wrapper(Stream &ss, T &&t, size_t min_spaces) {
ss.push_back('\n');
for_each(std::forward<T>(t),
[&ss, min_spaces](auto &v, auto i) IGUANA__INLINE_LAMBDA {
ss.append(min_spaces, ' ');
ss.append("- ");
render_yaml_value<Is_writing_escape>(ss, v, min_spaces + 1);
to_yaml<Is_writing_escape>(v, ss, min_spaces + 1);
});
}

template <bool Is_writing_escape, typename Stream, typename T, typename U>
IGUANA_INLINE void render_key_value(T &&t, Stream &s, size_t min_spaces,
U &&name) {
s.append(min_spaces, ' ');
render_yaml_value<false>(s, name, ':'); // key must be plaint type
s.append(" ");
to_yaml<Is_writing_escape>(t, s, min_spaces + 1);
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &t,
size_t min_spaces) {
IGUANA_INLINE void render_yaml_wrapper(Stream &ss, const T &t,
size_t min_spaces) {
ss.push_back('\n');
for (const auto &[k, v] : t) {
ss.append(min_spaces, ' ');
render_yaml_value<false, false>(ss, k, 0); // key must be plaint type
ss.append(": ");
render_yaml_value<Is_writing_escape>(ss, v, min_spaces + 1);
render_key_value<Is_writing_escape>(v, ss, min_spaces, k);
}
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<optional_v<T>, int>>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
size_t min_spaces) {
std::enable_if_t<optional_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_wrapper(Stream &ss, const T &val,
size_t min_spaces) {
if (!val) {
ss.append("null");
ss.push_back('\n');
}
else {
render_yaml_value<Is_writing_escape>(ss, *val, min_spaces);
} else {
to_yaml<Is_writing_escape>(*val, ss, min_spaces);
}
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int>>
IGUANA_INLINE void render_yaml_value(Stream &ss, const T &val,
size_t min_spaces) {
std::enable_if_t<smart_ptr_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_wrapper(Stream &ss, const T &val,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个名字我觉得不要改的好,用wrapper不知道是啥意思。

size_t min_spaces) {
if (!val) {
ss.push_back('\n');
}
else {
render_yaml_value<Is_writing_escape>(ss, *val, min_spaces);
} else {
to_yaml<Is_writing_escape>(*val, ss, min_spaces);
}
}

constexpr auto write_yaml_key = [](auto &s, auto i,
auto &t) IGUANA__INLINE_LAMBDA {
constexpr auto name = get_name<decltype(t), decltype(i)::value>();
s.append(name.data(), name.size());
};
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<std::is_aggregate_v<std::remove_cvref_t<T>> &&
non_refletable_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_struct(Stream &s, T &&t, size_t min_spaces) {
s.push_back('\n');
using U = std::remove_cvref_t<T>;
constexpr size_t Count = ylt::reflection::members_count_v<U>;
auto tp = ylt::reflection::object_to_tuple(t);
static constexpr auto arr = ylt::reflection::get_member_names<U>();

[&]<size_t... Is>(std::index_sequence<Is...>) mutable {
(render_key_value<Is_writing_escape>(std::get<Is>(tp), s, min_spaces,
arr[Is]),
...);
}(std::make_index_sequence<Count>{});
}

template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<refletable_v<T>, int>>
IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces) {
std::enable_if_t<refletable_v<T>, int> = 0>
IGUANA_INLINE void render_yaml_struct(Stream &s, T &&t, size_t min_spaces) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里还是用render_yaml_value比较好,统一成这个名字。
struct语义是不明确的,tuple 也算是struct的。

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

除了函数名之外,我觉得其它的地方没啥问题。

s.push_back('\n');
for_each(std::forward<T>(t),
[&t, &s, min_spaces](const auto &v, auto i) IGUANA__INLINE_LAMBDA {
using M = decltype(iguana_reflect_type(std::forward<T>(t)));
constexpr auto Idx = decltype(i)::value;
constexpr auto Count = M::value();
static_assert(Idx < Count);
s.append(min_spaces, ' ');
write_yaml_key(s, i, t);
s.append(": ");
if constexpr (!is_reflection<std::decay_t<decltype(v)>>::value) {
render_yaml_value<Is_writing_escape>(s, t.*v, min_spaces + 1);
}
else {
s.push_back('\n');
to_yaml<Is_writing_escape>(t.*v, s, min_spaces + 1);
}
[[maybe_unused]] constexpr std::string_view tag_name =
std::string_view(get_name<std::decay_t<T>, Idx>().data(),
get_name<std::decay_t<T>, Idx>().size());
render_key_value<Is_writing_escape>(t.*v, s, min_spaces, tag_name);
});
}

template <bool Is_writing_escape = false, typename Stream, typename T,
std::enable_if_t<non_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_yaml(T &&t, Stream &s) {
template <bool Is_writing_escape = false, typename Stream, typename T>
IGUANA_INLINE void to_yaml(T &&t, Stream &s, size_t min_spaces) {
if constexpr (tuple_v<T> || map_container_v<T> || sequence_container_v<T> ||
optional_v<T> || smart_ptr_v<T>)
render_yaml_value<Is_writing_escape>(s, std::forward<T>(t), 0);
else
render_yaml_wrapper<Is_writing_escape>(s, std::forward<T>(t), min_spaces);
else if constexpr (refletable_v<T> ||
std::is_aggregate_v<std::remove_cvref_t<T>>)
render_yaml_struct<Is_writing_escape>(s, std::forward<T>(t), min_spaces);
else if constexpr (yaml_not_support_v<T>)
static_assert(!sizeof(T), "don't suppport this type");
else
render_yaml_value<Is_writing_escape>(s, t);
}

template <typename T>
Expand Down
17 changes: 17 additions & 0 deletions test/test_yaml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,23 @@ price: 20 # this is price
validator(*op_p2);
}

struct my_optional_t {
int a;
std::optional<int> b;
std::optional<std::string> c;
bool d;
char e;
};

TEST_CASE("test aggregate reflect") {
my_optional_t op{1, 2, {}, 0, 'o'};

static_assert(std::is_aggregate_v<my_optional_t>, "err");
std::string ss;
iguana::to_yaml(op, ss);
std::cout << ss << "\n";
}

TEST_CASE("test optional") {
std::string str = R"()";
std::optional<int> opt;
Expand Down
Loading