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

Support struct yaml #330

Merged
merged 3 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 2 additions & 1 deletion cmake/module.cmake
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# module-list
set(yLT_ALL_PROJECTS "struct_pack;struct_pb;struct_json;struct_xml;coro_rpc;easylog;coro_http;coro_file")
set(yLT_ALL_PROJECTS "struct_pack;struct_pb;struct_json;struct_xml;struct_yaml;coro_rpc;easylog;coro_http;coro_file")
# separate
option(Build_ylt_struct_pack "Build struct_pack" ON)
option(Build_ylt_struct_pb "Build struct_pb" ON)
option(Build_ylt_struct_json "Build struct_json" ON)
option(Build_ylt_struct_xml "Build struct_xml" ON)
option(Build_ylt_struct_yaml "Build struct_yaml" ON)
option(Build_ylt_coro_rpc "Build coro_rpc" ON)
option(Build_ylt_easylog "Build easylog" ON)
option(Build_ylt_coro_http "Build coro_http" ON)
Expand Down
4 changes: 4 additions & 0 deletions cmake/yalantinglibsConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/struct_xmlTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/struct_xmlTargets.cmake")
message(STATUS "Imported target: yalantinglibs::struct_xml")
endif ()
if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/struct_yamlTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/struct_yamlTargets.cmake")
message(STATUS "Imported target: yalantinglibs::struct_yaml")
endif ()
if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/easylogTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/easylogTargets.cmake")
message(STATUS "Imported target: yalantinglibs::easylog")
Expand Down
15 changes: 15 additions & 0 deletions include/struct_yaml/yaml_reader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once
#include <iguana/yaml_reader.hpp>

namespace struct_yaml {

template <typename T, iguana::string_t View>
inline void from_yaml(T &value, const View &view) {
iguana::from_yaml(value, view);
}

template <typename T, iguana::string_t View>
inline void from_yaml(T &value, const View &view, std::error_code &ec) {
iguana::from_yaml(value, view, ec);
}
} // namespace struct_yaml
9 changes: 9 additions & 0 deletions include/struct_yaml/yaml_writer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once
#include <iguana/yaml_writer.hpp>

namespace struct_yaml {
template <typename Stream, typename T>
inline void to_yaml(T &&t, Stream &s, size_t min_spaces = 0) {
iguana::to_yaml(std::forward<T>(t), s, min_spaces);
}
} // namespace struct_yaml
3 changes: 1 addition & 2 deletions src/struct_xml/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ target_link_libraries(struct_xml_example PRIVATE struct_xml)

## manual import struct_xml
include_directories(${yaLanTingLibs_SOURCE_DIR}/thirdparty/iguana)
include_directories(${yaLanTingLibs_SOURCE_DIR}/thirdparty/iguana/thirdparty/rapidxml)
include_directories(${yaLanTingLibs_SOURCE_DIR}/thirdparty/iguana/thirdparty/mscharconv/include)
include_directories(${yaLanTingLibs_SOURCE_DIR}/thirdparty/iguana/thirdparty/rapidxml)
12 changes: 12 additions & 0 deletions src/struct_yaml/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_library(struct_yaml INTERFACE)
if (NOT TARGET yalantinglibs::struct_yaml)
add_library(yalantinglibs::struct_yaml ALIAS struct_yaml)
endif ()
target_include_directories(struct_yaml INTERFACE
$<BUILD_INTERFACE:${yaLanTingLibs_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/yalantinglibs>
)

target_link_libraries(struct_yaml INTERFACE $<BUILD_INTERFACE:iguana>)
ylt_install(struct_yaml)
install(DIRECTORY "${yaLanTingLibs_SOURCE_DIR}/include/struct_yaml" DESTINATION include/yalantinglibs)
8 changes: 8 additions & 0 deletions src/struct_yaml/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
add_executable(struct_yaml_example
main.cpp
)
target_link_libraries(struct_yaml_example PRIVATE struct_yaml)

## manual import struct_yaml
include_directories(${yaLanTingLibs_SOURCE_DIR}/thirdparty/iguana)
include_directories(${yaLanTingLibs_SOURCE_DIR}/thirdparty/iguana/thirdparty/rapidxml)
68 changes: 68 additions & 0 deletions src/struct_yaml/examples/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include <cassert>
#include <iostream>

#include "struct_yaml/yaml_reader.h"
#include "struct_yaml/yaml_writer.h"

struct address_t {
std::string_view street;
std::string_view city;
std::string_view state;
std::string_view country;
};
REFLECTION(address_t, street, city, state, country);
struct contact_t {
std::string_view type;
std::string_view value;
};
REFLECTION(contact_t, type, value);
struct person_t {
std::string_view name;
int age;
address_t address;
std::vector<contact_t> contacts;
};
REFLECTION(person_t, name, age, address, contacts);

std::ostream &operator<<(std::ostream &os, person_t p) {
os << "name: " << p.name << "\tage: " << p.age << std::endl;
os << p.address.street << "\n";
os << p.address.city << "\n";
os << p.address.state << "\n";
os << p.address.country << "\n";
os << p.contacts[0].type << " : " << p.contacts[0].value << "\n";
os << p.contacts[1].type << " : " << p.contacts[1].value << "\n";
os << p.contacts[2].type << " : " << p.contacts[2].value << "\n";
return os;
}

void person_example() {
std::string str = R"(
name: John Doe
age: 30
address:
street: 123 Main St
city: Anytown
state: Example State
country: Example Country
contacts:
- type: email
value: [email protected]
- type: phone
value: 123456789
- type: social
value: "johndoe"
)";
person_t p;
iguana::from_yaml(p, str);
std::cout << "========= deserialize person_t ========\n";
std::cout << p;
std::string ss;
iguana::to_yaml(p, ss);
std::cout << "========== serialize person_t =========\n";
std::cout << ss;
person_t p2;
iguana::from_yaml(p2, ss);
}

