555 lines
23 KiB
C++
555 lines
23 KiB
C++
#pragma once
|
||
#include <atomic>
|
||
#include <functional>
|
||
#include <iostream>
|
||
#include <limits> // std::numeric_limits
|
||
#include <optional>
|
||
#include <string>
|
||
#include <string_view>
|
||
#include <vector>
|
||
#include "../Base/global_include.h"
|
||
#include "../system/export.h"
|
||
#define PSC_JSON_KEY_PARAMS(M, SEP) M(std::string_view, key)
|
||
#define PSC_JSON_PARSE_PARAMS(M, SEP) M(std::string_view, text)
|
||
#define PSC_JSON_FILE_PARAMS(M, SEP) M(std::string_view, file_name)
|
||
#undef max
|
||
#undef min
|
||
#define Json_GET(type, name) \
|
||
[[nodiscard]] type get_##name(std::string_view _key) const { \
|
||
const Psc::JSON* that = get(_key); \
|
||
if (!that) { \
|
||
std::cout << "get" #type " error json not found! key:" << _key \
|
||
<< " json: " << to_json_string() << std::endl; \
|
||
return false; \
|
||
} \
|
||
return that->name##_val(); \
|
||
}
|
||
#pragma once
|
||
#include <stdexcept>
|
||
#include <string>
|
||
#include <system_error>
|
||
#include <utility>
|
||
#include "../Base/global_include.h"
|
||
namespace Psc {
|
||
class json_assign_error final : public std::system_error {
|
||
public:
|
||
using std::system_error::system_error;
|
||
};
|
||
enum JsonType : uint8_t { String,
|
||
Object,
|
||
Array,
|
||
Null,
|
||
Number,
|
||
Bool };
|
||
template <typename T>
|
||
void assign_json(JsonType& rt, std::string& rv, T&& val);
|
||
template <typename T_Number>
|
||
bool to_number_ec(std::string_view val, T_Number& out,
|
||
std::error_code& ec) noexcept;
|
||
template <typename T_Number>
|
||
T_Number to_number(std::string_view val) {
|
||
return detail::triple_api_throwing<T_Number>(
|
||
[&val](T_Number& out, std::error_code& ec) {
|
||
return to_number_ec(val, out, ec);
|
||
},
|
||
"to_number failed");
|
||
}
|
||
template <typename T_Number>
|
||
expected<T_Number, std::error_code> try_to_number(std::string_view val) noexcept {
|
||
return detail::triple_api_expected<T_Number>(
|
||
[&val](T_Number& out, std::error_code& ec) {
|
||
return to_number_ec(val, out, ec);
|
||
});
|
||
}
|
||
template <typename T_Number>
|
||
bool to_number_ec(std::string_view val, T_Number& out,
|
||
std::error_code& ec) noexcept {
|
||
ec.clear();
|
||
// 限定只支持整数/浮点,其他类型编译期报错(比运行时返回默认值更安全)
|
||
static_assert(std::is_integral_v<T_Number> ||
|
||
std::is_floating_point_v<T_Number>,
|
||
"to_number<T_Number>: T_Number must be an integral or "
|
||
"floating-point type");
|
||
try {
|
||
if constexpr (std::is_integral_v<T_Number>) {
|
||
if constexpr (std::is_unsigned_v<T_Number>) {
|
||
// stoull: 负号、非数字等会抛 invalid_argument;超范围抛 out_of_range
|
||
auto text = std::string(val);
|
||
unsigned long long x = std::stoull(text);
|
||
// 额外范围检查,避免静默截断
|
||
if (x > static_cast<unsigned long long>(
|
||
std::numeric_limits<T_Number>::max())) {
|
||
ec = std::make_error_code(std::errc::result_out_of_range);
|
||
return false;
|
||
}
|
||
out = static_cast<T_Number>(x);
|
||
return true;
|
||
}
|
||
else {
|
||
auto text = std::string(val);
|
||
long long x = std::stoll(text);
|
||
// 额外范围检查,避免静默截断
|
||
if (x < static_cast<long long>(std::numeric_limits<T_Number>::min()) ||
|
||
x > static_cast<long long>(std::numeric_limits<T_Number>::max())) {
|
||
ec = std::make_error_code(std::errc::result_out_of_range);
|
||
return false;
|
||
}
|
||
out = static_cast<T_Number>(x);
|
||
return true;
|
||
}
|
||
}
|
||
else {
|
||
// floating point
|
||
auto text = std::string(val);
|
||
long double x = std::stold(text);
|
||
// 可选:检查是否超出目标浮点类型范围
|
||
if (x < -static_cast<long double>(std::numeric_limits<T_Number>::max()) ||
|
||
x > static_cast<long double>(std::numeric_limits<T_Number>::max())) {
|
||
ec = std::make_error_code(std::errc::result_out_of_range);
|
||
return false;
|
||
}
|
||
out = static_cast<T_Number>(x);
|
||
return true;
|
||
}
|
||
}
|
||
catch (const std::invalid_argument&) {
|
||
ec = std::make_error_code(std::errc::invalid_argument);
|
||
return false; // 不能解析为数字
|
||
}
|
||
catch (const std::out_of_range&) {
|
||
ec = std::make_error_code(std::errc::result_out_of_range);
|
||
return false; // 数值超范围
|
||
}
|
||
}
|
||
class JSON {
|
||
public:
|
||
static std::function<void(const char*, std::string_view, const char*,
|
||
int)>
|
||
handle_error;
|
||
explicit operator std::string() const;
|
||
friend std::ostream& operator<<(std::ostream& os, const JSON& obj);
|
||
[[nodiscard]] bool has(std::string_view key) const;
|
||
[[nodiscard]] const JSON* get(std::string_view key) const;
|
||
PSC_DECLARE_TRIPLE_API_CONST_METHOD(std::string, get_string,
|
||
PSC_JSON_KEY_PARAMS);
|
||
PSC_DECLARE_TRIPLE_API_CONST_METHOD_0(bool, bool_val);
|
||
PSC_DECLARE_TRIPLE_API_CONST_METHOD(bool, get_bool, PSC_JSON_KEY_PARAMS);
|
||
template <typename T_Number>
|
||
T_Number get_number(std::string_view key) const {
|
||
return detail::triple_api_throwing<T_Number>(
|
||
[this, &key](T_Number& out, std::error_code& ec) {
|
||
return get_number_ec(key, out, ec);
|
||
},
|
||
"get_number failed");
|
||
}
|
||
template <typename T_Number>
|
||
expected<T_Number, std::error_code> try_get_number(std::string_view key) const noexcept {
|
||
return detail::triple_api_expected<T_Number>(
|
||
[this, &key](T_Number& out, std::error_code& ec) {
|
||
return get_number_ec(key, out, ec);
|
||
});
|
||
}
|
||
template <typename T_Number>
|
||
bool get_number_ec(std::string_view key, T_Number& out,
|
||
std::error_code& ec) const noexcept {
|
||
const JSON* that = get(key);
|
||
if (!that) {
|
||
ec = std::make_error_code(std::errc::invalid_argument);
|
||
return false;
|
||
}
|
||
return that->number_val_ec(out, ec);
|
||
}
|
||
template <typename T_Number>
|
||
T_Number number_val() const {
|
||
return detail::triple_api_throwing<T_Number>(
|
||
[this](T_Number& out, std::error_code& ec) {
|
||
return number_val_ec(out, ec);
|
||
},
|
||
"number_val failed");
|
||
}
|
||
template <typename T_Number>
|
||
expected<T_Number, std::error_code> try_number_val() const noexcept {
|
||
return detail::triple_api_expected<T_Number>(
|
||
[this](T_Number& out, std::error_code& ec) {
|
||
return number_val_ec(out, ec);
|
||
});
|
||
}
|
||
template <typename T_Number>
|
||
bool number_val_ec(T_Number& out, std::error_code& ec) const noexcept {
|
||
if (valueType != Number) {
|
||
ec = std::make_error_code(std::errc::invalid_argument);
|
||
return false;
|
||
}
|
||
return to_number_ec(val, out, ec);
|
||
}
|
||
JSON(std::string_view key, const JSON& body);
|
||
template <typename T,
|
||
typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, JSON>>>
|
||
JSON(std::string_view k, T&& v) {
|
||
key = std::string(k);
|
||
assign_json(valueType, val, std::forward<T>(v));
|
||
}
|
||
template <typename T,
|
||
typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, JSON>>>
|
||
JSON(T&& v) {
|
||
assign_json(valueType, val, std::forward<T>(v));
|
||
}
|
||
JSON();
|
||
JSON(const JSON& other);
|
||
JSON(JSON&& other) noexcept;
|
||
JSON& operator=(const JSON& other);
|
||
JSON& operator=(JSON&& other) noexcept;
|
||
JSON& operator+=(const JSON& child);
|
||
JSON& operator+=(JSON&& child);
|
||
void append_list(const std::vector<JSON>& arr);
|
||
void append(const JSON& json);
|
||
void prepend(const JSON& json);
|
||
static JSON array(std::string_view key, const std::vector<JSON>& arr);
|
||
static JSON object(std::string_view key, const std::vector<JSON>& children);
|
||
static JSON array(const std::vector<JSON>& arr = {});
|
||
static JSON object(const std::vector<JSON>& children = {});
|
||
JsonType valueType = Null;
|
||
std::string key, val;
|
||
std::vector<JSON> children;
|
||
[[nodiscard]] std::string to_json_string(std::string_view object_split = "\n",
|
||
std::string_view array_split = "\n",
|
||
std::string_view indentStr = " ",
|
||
const int& indent = 0) const;
|
||
private:
|
||
static std::string get_indent(const int& indent,
|
||
std::string_view indentStr);
|
||
void append_value_string(std::string& ret, const int& indent, bool isEnd,
|
||
std::string_view object_split,
|
||
std::string_view array_split,
|
||
std::string_view indentStr,
|
||
JsonType fatherType) const;
|
||
};
|
||
PSC_DECLARE_TRIPLE_API(JSON, parse_json, PSC_JSON_PARSE_PARAMS);
|
||
PSC_DECLARE_TRIPLE_API(JSON, parse_json_file, PSC_JSON_FILE_PARAMS);
|
||
template <typename T>
|
||
void assign_json(JsonType& rt, std::string& rv, T&& v) {
|
||
using C = std::decay_t<T>;
|
||
if constexpr (std::is_same_v<C, std::string>) {
|
||
rt = String;
|
||
rv = std::forward<T>(v);
|
||
}
|
||
else if constexpr (std::is_same_v<C, std::string_view>) {
|
||
rt = String;
|
||
rv = std::string(v);
|
||
}
|
||
else if constexpr (std::is_same_v<C, void*>) {
|
||
std::stringstream ss;
|
||
ss << "0x" << std::setw(sizeof(void*) * 2) << std::setfill('0') << std::hex
|
||
<< reinterpret_cast<std::uintptr_t>(v);
|
||
rt = String;
|
||
rv = ss.str();
|
||
}
|
||
// 处理 char* 类型
|
||
else if constexpr (std::is_same_v<C, char*>) {
|
||
rt = String;
|
||
rv = std::string(v);
|
||
}
|
||
else if constexpr (std::is_same_v<C, const char*>) {
|
||
rt = String;
|
||
rv = std::string(v); // 或 val = v;
|
||
}
|
||
// 处理 wchar_t* 类型
|
||
else if constexpr (std::is_same_v<C, wchar_t*>) {
|
||
std::wstring ws(v);
|
||
rt = String;
|
||
rv = std::string(ws.begin(), ws.end());
|
||
}
|
||
// 处理 std::wstring 类型
|
||
else if constexpr (std::is_same_v<C, std::wstring>) {
|
||
rt = String;
|
||
rv = std::string(v.begin(), v.end());
|
||
}
|
||
// 处理枚举类型
|
||
else if constexpr (std::is_enum_v<C>) {
|
||
rt = String;
|
||
rv = std::string(magic_enum::enum_name(v));
|
||
}
|
||
// 处理 bool 类型
|
||
else if constexpr (std::is_same_v<C, bool>) {
|
||
rt = Bool;
|
||
rv = v ? "true" : "false"; // 对 bool 类型进行自定义转换
|
||
}
|
||
// 处理 std::nullptr_t 类型
|
||
else if constexpr (std::is_same_v<C, std::nullptr_t>) {
|
||
rt = Null;
|
||
rv = "null";
|
||
}
|
||
else if constexpr (std::is_arithmetic_v<C>) {
|
||
rt = Number;
|
||
rv = std::to_string(v);
|
||
}
|
||
else if constexpr (is_atomic_v<C> || is_copyable_atomic_v<C>) {
|
||
assign_json(rt, rv, v.load());
|
||
}
|
||
else if constexpr (is_optional_v<C>) {
|
||
if (v.has_value()) {
|
||
assign_json(rt, rv, v.value());
|
||
}
|
||
else {
|
||
rt = Null;
|
||
rv = "null";
|
||
}
|
||
}
|
||
else {
|
||
rt = String;
|
||
rv = v.to_string();
|
||
}
|
||
}
|
||
void check_json_assign_field(bool ok, const std::error_code& ec,
|
||
const Psc::JSON* that_json, const char* name);
|
||
template <typename T>
|
||
bool assign_field_ec(T& field, const Psc::JSON* that_json, const char* name, std::error_code& ec) noexcept;
|
||
template <typename T>
|
||
void assign_field(T& field, const Psc::JSON* that_json, const char* name) {
|
||
detail::triple_api_throwing_void(
|
||
[&field, that_json, name](std::error_code& ec) {
|
||
return assign_field_ec(field, that_json, name, ec);
|
||
},
|
||
"assign_field failed");
|
||
}
|
||
template <typename T>
|
||
expected<void, std::error_code> try_assign_field(T& field,
|
||
const Psc::JSON* that_json,
|
||
const char* name) noexcept {
|
||
return detail::triple_api_expected_void(
|
||
[&field, that_json, name](std::error_code& ec) {
|
||
return assign_field_ec(field, that_json, name, ec);
|
||
});
|
||
}
|
||
template <typename T>
|
||
bool assign_field_ec(T& field, const Psc::JSON* that_json, const char* name,
|
||
std::error_code& ec) noexcept {
|
||
using C = clean_t<T>;
|
||
if (that_json == nullptr) {
|
||
ec = std::make_error_code(std::errc::invalid_argument);
|
||
return false;
|
||
}
|
||
auto j = that_json->get(name);
|
||
if (j == nullptr) {
|
||
ec = std::make_error_code(std::errc::invalid_argument);
|
||
return false;
|
||
}
|
||
if constexpr (std::is_same_v<C, bool>) {
|
||
return j->bool_val_ec(field, ec);
|
||
}
|
||
else if constexpr (std::is_arithmetic_v<C> && !std::is_same_v<C, bool>) {
|
||
return that_json->get_number_ec(name, field, ec);
|
||
}
|
||
else if constexpr (std::is_same_v<C, std::string>) {
|
||
return that_json->get_string_ec(name, field, ec);
|
||
}
|
||
else if constexpr (std::is_enum_v<C>) {
|
||
std::string value;
|
||
if (!that_json->get_string_ec(name, value, ec))
|
||
return false;
|
||
field = Psc::to_enum<C>(value);
|
||
}
|
||
else if constexpr (is_atomic_v<T>) {
|
||
using V = atomic_value_t<T>;
|
||
V value{};
|
||
if constexpr (std::is_same_v<V, bool>) {
|
||
if (!that_json->get_bool_ec(name, value, ec))
|
||
return false;
|
||
}
|
||
else if constexpr (std::is_arithmetic_v<V>) {
|
||
if (!that_json->get_number_ec(name, value, ec))
|
||
return false;
|
||
}
|
||
field = value;
|
||
}
|
||
else if constexpr (is_copyable_atomic_v<T>) {
|
||
using V = copyable_atomic_value_t<T>;
|
||
V value{};
|
||
if constexpr (std::is_same_v<V, bool>) {
|
||
if (!that_json->get_bool_ec(name, value, ec)) {
|
||
return false;
|
||
}
|
||
}
|
||
else if constexpr (std::is_arithmetic_v<V>) {
|
||
if (!that_json->get_number_ec(name, value, ec)) {
|
||
return false;
|
||
}
|
||
}
|
||
field = value;
|
||
}
|
||
else if constexpr (is_optional_v<T>) {
|
||
using V = optional_value_t<T>;
|
||
V value{};
|
||
if constexpr (std::is_same_v<V, bool>) {
|
||
if (!that_json->get_bool_ec(name, value, ec))
|
||
return false;
|
||
}
|
||
else if constexpr (std::is_arithmetic_v<V> && !std::is_same_v<V, bool>) {
|
||
if (!that_json->get_number_ec(name, value, ec))
|
||
return false;
|
||
}
|
||
else if constexpr (std::is_same_v<V, std::string>) {
|
||
if (!that_json->get_string_ec(name, value, ec))
|
||
return false;
|
||
}
|
||
else if constexpr (std::is_enum_v<V>) {
|
||
std::string text;
|
||
if (!that_json->get_string_ec(name, text, ec))
|
||
return false;
|
||
value = Psc::to_enum<V>(text);
|
||
}
|
||
else {
|
||
static_assert(!sizeof(T),
|
||
"assign_field does not support this optional type");
|
||
}
|
||
field = std::move(value);
|
||
}
|
||
else {
|
||
static_assert(!sizeof(T), "assign_field does not support this type");
|
||
}
|
||
return true;
|
||
}
|
||
template <typename T>
|
||
void output_field(const T& field, Psc::JSON& ret, const char* name) {
|
||
using C = clean_t<T>;
|
||
// bool
|
||
if constexpr (std::is_same_v<C, bool>) {
|
||
ret.append(Psc::JSON(name, field));
|
||
}
|
||
// 数字(排除 bool)
|
||
else if constexpr (std::is_arithmetic_v<C> && !std::is_same_v<C, bool>) {
|
||
ret.append(Psc::JSON(name, field));
|
||
}
|
||
// string
|
||
else if constexpr (std::is_same_v<C, std::string>) {
|
||
ret.append(Psc::JSON(name, field));
|
||
}
|
||
// enum
|
||
else if constexpr (std::is_enum_v<C>) {
|
||
ret.append(Psc::JSON(name, Psc::to_string(field)));
|
||
}
|
||
// atomic<T>
|
||
else if constexpr (is_atomic_v<T> || is_copyable_atomic_v<C>) {
|
||
ret.append(Psc::JSON(name, field.load()));
|
||
}
|
||
// optional<T>
|
||
else if constexpr (is_optional_v<T>) {
|
||
using V = optional_value_t<T>;
|
||
if (field.has_value()) {
|
||
// --- optional<bool>
|
||
if constexpr (std::is_same_v<V, bool>) {
|
||
ret.append(Psc::JSON(name, field.value()));
|
||
}
|
||
// --- optional<number>
|
||
else if constexpr (std::is_arithmetic_v<V> && !std::is_same_v<V, bool>) {
|
||
ret.append(Psc::JSON(name, field.value()));
|
||
}
|
||
// --- optional<string>
|
||
else if constexpr (std::is_same_v<V, std::string>) {
|
||
ret.append(Psc::JSON(name, field.value()));
|
||
}
|
||
// --- optional<enum>
|
||
else if constexpr (std::is_enum_v<V>) {
|
||
ret.append(Psc::JSON(name, to_string(field.value())));
|
||
}
|
||
// --- optional<atomic>
|
||
else if constexpr (is_atomic_v<T> || is_copyable_atomic_v<T>) {
|
||
ret.append(Psc::JSON(name, field.value().load()));
|
||
}
|
||
// --- optional<未知类型>(调试输出)
|
||
else {
|
||
ret.append(Psc::JSON(name, field.value().to_string()));
|
||
}
|
||
}
|
||
else {
|
||
// optional 没值 → JSON null
|
||
ret.append(Psc::JSON(name, nullptr));
|
||
}
|
||
}
|
||
else if constexpr (is_shared_ptr_v<C>) {
|
||
if (field) {
|
||
ret.append(Psc::JSON(name, field->to_string()));
|
||
}
|
||
else {
|
||
ret.append(Psc::JSON(name, nullptr));
|
||
}
|
||
}
|
||
// 最终兜底
|
||
else {
|
||
ret.append(Psc::JSON(name, field.to_string()));
|
||
}
|
||
}
|
||
} // namespace Psc
|
||
#undef Json_GET
|
||
#define Get_J_ex(NAME, FIELD) \
|
||
{ \
|
||
std::error_code ec_##FIELD; \
|
||
const bool ok_##FIELD = \
|
||
Psc::assign_field_ec(FIELD, that_json, NAME, ec_##FIELD); \
|
||
Psc::check_json_assign_field(ok_##FIELD, ec_##FIELD, that_json, NAME); \
|
||
}
|
||
#define Ret_J_ex(NAME, FIELD) \
|
||
{ \
|
||
Psc::output_field(FIELD, ret, NAME); \
|
||
}
|
||
#define Get_J(FIELD) Get_J_ex(#FIELD, FIELD)
|
||
#define Ret_J(FIELD) Ret_J_ex(#FIELD, FIELD)
|
||
#define JSON_KV(P) {#P, (P)}
|
||
#define JSON_KV_1(P1) JSON_KV(P1)
|
||
#define JSON_KV_2(P1, P2) JSON_KV_1(P1), JSON_KV(P2)
|
||
#define JSON_KV_3(P1, P2, P3) JSON_KV_2(P1, P2), JSON_KV(P3)
|
||
#define JSON_KV_4(P1, P2, P3, P4) JSON_KV_3(P1, P2, P3), JSON_KV(P4)
|
||
#define JSON_KV_5(P1, P2, P3, P4, P5) JSON_KV_4(P1, P2, P3, P4), JSON_KV(P5)
|
||
#define JSON_KV_6(P1, P2, P3, P4, P5, P6) \
|
||
JSON_KV_5(P1, P2, P3, P4, P5), JSON_KV(P6)
|
||
#define JSON_KV_7(P1, P2, P3, P4, P5, P6, P7) \
|
||
JSON_KV_6(P1, P2, P3, P4, P5, P6), JSON_KV(P7)
|
||
#define JSON_KV_8(P1, P2, P3, P4, P5, P6, P7, P8) \
|
||
JSON_KV_7(P1, P2, P3, P4, P5, P6, P7), JSON_KV(P8)
|
||
#define JSON_KV_9(P1, P2, P3, P4, P5, P6, P7, P8, P9) \
|
||
JSON_KV_8(P1, P2, P3, P4, P5, P6, P7, P8), JSON_KV(P9)
|
||
#define JSON_KV_10(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \
|
||
JSON_KV_9(P1, P2, P3, P4, P5, P6, P7, P8, P9), JSON_KV(P10)
|
||
#define VAR_JSON_1(P1) Psc::JSON::object({JSON_KV_1(P1)})
|
||
#define VAR_JSON_2(P1, P2) Psc::JSON::object({JSON_KV_2(P1, P2)})
|
||
#define VAR_JSON_3(P1, P2, P3) Psc::JSON::object({JSON_KV_3(P1, P2, P3)})
|
||
#define VAR_JSON_4(P1, P2, P3, P4) \
|
||
Psc::JSON::object({JSON_KV_4(P1, P2, P3, P4)})
|
||
#define VAR_JSON_5(P1, P2, P3, P4, P5) \
|
||
Psc::JSON::object({JSON_KV_5(P1, P2, P3, P4, P5)})
|
||
#define VAR_JSON_6(P1, P2, P3, P4, P5, P6) \
|
||
Psc::JSON::object({JSON_KV_6(P1, P2, P3, P4, P5, P6)})
|
||
#define VAR_JSON_7(P1, P2, P3, P4, P5, P6, P7) \
|
||
Psc::JSON::object({JSON_KV_7(P1, P2, P3, P4, P5, P6, P7)})
|
||
#define VAR_JSON_8(P1, P2, P3, P4, P5, P6, P7, P8) \
|
||
Psc::JSON::object({JSON_KV_8(P1, P2, P3, P4, P5, P6, P7, P8)})
|
||
#define VAR_JSON_9(P1, P2, P3, P4, P5, P6, P7, P8, P9) \
|
||
Psc::JSON::object({JSON_KV_9(P1, P2, P3, P4, P5, P6, P7, P8, P9)})
|
||
#define VAR_JSON_10(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \
|
||
Psc::JSON::object({JSON_KV_10(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10)})
|
||
#include <boost/pfr/core_name.hpp>
|
||
#include <string_view>
|
||
#include "boost/pfr/core.hpp"
|
||
#define PSC_TO_JSON \
|
||
[[nodiscard]] Psc::JSON to_base_json() const { \
|
||
Psc::JSON ret = Psc::JSON::object(); \
|
||
using T = std::remove_cv_t<std::remove_pointer_t<decltype(this)>>; \
|
||
boost::pfr::for_each_field(*this, [&, names = boost::pfr::names_as_array<T>(), i = std::size_t{}](const auto& field) mutable { \
|
||
Psc::output_field(field, ret, names[i++].data()); \
|
||
}); \
|
||
return ret; \
|
||
}
|
||
#define PSC_FROM_JSON \
|
||
void from_base_json(const Psc::JSON* that_json) { \
|
||
using T = std::remove_cv_t<std::remove_pointer_t<decltype(this)>>; \
|
||
boost::pfr::for_each_field(*this, [&, names = boost::pfr::names_as_array<T>(), i = std::size_t{}](auto& field) mutable { \
|
||
auto name = names[i++].data(); \
|
||
std::error_code ec; \
|
||
auto t = Psc::assign_field_ec(field, that_json, name, ec); \
|
||
Psc::check_json_assign_field(t, ec, that_json, name); \
|
||
}); \
|
||
}
|
||
#define PSC_USE_JSON \
|
||
PSC_TO_JSON \
|
||
PSC_FROM_JSON
|