int main() { person_example(); }
53 changes: 53 additions & 0 deletions thirdparty/iguana/iguana/detail/charconv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once
#include <charconv>

#include "dragonbox_to_chars.h"
#include "fast_float.h"
#include "itoa.hpp"

namespace iguana {
template <typename T>
struct is_char_type
: std::disjunction<std::is_same<T, char>, std::is_same<T, unsigned char>,
std::is_same<T, signed char>, std::is_same<T, wchar_t>,
std::is_same<T, char16_t>, std::is_same<T, char32_t>> {};

namespace detail {
template <typename U>
std::pair<const char *, std::errc> from_chars(const char *first,
const char *last,
U &value) noexcept {
using T = std::decay_t<U>;
if constexpr (std::is_floating_point_v<T>) {
auto [p, ec] = fast_float::from_chars(first, last, value);
return {p, ec};
}
else {
auto [p, ec] = std::from_chars(first, last, value);
return {p, ec};
}
}

// not support uint8 for now
template <typename T>
char *to_chars(char *buffer, T value) noexcept {
using U = std::decay_t<T>;
if constexpr (std::is_floating_point_v<U>) {
return jkj::dragonbox::to_chars(value, buffer);
}
else if constexpr (std::is_signed_v<U> && (sizeof(U) >= 8)) {
return xtoa(value, buffer, 10, 1); // int64_t
}
else if constexpr (std::is_unsigned_v<U> && (sizeof(U) >= 8)) {
return xtoa(value, buffer, 10, 0); // uint64_t
}
else if constexpr (std::is_integral_v<U> && !is_char_type<U>::value) {
return itoa_fwd(value, buffer); // only support more than 2 bytes intergal
}
else {
static_assert(!sizeof(U), "only support arithmetic type except char type");
}
}

} // namespace detail
} // namespace iguana
134 changes: 6 additions & 128 deletions thirdparty/iguana/iguana/json_reader.hpp
Original file line number Diff line number Diff line change
@@ -1,133 +1,12 @@
#pragma once
#include <charconv>
#include <filesystem>
#include <forward_list>
#include <fstream>
#include <string_view>
#include <type_traits>

#include "detail/fast_float.h"
#include "detail/charconv.h"
#include "detail/utf.hpp"
#include "error_code.h"
#include "json_util.hpp"
#include "reflection.hpp"
#include "value.hpp"

namespace iguana {

template <class T>
concept char_t = std::same_as < std::decay_t<T>,
char > || std::same_as<std::decay_t<T>, char16_t> ||
std::same_as<std::decay_t<T>, char32_t> ||
std::same_as<std::decay_t<T>, wchar_t>;

template <class T>
concept bool_t = std::same_as < std::decay_t<T>,
bool > || std::same_as<std::decay_t<T>, std::vector<bool>::reference>;

template <class T>
concept int_t =
std::integral<std::decay_t<T>> && !char_t<std::decay_t<T>> && !bool_t<T>;

template <class T>
concept num_t = std::floating_point<std::decay_t<T>> || int_t<T>;

template <class T>
concept enum_type_t = std::is_enum_v<std::decay_t<T>>;

template <typename T>
constexpr inline bool is_basic_string_view = false;

template <typename T>
constexpr inline bool is_basic_string_view<std::basic_string_view<T>> = true;

template <typename T>
concept str_view_t = is_basic_string_view<std::remove_reference_t<T>>;

template <class T>
concept str_t =
std::convertible_to<std::decay_t<T>, std::string_view> && !str_view_t<T>;

template <typename Type>
constexpr inline bool is_std_vector_v = false;

template <typename... args>
constexpr inline bool is_std_vector_v<std::vector<args...>> = true;

template <typename Type>
concept vector_container = is_std_vector_v<std::remove_reference_t<Type>>;

template <typename Type>
concept optional = requires(Type optional) {
optional.value();
optional.has_value();
optional.operator*();
typename std::remove_cvref_t<Type>::value_type;
};

template <typename Type>
concept container = requires(Type container) {
typename std::remove_cvref_t<Type>::value_type;
container.size();
container.begin();
container.end();
};

template <typename Type>
concept map_container = container<Type> && requires(Type container) {
typename std::remove_cvref_t<Type>::mapped_type;
};

template <class T>
concept c_array = std::is_array_v<std::remove_cvref_t<T>> &&
std::extent_v<std::remove_cvref_t<T>> >
0;

template <typename Type>
concept array = requires(Type arr) {
arr.size();
std::tuple_size<std::remove_cvref_t<Type>>{};
};

template <typename Type>
concept fixed_array = c_array<Type> || array<Type>;

template <typename Type>
concept tuple = !array<Type> && requires(Type tuple) {
std::get<0>(tuple);
sizeof(std::tuple_size<std::remove_cvref_t<Type>>);
};

template <typename Type>
concept json_view = requires(Type container) {
container.size();
container.begin();
container.end();
};

template <typename T>
concept json_byte = std::is_same_v<char, T> ||
std::is_same_v<unsigned char, T> || std::is_same_v<std::byte, T>;

template <typename Type>
constexpr inline bool is_std_list_v = false;
template <typename... args>
constexpr inline bool is_std_list_v<std::list<args...>> = true;

template <typename Type>
constexpr inline bool is_std_deque_v = false;
template <typename... args>
constexpr inline bool is_std_deque_v<std::deque<args...>> = true;

template <typename Type>
concept sequence_container = is_std_list_v<std::remove_reference_t<Type>> ||
is_std_vector_v<std::remove_reference_t<Type>> ||
is_std_deque_v<std::remove_reference_t<Type>>;

template <class T>
concept non_refletable = container<T> || c_array<T> || tuple<T> ||
optional<T> || std::is_fundamental_v<T>;

template <refletable T, typename It>
void from_json(T &value, It &&it, It &&end);

Expand Down Expand Up @@ -187,7 +66,7 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) {
if (size == 0) [[unlikely]]
throw std::runtime_error("Failed to parse number");
const auto start = &*it;
auto [p, ec] = fast_float::from_chars(start, start + size, value);
auto [p, ec] = detail::from_chars(start, start + size, value);
if (ec != std::errc{}) [[unlikely]]
throw std::runtime_error("Failed to parse number");
it += (p - &*it);
Expand All @@ -202,7 +81,6 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) {
}
}
else {
double num;
char buffer[256];
size_t i{};
while (it != end && is_numeric(*it)) {
Expand All @@ -211,16 +89,16 @@ IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) {
buffer[i] = *it++;
++i;
}
auto [p, ec] = fast_float::from_chars(buffer, buffer + i, num);
auto [p, ec] = detail::from_chars(buffer, buffer + i, value);
if (ec != std::errc{}) [[unlikely]]
throw std::runtime_error("Failed to parse number");
value = static_cast<T>(num);
}
}

template <enum_type_t U, class It>
template <enum_t U, class It>
IGUANA_INLINE void parse_item(U &value, It &&it, It &&end) {
parse_item((int &)value, it, end);
using T = std::underlying_type_t<std::decay_t<U>>;
parse_item(reinterpret_cast<T &>(value), it, end);
}

template <str_t U, class It>
Expand Down
Loading