diff --git a/src/core/include/battery/application.hpp b/src/core/include/battery/application.hpp index 83aa1380..8cc2a38c 100644 --- a/src/core/include/battery/application.hpp +++ b/src/core/include/battery/application.hpp @@ -8,6 +8,7 @@ #include "eventbus.hpp" #include "events.hpp" #include "renderer.hpp" +#include extern std::unique_ptr CreateApp(); diff --git a/src/core/include/battery/thread.hpp b/src/core/include/battery/thread.hpp index 967c4c75..b3a8dd37 100644 --- a/src/core/include/battery/thread.hpp +++ b/src/core/include/battery/thread.hpp @@ -140,11 +140,11 @@ namespace b { : std::jthread([func = std::forward(func), ...args = std::forward(args), this]() mutable { m_waitRunningPromise.set_value(); if constexpr (std::is_invocable_v, std::stop_token, std::decay_t...>) { - if (!catchCommonExceptions(std::move(func), get_stop_token(), std::move(args)...)) { + if (!CatchCommonExceptions(std::move(func), get_stop_token(), std::move(args)...)) { closeApplicationIfExists(); } } else { - if (!catchCommonExceptions(std::move(func), std::move(args)...)) { + if (!CatchCommonExceptions(std::move(func), std::move(args)...)) { closeApplicationIfExists(); } } diff --git a/src/core/include/glaze/api/impl.hpp b/src/core/include/glaze/api/impl.hpp index 53386f93..44b95c19 100644 --- a/src/core/include/glaze/api/impl.hpp +++ b/src/core/include/glaze/api/impl.hpp @@ -91,8 +91,9 @@ namespace glz } template - requires std::invocable>...> decltype(auto) - call_args(F&& f, Parent&& parent, [[maybe_unused]] std::span args, std::index_sequence) + requires std::invocable>...> + decltype(auto) call_args(F&& f, Parent&& parent, [[maybe_unused]] std::span args, + std::index_sequence) { return f(parent, to_ref>(args[Is])...); } @@ -245,8 +246,8 @@ namespace glz static constexpr auto h = glz::hash(); if (h == type_hash) [[likely]] { auto* f = new F{}; - *f = [parent = &parent, val = &val](auto&&... args) -> decltype(auto) { - return ((*parent).*(*val))(std::forward(args)...); + *f = [=, parent = &parent](auto&&... args) -> decltype(auto) { + return ((*parent).*(val))(std::forward(args)...); }; result = std::unique_ptr{ f, [](void* ptr) { delete static_cast(ptr); }}; diff --git a/src/core/include/glaze/api/lib.hpp b/src/core/include/glaze/api/lib.hpp index ca97d3e4..55c833c2 100644 --- a/src/core/include/glaze/api/lib.hpp +++ b/src/core/include/glaze/api/lib.hpp @@ -29,7 +29,7 @@ #include #define SHARED_LIBRARY_EXTENSION ".dylib" #define SHARED_LIBRARY_PREFIX "lib" -#elif __linux__ +#elif __has_include() #include #define SHARED_LIBRARY_EXTENSION ".so" #define SHARED_LIBRARY_PREFIX "lib" diff --git a/src/core/include/glaze/binary/read.hpp b/src/core/include/glaze/binary/read.hpp index 509845e6..a1338a04 100644 --- a/src/core/include/glaze/binary/read.hpp +++ b/src/core/include/glaze/binary/read.hpp @@ -8,6 +8,7 @@ #include "glaze/core/format.hpp" #include "glaze/core/read.hpp" #include "glaze/file/file_ops.hpp" +#include "glaze/reflection/reflect.hpp" #include "glaze/util/dump.hpp" namespace glz @@ -19,11 +20,10 @@ namespace glz {}; template - concept read_binary_invocable = - requires(T&& value, Ctx&& ctx, It0&& it, It1&& end) { - from_binary>::template op(std::forward(value), std::forward(ctx), - std::forward(it), std::forward(end)); - }; + concept read_binary_invocable = requires(T&& value, Ctx&& ctx, It0&& it, It1&& end) { + from_binary>::template op(std::forward(value), std::forward(ctx), + std::forward(it), std::forward(end)); + }; template <> struct read @@ -65,6 +65,37 @@ namespace glz } }; + template + struct from_binary + { + template + GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, auto&& it, auto&& end) noexcept + { + const auto tag = uint8_t(*it); + + constexpr uint8_t type = uint8_t(3) << 3; + constexpr uint8_t header = tag::typed_array | type; + + if (tag != header) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } + + ++it; + + const auto n = int_from_compressed(it, end); + + const auto num_bytes = (value.size() + 7) / 8; + for (size_t byte_i{}, i{}; byte_i < num_bytes; ++byte_i, ++it) { + uint8_t byte; + std::memcpy(&byte, &*it, 1); + for (size_t bit_i = 0; bit_i < 8 && i < n; ++bit_i, ++i) { + value[i] = byte >> bit_i & uint8_t(1); + } + } + } + }; + template <> struct from_binary { @@ -90,9 +121,9 @@ namespace glz std::advance(it, Length); for_each([&](auto I) { - static constexpr auto item = glz::tuplet::get(meta_v); + static constexpr auto item = glz::get(meta_v); - get_member(value, glz::tuplet::get<1>(item)) = data[I / 8] & (uint8_t{1} << (7 - (I % 8))); + get_member(value, glz::get<1>(item)) = data[I / 8] & (uint8_t{1} << (7 - (I % 8))); }); } }; @@ -122,7 +153,7 @@ namespace glz ++it; - using V = std::decay_t; + using V = std::decay_t; std::memcpy(&value, &(*it), sizeof(V)); std::advance(it, sizeof(V)); } @@ -182,7 +213,7 @@ namespace glz return; } - value = tag >> 3; + value = tag >> 4; ++it; } }; @@ -272,6 +303,115 @@ namespace glz } }; + // for set types + template + requires(readable_array_t && !emplace_backable && !resizeable && emplaceable) + struct from_binary final + { + template + GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, auto&& it, auto&& end) noexcept + { + using V = range_value_t>; + + const auto tag = uint8_t(*it); + + if constexpr (boolean_like) { + constexpr uint8_t type = uint8_t(3) << 3; + constexpr uint8_t header = tag::typed_array | type; + + if (tag != header) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } + + ++it; + + const auto n = int_from_compressed(it, end); + + value.clear(); + + const auto num_bytes = (value.size() + 7) / 8; + for (size_t byte_i{}, i{}; byte_i < num_bytes; ++byte_i, ++it) { + uint8_t byte; + std::memcpy(&byte, &*it, 1); + for (size_t bit_i = 7; bit_i < 8 && i < n; --bit_i, ++i) { + bool x = byte >> bit_i & uint8_t(1); + value.emplace(x); + } + } + } + else if constexpr (num_t) { + constexpr uint8_t type = + std::floating_point ? 0 : (std::is_signed_v ? 0b000'01'000 : 0b000'10'000); + constexpr uint8_t header = tag::typed_array | type | (byte_count << 5); + + if (tag != header) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } + + ++it; + + const auto n = int_from_compressed(it, end); + + value.clear(); + + for (size_t i = 0; i < n; ++i) { + V x; + std::memcpy(&x, &*it, sizeof(V)); + std::advance(it, sizeof(V)); + value.emplace(x); + } + } + else if constexpr (str_t) { + constexpr uint8_t type = uint8_t(3) << 3; + constexpr uint8_t string_indicator = uint8_t(1) << 5; + constexpr uint8_t header = tag::typed_array | type | string_indicator; + + if (tag != header) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } + + ++it; + + const auto n = int_from_compressed(it, end); + + value.clear(); + + for (size_t i = 0; i < n; ++i) { + const auto length = int_from_compressed(it, end); + V str; + str.resize(length); + std::memcpy(str.data(), &*it, length); + std::advance(it, length); + value.emplace(std::move(str)); + } + } + else if constexpr (complex_t) { + static_assert(false_v, "TODO"); + } + else { + // generic array + if ((tag & 0b00000'111) != tag::generic_array) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } + ++it; + + const auto n = int_from_compressed(it, end); + + value.clear(); + + for (size_t i = 0; i < n; ++i) { + V v; + read::op(v, ctx, it, end); + value.emplace(std::move(v)); + } + } + } + }; + template struct from_binary final { @@ -553,19 +693,67 @@ namespace glz } }; - template - struct from_binary> + template + struct from_binary { template - GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&&, auto&&, auto&&) noexcept - {} + GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&& ctx, auto&& it, auto&& end) noexcept + { + if constexpr (Opts.no_header) { + std::ignore = int_from_compressed(it, end); + } + else { + constexpr uint8_t header = tag::string; + + const auto tag = uint8_t(*it); + if (tag != header) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } + + ++it; + + std::ignore = int_from_compressed(it, end); + } + } }; template - requires glaze_object_t + requires glaze_object_t || reflectable struct from_binary final { template + requires(Opts.structs_as_arrays == true) + GLZ_FLATTEN static void op(auto&& value, is_context auto&& ctx, auto&& it, auto&& end) noexcept + { + if constexpr (reflectable) { + auto t = to_tuple(value); + read::op(t, ctx, it, end); + } + else { + const auto tag = uint8_t(*it); + if (tag != tag::generic_array) [[unlikely]] { + ctx.error = error_code::syntax_error; + return; + } + ++it; + + using V = std::decay_t; + static constexpr auto N = std::tuple_size_v>; + skip_compressed_int(it, end); + + for_each([&](auto I) { + static constexpr auto item = get(meta_v); + using T0 = std::decay_t(item))>; + static constexpr bool use_reflection = std::is_member_object_pointer_v; + static constexpr auto member_index = use_reflection ? 0 : 1; + read::op(get_member(value, get(item)), ctx, it, end); + }); + } + } + + template + requires(Opts.structs_as_arrays == false) GLZ_FLATTEN static void op(auto&& value, is_context auto&& ctx, auto&& it, auto&& end) noexcept { constexpr uint8_t type = 0; // string key @@ -581,7 +769,17 @@ namespace glz const auto n_keys = int_from_compressed(it, end); - static constexpr auto storage = detail::make_map(); + decltype(auto) storage = [&] { + if constexpr (reflectable) { + static constinit auto cmap = make_map(); + populate_map(value, cmap); // Function required for MSVC to build + return cmap; + } + else { + static constexpr auto cmap = make_map(); + return cmap; + } + }(); for (size_t i = 0; i < n_keys; ++i) { const auto length = int_from_compressed(it, end); @@ -590,9 +788,7 @@ namespace glz std::advance(it, length); - const auto& p = storage.find(key); - - if (p != storage.end()) [[likely]] { + if (const auto& p = storage.find(key); p != storage.end()) [[likely]] { std::visit( [&](auto&& member_ptr) { read::op(get_member(value, member_ptr), ctx, it, end); }, p->second); @@ -633,9 +829,8 @@ namespace glz skip_compressed_int(it, end); using V = std::decay_t; - for_each>>([&](auto I) { - read::op(get_member(value, glz::tuplet::get(meta_v)), ctx, it, end); - }); + for_each>>( + [&](auto I) { read::op(get_member(value, glz::get(meta_v)), ctx, it, end); }); } }; @@ -678,7 +873,7 @@ namespace glz return value; } - template + template [[nodiscard]] inline parse_error read_file_binary(T& value, const sv file_name, auto&& buffer) noexcept { context ctx{}; @@ -690,6 +885,31 @@ namespace glz return parse_error{file_error}; } - return read(value, buffer, ctx); + return read()>(value, buffer, ctx); + } + + template + [[nodiscard]] inline parse_error read_binary_untagged(T&& value, Buffer&& buffer) noexcept + { + return read(std::forward(value), + std::forward(buffer)); + } + + template + [[nodiscard]] inline expected read_binary_untagged(Buffer&& buffer) noexcept + { + T value{}; + const auto pe = read(value, std::forward(buffer)); + if (pe) [[unlikely]] { + return unexpected(pe); + } + return value; + } + + template + [[nodiscard]] inline parse_error read_file_binary_untagged(T& value, const std::string& file_name, + auto&& buffer) noexcept + { + return read_file_binary>(value, file_name, buffer); } } diff --git a/src/core/include/glaze/binary/write.hpp b/src/core/include/glaze/binary/write.hpp index e8c10843..ac2eea96 100644 --- a/src/core/include/glaze/binary/write.hpp +++ b/src/core/include/glaze/binary/write.hpp @@ -11,17 +11,22 @@ #include "glaze/json/json_ptr.hpp" #include "glaze/util/dump.hpp" #include "glaze/util/for_each.hpp" -#include "glaze/util/murmur.hpp" #include "glaze/util/variant.hpp" namespace glz { namespace detail { - template - GLZ_ALWAYS_INLINE void dump_type(auto&& value, Args&&... args) noexcept + GLZ_ALWAYS_INLINE void dump_type(auto&& value, auto&& b, auto&& ix) noexcept { - dump(std::as_bytes(std::span{&value, 1}), std::forward(args)...); + assert(ix <= b.size()); + constexpr auto n = sizeof(std::decay_t); + if (ix + n > b.size()) [[unlikely]] { + b.resize((std::max)(b.size() * 2, ix + n)); + } + + std::memcpy(b.data() + ix, &value, n); + ix += n; } template @@ -77,11 +82,10 @@ namespace glz {}; template - concept write_binary_invocable = - requires(T&& value, Ctx&& ctx, B&& b, IX&& ix) { - to_binary>::template op(std::forward(value), std::forward(ctx), - std::forward(b), std::forward(ix)); - }; + concept write_binary_invocable = requires(T&& value, Ctx&& ctx, B&& b, IX&& ix) { + to_binary>::template op(std::forward(value), std::forward(ctx), + std::forward(b), std::forward(ix)); + }; template <> struct write @@ -117,6 +121,32 @@ namespace glz } }; + template + struct to_binary + { + template + GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&&, auto&&... args) noexcept + { + constexpr uint8_t type = uint8_t(3) << 3; + constexpr uint8_t tag = tag::typed_array | type; + dump_type(tag, args...); + dump_compressed_int(value.size(), args...); + + // constexpr auto num_bytes = (value.size() + 7) / 8; + const auto num_bytes = (value.size() + 7) / 8; + // .size() should be constexpr, but clang doesn't support this + std::vector bytes(num_bytes); + // std::array bytes{}; + for (size_t byte_i{}, i{}; byte_i < num_bytes; ++byte_i) { + for (size_t bit_i = 0; bit_i < 8 && i < value.size(); ++bit_i, ++i) { + bytes[byte_i] |= uint8_t(value[i]) << uint8_t(bit_i); + } + } + // dump(bytes, args...); + dump(std::as_bytes(std::span{bytes}), args...); + } + }; + template struct to_binary { @@ -128,9 +158,9 @@ namespace glz std::array()> data{}; for_each([&](auto I) { - static constexpr auto item = glz::tuplet::get(meta_v); + static constexpr auto item = glz::get(meta_v); - data[I / 8] |= static_cast(get_member(value, glz::tuplet::get<1>(item))) << (7 - (I % 8)); + data[I / 8] |= static_cast(get_member(value, glz::get<1>(item))) << (7 - (I % 8)); }); dump(data, b, ix); @@ -145,12 +175,18 @@ namespace glz {} }; - template - struct to_binary> + // // write includers as empty strings + template + struct to_binary { template - GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&&, Args&&...) noexcept - {} + GLZ_ALWAYS_INLINE static void op(auto&&, is_context auto&&, Args&&... args) noexcept + { + constexpr uint8_t tag = tag::string; + + dump_type(tag, args...); + dump_compressed_int(0, args...); + } }; template @@ -181,19 +217,28 @@ namespace glz } }; + template + constexpr std::size_t variant_index_impl(std::index_sequence) + { + return ((std::is_same_v> * Is) + ...); + } + + template + constexpr size_t variant_index_v = variant_index_impl(std::make_index_sequence>{}); + template struct to_binary final { template GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, Args&&... args) noexcept { + using Variant = std::decay_t; + std::visit( [&](auto&& v) { using V = std::decay_t; - static constexpr auto index = []() { - T var = V{}; - return var.index(); - }(); + + static constexpr uint64_t index = variant_index_v; constexpr uint8_t tag = tag::extensions | 0b00001'000; @@ -256,20 +301,36 @@ namespace glz struct to_binary final { template - GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&&, auto&&... args) noexcept + GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&&, auto&& b, auto&& ix) noexcept { constexpr uint8_t tag = tag::string; - dump_type(tag, args...); - dump_compressed_int(value.size(), args...); - dump(std::as_bytes(std::span{value.data(), value.size()}), args...); + dump_type(tag, b, ix); + const auto n = value.size(); + dump_compressed_int(n, b, ix); + + assert(ix <= b.size()); + if (ix + n > b.size()) [[unlikely]] { + b.resize((std::max)(b.size() * 2, ix + n)); + } + + std::memcpy(b.data() + ix, value.data(), n); + ix += n; } template - GLZ_ALWAYS_INLINE static void no_header(auto&& value, is_context auto&&, auto&&... args) noexcept + GLZ_ALWAYS_INLINE static void no_header(auto&& value, is_context auto&&, auto&& b, auto&& ix) noexcept { - dump_compressed_int(value.size(), args...); - dump(std::as_bytes(std::span{value.data(), value.size()}), args...); + dump_compressed_int(value.size(), b, ix); + + assert(ix <= b.size()); + const auto n = value.size(); + if (ix + n > b.size()) [[unlikely]] { + b.resize((std::max)(b.size() * 2, ix + n)); + } + + std::memcpy(b.data() + ix, value.data(), n); + ix += n; } }; @@ -291,14 +352,14 @@ namespace glz if constexpr (has_static_size) { constexpr auto num_bytes = (value.size() + 7) / 8; std::array bytes{}; - for (size_t byte_i{}, i{}; byte_i < num_bytes - 1; ++byte_i) { + for (size_t byte_i{}, i{}; byte_i < num_bytes; ++byte_i) { for (size_t bit_i = 7; bit_i < 8 && i < value.size(); --bit_i, ++i) { bytes[byte_i] |= uint8_t(value[i]) << uint8_t(bit_i); } } dump(bytes, args...); } - else { + else if constexpr (accessible) { const auto num_bytes = (value.size() + 7) / 8; for (size_t byte_i{}, i{}; byte_i < num_bytes; ++byte_i) { uint8_t byte{}; @@ -308,6 +369,9 @@ namespace glz dump_type(byte, args...); } } + else { + static_assert(false_v); + } } else if constexpr (num_t) { constexpr uint8_t type = @@ -317,7 +381,18 @@ namespace glz dump_compressed_int(value.size(), args...); if constexpr (contiguous) { - dump(std::as_bytes(std::span{value.data(), static_cast(value.size())}), args...); + auto dump_array = [&](auto&& b, auto&& ix) { + assert(ix <= b.size()); + const auto n = value.size() * sizeof(V); + if (ix + n > b.size()) [[unlikely]] { + b.resize((std::max)(b.size() * 2, ix + n)); + } + + std::memcpy(b.data() + ix, value.data(), n); + ix += n; + }; + + dump_array(args...); } else { for (auto& x : value) { @@ -334,7 +409,19 @@ namespace glz for (auto& x : value) { dump_compressed_int(x.size(), args...); - dump(std::as_bytes(std::span{x.data(), x.size()}), args...); + + auto dump_array = [&](auto&& b, auto&& ix) { + assert(ix <= b.size()); + const auto n = x.size(); + if (ix + n > b.size()) [[unlikely]] { + b.resize((std::max)(b.size() * 2, ix + n)); + } + + std::memcpy(b.data() + ix, x.data(), n); + ix += n; + }; + + dump_array(args...); } } else if constexpr (complex_t) { @@ -390,7 +477,7 @@ namespace glz struct to_binary final { template - GLZ_ALWAYS_INLINE static auto op(auto&& value, is_context auto&& ctx, Args&&... args) noexcept + GLZ_FLATTEN static auto op(auto&& value, is_context auto&& ctx, Args&&... args) noexcept { using Key = typename T::key_type; @@ -423,25 +510,99 @@ namespace glz }; template - requires glaze_object_t + requires is_specialization_v || is_specialization_v + struct to_binary + { + template + GLZ_FLATTEN static void op(auto&& value, is_context auto&& ctx, auto&&... args) noexcept + { + constexpr uint8_t type = 0; // string key + constexpr uint8_t tag = tag::object | type; + dump_type(tag, args...); + + using V = std::decay_t; + static constexpr auto N = std::tuple_size_v / 2; + dump_compressed_int(args...); + + for_each([&](auto I) { + write::no_header(get<2 * I>(value.value), ctx, args...); + write::op(get<2 * I + 1>(value.value), ctx, args...); + }); + } + }; + + template + requires glaze_object_t || reflectable struct to_binary final { template - GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, Args&&... args) noexcept + requires(Opts.structs_as_arrays == true) + GLZ_FLATTEN static void op(auto&& value, is_context auto&& ctx, Args&&... args) noexcept + { + if constexpr (reflectable) { + const auto t = to_tuple(value); + write::op(t, ctx, args...); + } + else { + dump(args...); + + using V = std::decay_t; + static constexpr auto N = std::tuple_size_v>; + dump_compressed_int(args...); + + for_each([&](auto I) { + static constexpr auto item = get(meta_v); + using T0 = std::decay_t(item))>; + static constexpr bool use_reflection = std::is_member_object_pointer_v; + static constexpr auto member_index = use_reflection ? 0 : 1; + write::op(get_member(value, get(item)), ctx, args...); + }); + } + } + + template + requires(Opts.structs_as_arrays == false) + GLZ_FLATTEN static void op(auto&& value, is_context auto&& ctx, Args&&... args) noexcept { constexpr uint8_t type = 0; // string key constexpr uint8_t tag = tag::object | type; dump_type(tag, args...); using V = std::decay_t; - static constexpr auto N = std::tuple_size_v>; + static constexpr auto N = [] { + if constexpr (reflectable) { + return count_members; + } + else { + return std::tuple_size_v>; + } + }(); + dump_compressed_int(args...); - for_each([&](auto I) { - static constexpr auto item = glz::tuplet::get(meta_v); - write::no_header(glz::tuplet::get<0>(item), ctx, args...); - write::op(get_member(value, glz::tuplet::get<1>(item)), ctx, args...); - }); + if constexpr (reflectable) { + static constexpr auto members = member_names; + const auto t = to_tuple(value); + for_each([&](auto I) { + write::no_header(get(members), ctx, args...); + write::op(std::get(t), ctx, args...); + }); + } + else { + for_each([&](auto I) { + static constexpr auto item = get(meta_v); + using T0 = std::decay_t(item))>; + static constexpr bool use_reflection = std::is_member_object_pointer_v; + static constexpr auto member_index = use_reflection ? 0 : 1; + if constexpr (use_reflection) { + write::no_header(get_name(item)>(), ctx, args...); + } + else { + write::no_header(get<0>(item), ctx, args...); + } + write::op(get_member(value, get(item)), ctx, args...); + }); + } } }; @@ -458,9 +619,8 @@ namespace glz dump_compressed_int(args...); using V = std::decay_t; - for_each>>([&](auto I) { - write::op(get_member(value, glz::tuplet::get(meta_v)), ctx, args...); - }); + for_each>>( + [&](auto I) { write::op(get_member(value, glz::get(meta_v)), ctx, args...); }); } }; @@ -483,16 +643,16 @@ namespace glz } template - inline void write_binary(T&& value, Buffer&& buffer) + inline void write_binary(T&& value, Buffer&& buffer) noexcept { write(std::forward(value), std::forward(buffer)); } - template - inline auto write_binary(T&& value) + template + inline auto write_binary(T&& value) noexcept { std::string buffer{}; - write(std::forward(value), buffer); + write()>(std::forward(value), buffer); return buffer; } @@ -523,7 +683,7 @@ namespace glz if constexpr (detail::glaze_object_t>) { for_each([&](auto I) { - static constexpr auto group = glz::tuplet::get(groups); + static constexpr auto group = glz::get(groups); static constexpr auto key = std::get<0>(group); static constexpr auto sub_partial = std::get<1>(group); @@ -539,7 +699,7 @@ namespace glz } else if constexpr (detail::writable_map_t>) { for_each([&](auto I) { - static constexpr auto group = glz::tuplet::get(groups); + static constexpr auto group = glz::get(groups); static constexpr auto key_value = std::get<0>(group); static constexpr auto sub_partial = std::get<1>(group); @@ -590,16 +750,18 @@ namespace glz } template - inline auto write_binary(T&& value, Buffer&& buffer) + inline auto write_binary(T&& value, Buffer&& buffer) noexcept { return write(std::forward(value), std::forward(buffer)); } // std::string file_name needed for std::ofstream - template + template [[nodiscard]] inline write_error write_file_binary(T&& value, const std::string& file_name, auto&& buffer) noexcept { - write(std::forward(value), buffer); + static_assert(sizeof(decltype(*buffer.data())) == 1); + + write()>(std::forward(value), buffer); std::ofstream file(file_name, std::ios::binary); @@ -612,4 +774,25 @@ namespace glz return {}; } + + template + inline void write_binary_untagged(T&& value, Buffer&& buffer) noexcept + { + write(std::forward(value), std::forward(buffer)); + } + + template + inline auto write_binary_untagged(T&& value) noexcept + { + std::string buffer{}; + write(std::forward(value), buffer); + return buffer; + } + + template + [[nodiscard]] inline write_error write_file_binary_untagged(T&& value, const std::string& file_name, + auto&& buffer) noexcept + { + return write_file_binary>(std::forward(value), file_name, buffer); + } } diff --git a/src/core/include/glaze/compare/approx.hpp b/src/core/include/glaze/compare/approx.hpp new file mode 100644 index 00000000..5ea8434a --- /dev/null +++ b/src/core/include/glaze/compare/approx.hpp @@ -0,0 +1,38 @@ +// Glaze Library +// For the license information refer to glaze.hpp + +#pragma once + +#include "glaze/core/common.hpp" + +namespace glz +{ + // Test that two meta objects are equal, with epsilon support for floating point values + struct approx_equal_to final + { + template + constexpr bool operator()(T&& lhs, T&& rhs) noexcept + { + constexpr auto N = std::tuple_size_v>; + + bool equal = true; + for_each([&](auto I) { + auto& l = detail::get_member(lhs, get<1>(get(meta_v))); + auto& r = detail::get_member(rhs, get<1>(get(meta_v))); + using V = std::decay_t; + if constexpr (std::floating_point && requires { meta>::compare_epsilon; }) { + if (std::abs(l - r) >= meta>::compare_epsilon) { + equal = false; + } + } + else { + if (l != r) { + equal = false; + } + } + }); + + return equal; + } + }; +} diff --git a/src/core/include/glaze/compare/compare.hpp b/src/core/include/glaze/compare/compare.hpp new file mode 100644 index 00000000..028c63d8 --- /dev/null +++ b/src/core/include/glaze/compare/compare.hpp @@ -0,0 +1,116 @@ +// Glaze Library +// For the license information refer to glaze.hpp + +#pragma once + +#include + +#include "glaze/core/common.hpp" + +namespace glz +{ + struct equal_to final + { + template + constexpr bool operator()(T&& lhs, T&& rhs) noexcept + { + if constexpr (std::equality_comparable) { + return lhs == rhs; + } + else { + constexpr auto N = std::tuple_size_v>; + + bool equal = true; + for_each([&](auto I) { + auto& l = detail::get_member(lhs, get<1>(get(meta_v))); + auto& r = detail::get_member(rhs, get<1>(get(meta_v))); + if (!std::equal_to{}(l, r)) { + equal = false; + } + }); + + return equal; + } + } + }; + + struct less final + { + template + constexpr bool operator()(T&& lhs, T&& rhs) noexcept + { + constexpr auto N = std::tuple_size_v>; + + bool less_than = true; + for_each([&](auto I) { + auto& l = detail::get_member(lhs, get<1>(get(meta_v))); + auto& r = detail::get_member(rhs, get<1>(get(meta_v))); + if (!std::less{}(l, r)) { + less_than = false; + } + }); + + return less_than; + } + }; + + struct less_equal final + { + template + constexpr bool operator()(T&& lhs, T&& rhs) noexcept + { + constexpr auto N = std::tuple_size_v>; + + bool less_than = true; + for_each([&](auto I) { + auto& l = detail::get_member(lhs, get<1>(get(meta_v))); + auto& r = detail::get_member(rhs, get<1>(get(meta_v))); + if (!std::less_equal{}(l, r)) { + less_than = false; + } + }); + + return less_than; + } + }; + + struct greater final + { + template + constexpr bool operator()(T&& lhs, T&& rhs) noexcept + { + constexpr auto N = std::tuple_size_v>; + + bool greater_than = true; + for_each([&](auto I) { + auto& l = detail::get_member(lhs, get<1>(get(meta_v))); + auto& r = detail::get_member(rhs, get<1>(get(meta_v))); + if (!std::greater{}(l, r)) { + greater_than = false; + } + }); + + return greater_than; + } + }; + + struct greater_equal final + { + template + constexpr bool operator()(T&& lhs, T&& rhs) noexcept + { + constexpr auto N = std::tuple_size_v>; + + bool greater_than = true; + for_each([&](auto I) { + auto& l = detail::get_member(lhs, get<1>(get(meta_v))); + auto& r = detail::get_member(rhs, get<1>(get(meta_v))); + if (!std::greater_equal{}(l, r)) { + greater_than = false; + } + }); + + return greater_than; + } + }; +} diff --git a/src/core/include/glaze/core/common.hpp b/src/core/include/glaze/core/common.hpp index 06677598..5289fb7d 100644 --- a/src/core/include/glaze/core/common.hpp +++ b/src/core/include/glaze/core/common.hpp @@ -19,10 +19,8 @@ #include "glaze/core/meta.hpp" #include "glaze/util/bit_array.hpp" #include "glaze/util/expected.hpp" -#include "glaze/util/fixed_string.hpp" #include "glaze/util/for_each.hpp" #include "glaze/util/hash_map.hpp" -#include "glaze/util/murmur.hpp" #include "glaze/util/string_view.hpp" #include "glaze/util/tuple.hpp" #include "glaze/util/type_traits.hpp" @@ -50,6 +48,7 @@ namespace glz struct obj final { glz::tuplet::tuple, sv>, sv, T>...> value; + static constexpr auto glaze_reflect = false; }; template @@ -59,6 +58,7 @@ namespace glz struct obj_copy final { glz::tuplet::tuple value; + static constexpr auto glaze_reflect = false; }; template @@ -68,6 +68,7 @@ namespace glz struct arr final { glz::tuplet::tuple, sv>, sv, T>...> value; + static constexpr auto glaze_reflect = false; }; template @@ -87,6 +88,7 @@ namespace glz struct merge final { glz::tuplet::tuple, sv>, sv, T>...> value; + static constexpr auto glaze_reflect = false; }; template @@ -126,6 +128,9 @@ namespace glz struct includer { T& value; + + static constexpr auto glaze_includer = true; + static constexpr auto glaze_reflect = false; }; template @@ -135,17 +140,27 @@ namespace glz }; // Register this with an object to allow file including (direct writes) to the meta object - struct file_include + struct file_include final { - constexpr decltype(auto) operator()(auto&& value) const { return includer>{value}; } + bool reflection_helper{}; // needed for count_members + static constexpr auto glaze_includer = true; + static constexpr auto glaze_reflect = false; + + constexpr decltype(auto) operator()(auto&& value) const noexcept + { + return includer>{value}; + } }; + template + concept is_includer = requires(T t) { requires T::glaze_includer == true; }; + template concept range = requires(T& t) { - requires !std::same_as; - requires !std::same_as; - requires std::input_iterator; - }; + requires !std::same_as; + requires !std::same_as; + requires std::input_iterator; + }; // range like template @@ -155,26 +170,30 @@ namespace glz using range_value_t = std::iter_value_t>; template - [[nodiscard]] bool empty_range(const R& rng) + [[nodiscard]] constexpr bool empty_range(R&& rng) { +#ifdef __cpp_lib_ranges + return std::ranges::empty(rng); +#else // in lieu of std::ranges::empty if constexpr (requires() { { rng.empty() - } -> std::convertible_to; + } -> std::convertible_to; }) { return rng.empty(); } else if constexpr (requires() { { rng.size() - } -> std::same_as; + } -> std::same_as; }) { - return rng.size() == 0; + return rng.size() == std::size_t{0}; } else { return std::cbegin(rng) == std::cend(rng); } +#endif } template @@ -292,36 +311,35 @@ namespace glz std::same_as, bool> || std::same_as, std::vector::reference>; template - concept int_t = std::integral> && ! - char_t> && !bool_t; + concept int_t = std::integral> && !char_t> && !bool_t; template concept num_t = std::floating_point> || int_t; template concept complex_t = requires(T a, T b) { - { - a.real() - } -> std::convertible_to; - { - a.imag() - } -> std::convertible_to; - { - T(a.real(), a.imag()) - } -> std::same_as; - { - a + b - } -> std::same_as; - { - a - b - } -> std::same_as; - { - a* b - } -> std::same_as; - { - a / b - } -> std::same_as; - }; + { + a.real() + } -> std::convertible_to; + { + a.imag() + } -> std::convertible_to; + { + T(a.real(), a.imag()) + } -> std::same_as; + { + a + b + } -> std::same_as; + { + a - b + } -> std::same_as; + { + a* b + } -> std::same_as; + { + a / b + } -> std::same_as; + }; template concept constructible = requires { meta>::construct; } || local_construct_t>; @@ -330,16 +348,17 @@ namespace glz concept meta_value_t = glaze_t>; template - concept str_t = ! - std::same_as&& std::convertible_to, std::string_view>; + concept str_t = !std::same_as && std::convertible_to, std::string_view>; template concept has_push_back = requires(T t, typename T::value_type v) { t.push_back(v); }; + template + concept has_reserve = requires(T t) { t.reserve(size_t(1)); }; + // this concept requires that T is string and copies the string in json template - concept string_t = str_t && ! - std::same_as, std::string_view>&& has_push_back; + concept string_t = str_t && !std::same_as, std::string_view> && has_push_back; template concept char_array_t = str_t && std::is_array_v>>; @@ -350,39 +369,39 @@ namespace glz template concept pair_t = requires(T pair) { - { - pair.first - } -> std::same_as; - { - pair.second - } -> std::same_as; - }; + { + pair.first + } -> std::same_as; + { + pair.second + } -> std::same_as; + }; template concept map_subscriptable = requires(T container) { - { - container[std::declval()] - } -> std::same_as; - }; + { + container[std::declval()] + } -> std::same_as; + }; template - concept readable_map_t = ! - custom_read && !meta_value_t && !str_t && range && pair_t> && map_subscriptable; + concept readable_map_t = !custom_read && !meta_value_t && !str_t && range && + pair_t> && map_subscriptable; template - concept writable_map_t = ! - custom_write && !meta_value_t && !str_t && range && pair_t>; + concept writable_map_t = + !custom_write && !meta_value_t && !str_t && range && pair_t>; template concept heterogeneous_map = requires { - typename Map::key_compare; - requires(std::same_as> || - std::same_as> || - requires { typename Map::key_compare::is_transparent; }); - }; + typename Map::key_compare; + requires(std::same_as> || + std::same_as> || + requires { typename Map::key_compare::is_transparent; }); + }; template - concept array_t = (!meta_value_t && !str_t && !(readable_map_t || writable_map_t) && range); + concept array_t = (!meta_value_t && !str_t && !(readable_map_t || writable_map_t)&&range); template concept readable_array_t = (!custom_read && array_t); @@ -392,24 +411,24 @@ namespace glz template concept emplace_backable = requires(T container) { - { - container.emplace_back() - } -> std::same_as; - }; + { + container.emplace_back() + } -> std::same_as; + }; template concept emplaceable = requires(T container) { - { - container.emplace(std::declval()) - }; - }; + { + container.emplace(std::declval()) + }; + }; template concept push_backable = requires(T container) { - { - container.push_back(std::declval()) - }; - }; + { + container.push_back(std::declval()) + }; + }; template concept resizeable = requires(T container) { container.resize(0); }; @@ -418,18 +437,18 @@ namespace glz concept erasable = requires(T container) { container.erase(container.cbegin(), container.cend()); }; template - concept fixed_array_value_t = array_t()[0])>> && ! - resizeable()[0])>>; + concept fixed_array_value_t = array_t()[0])>> && + !resizeable()[0])>>; template concept has_size = requires(T container) { container.size(); }; template concept has_empty = requires(T container) { - { - container.empty() - } -> std::convertible_to; - }; + { + container.empty() + } -> std::convertible_to; + }; template concept has_data = requires(T container) { container.data(); }; @@ -439,10 +458,10 @@ namespace glz template concept accessible = requires(T container) { - { - container[size_t{}] - } -> std::same_as; - }; + { + container[size_t{}] + } -> std::same_as; + }; template concept boolean_like = std::same_as || std::same_as::reference> || @@ -453,20 +472,40 @@ namespace glz template concept is_span = requires(T t) { - T::extent; - typename T::element_type; - }; + T::extent; + typename T::element_type; + }; + + template + concept is_no_reflect = requires(T t) { requires T::glaze_reflect == false; }; template concept is_dynamic_span = T::extent == static_cast(-1); template - concept has_static_size = (is_span && !is_dynamic_span) || - (requires(T container) { - { - std::bool_constant<(std::decay_t{}.size(), true)>() - } -> std::same_as; - } && std::decay_t{}.size() > 0); + concept has_static_size = (is_span && !is_dynamic_span) || (requires(T container) { + { + std::bool_constant<(std::decay_t{}.size(), true)>() + } -> std::same_as; + } && std::decay_t{}.size() > 0); + + template + concept is_bitset = requires(T bitset) { + bitset.flip(); + bitset.set(0); + { + bitset.to_string() + } -> std::same_as; + { + bitset.count() + } -> std::same_as; + }; + + template + concept is_float128 = requires(T x) { + requires sizeof(x) == 16; + requires std::floating_point; + }; template constexpr size_t get_size() noexcept @@ -484,23 +523,21 @@ namespace glz template concept tuple_t = requires(T t) { - std::tuple_size::value; - glz::tuplet::get<0>(t); - } && ! - meta_value_t && !range; + std::tuple_size::value; + glz::get<0>(t); + } && !meta_value_t && !range; template concept always_null_t = std::same_as || std::convertible_to || std::same_as; template - concept nullable_t = ! - meta_value_t && !str_t && requires(T t) { - bool(t); - { - *t - }; - }; + concept nullable_t = !meta_value_t && !str_t && requires(T t) { + bool(t); + { + *t + }; + }; template concept raw_nullable = is_specialization_v && requires { requires nullable_t; }; @@ -510,10 +547,9 @@ namespace glz template concept func_t = requires(T t) { - typename T::result_type; - std::function(t); - } && ! - glaze_t; + typename T::result_type; + std::function(t); + } && !glaze_t; template concept glaze_array_t = glaze_t && is_specialization_v, Array>; @@ -531,15 +567,24 @@ namespace glz concept glaze_value_t = glaze_t && !(glaze_array_t || glaze_object_t || glaze_enum_t || glaze_flags_t); + template + concept reflectable = std::is_aggregate_v> && std::is_class_v> && + !(is_no_reflect || glaze_value_t || glaze_object_t || glaze_array_t || + glaze_flags_t || range || pair_t || null_t); + + template + concept glaze_const_value_t = glaze_value_t && std::is_pointer_v> && + std::is_const_v>>; + template concept non_narrowing_convertable = requires(From from, To to) { #if __GNUC__ - // TODO: guard gcc against narrowing conversions when fixed - to = from; + // TODO: guard gcc against narrowing conversions when fixed + to = from; #else - To{from}; + To{from}; #endif - }; + }; // from // https://stackoverflow.com/questions/55941964/how-to-filter-duplicate-types-from-tuple-c @@ -579,11 +624,18 @@ namespace glz template ::value>> struct value_tuple_variant; + template + struct member_type + { + using T0 = std::decay_t>>; + using type = std::tuple_element_t ? 0 : 1, std::tuple_element_t>; + }; + template struct value_tuple_variant> { using type = typename tuple_variant>>>()...))>::type; + std::declval::type>>()...))>::type; }; template @@ -593,7 +645,7 @@ namespace glz inline constexpr auto make_array_impl(std::index_sequence) { using value_t = typename tuple_variant>::type; - return std::array>>{glz::tuplet::get(meta_v)...}; + return std::array>>{glz::get(meta_v)...}; } template @@ -614,7 +666,7 @@ namespace glz return &std::get(t); } else { - return &glz::tuplet::get(t); + return &glz::get(t); } }...}; } @@ -628,10 +680,63 @@ namespace glz return runtime_getter[index](t); } + template + constexpr auto key_value() noexcept + { + using value_t = value_tuple_variant_t>; + constexpr auto first = get<0>(get(meta_v)); + using T0 = std::decay_t; + if constexpr (std::is_member_object_pointer_v) { + return std::pair{get_name(), first}; + } + else { + return std::pair{sv(first), get<1>(get(meta_v))}; + } + } + + template + constexpr sv get_key() noexcept + { + constexpr auto first = get<0>(get(meta_v)); + using T0 = std::decay_t; + if constexpr (std::is_member_object_pointer_v) { + return get_name(); + } + else { + return {first}; + } + } + + template + constexpr sv get_enum_key() noexcept + { + constexpr auto first = get<0>(get(meta_v)); + using T0 = std::decay_t; + if constexpr (std::is_enum_v) { + return get_name(); + } + else { + return {first}; + } + } + + template + constexpr auto get_enum_value() noexcept + { + constexpr auto first = get<0>(get(meta_v)); + using T0 = std::decay_t; + if constexpr (std::is_enum_v) { + return first; + } + else { + return get<1>(get(meta_v)); + } + } + template struct meta_sv { - static constexpr sv value = glz::tuplet::get<0>(glz::tuplet::get(meta_v)); + static constexpr sv value = get_key(); }; template @@ -640,74 +745,62 @@ namespace glz using value_t = value_tuple_variant_t>; constexpr auto n = std::tuple_size_v>; - auto naive_or_normal_hash = [&] { - if constexpr (n <= 20) { - return glz::detail::naive_map( - {std::pair{sv(glz::tuplet::get<0>(glz::tuplet::get(meta_v))), - glz::tuplet::get<1>(glz::tuplet::get(meta_v))}...}); - } - else { - return glz::detail::normal_map( - {std::pair{sv(glz::tuplet::get<0>(glz::tuplet::get(meta_v))), - glz::tuplet::get<1>(glz::tuplet::get(meta_v))}...}); - } - }; - if constexpr (n == 0) { - static_assert(false_v, "Empty object map is illogical. Handle empty upstream."); + return nullptr; // Hack to fix MSVC + // static_assert(false_v, "Empty object map is illogical. Handle empty upstream."); } else if constexpr (n == 1) { - return micro_map1::value...>{ - std::make_pair(sv(glz::tuplet::get<0>(glz::tuplet::get(meta_v))), - glz::tuplet::get<1>(glz::tuplet::get(meta_v)))...}; + return micro_map1::value...>{key_value()...}; } else if constexpr (n == 2) { - return micro_map2::value...>{ - std::make_pair(sv(glz::tuplet::get<0>(glz::tuplet::get(meta_v))), - glz::tuplet::get<1>(glz::tuplet::get(meta_v)))...}; + return micro_map2::value...>{key_value()...}; } - else if constexpr (n < 128) // don't even attempt a first character hash if we have too many keys + else if constexpr (n < 64) // don't even attempt a first character hash if we have too many keys { - constexpr auto front_desc = - single_char_hash(std::array{sv{glz::tuplet::get<0>(glz::tuplet::get(meta_v))}...}); + constexpr auto front_desc = single_char_hash(std::array{get_key()...}); if constexpr (front_desc.valid) { - return make_single_char_map( - {std::make_pair(sv(glz::tuplet::get<0>(glz::tuplet::get(meta_v))), - glz::tuplet::get<1>(glz::tuplet::get(meta_v)))...}); + return make_single_char_map({key_value()...}); } else { - constexpr auto back_desc = single_char_hash( - std::array{sv{glz::tuplet::get<0>(glz::tuplet::get(meta_v))}...}); + constexpr auto back_desc = single_char_hash(std::array{get_key()...}); if constexpr (back_desc.valid) { - return make_single_char_map( - {std::make_pair(sv(glz::tuplet::get<0>(glz::tuplet::get(meta_v))), - glz::tuplet::get<1>(glz::tuplet::get(meta_v)))...}); + return make_single_char_map({key_value()...}); } else { - return naive_or_normal_hash(); + if constexpr (n <= 20) { + return glz::detail::naive_map({key_value()...}); + } + else { + return glz::detail::normal_map({key_value()...}); + } } } } else { - return naive_or_normal_hash(); + if constexpr (n <= 20) { + return glz::detail::naive_map({key_value()...}); + } + else { + return glz::detail::normal_map({key_value()...}); + } } } template + requires(!reflectable) constexpr auto make_map() { constexpr auto indices = std::make_index_sequence>>{}; return make_map_impl, use_hash_comparison>(indices); } - template + /*template constexpr auto make_int_storage_impl(std::index_sequence) { using value_t = value_tuple_variant_t>; - return std::array>>( - {glz::tuplet::get<1>(glz::tuplet::get(meta_v))...}); + return std::array>>({glz::get<1>(glz::get(meta_v))...}); } template @@ -715,13 +808,13 @@ namespace glz { constexpr auto indices = std::make_index_sequence>>{}; return make_int_storage_impl(indices); - } + }*/ template constexpr auto make_key_int_map_impl(std::index_sequence) { return normal_map>>( - {std::make_pair(glz::tuplet::get<0>(glz::tuplet::get(meta_v)), I)...}); + {std::make_pair(get_enum_key(), I)...}); } template @@ -731,31 +824,12 @@ namespace glz return make_key_int_map_impl(indices); } - template - constexpr auto make_crusher_map_impl(std::index_sequence) - { - using value_t = value_tuple_variant_t>; - constexpr auto n = std::tuple_size_v>; - - return normal_map( - {std::make_pair(murmur3_32(glz::tuplet::get<0>(glz::tuplet::get(meta_v))), - glz::tuplet::get<1>(glz::tuplet::get(meta_v)))...}); - } - - template - constexpr auto make_crusher_map() - { - constexpr auto indices = std::make_index_sequence>>{}; - return make_crusher_map_impl(indices); - } - template constexpr auto make_enum_to_string_map_impl(std::index_sequence) { using key_t = std::underlying_type_t; return normal_map>>( - {std::make_pair(static_cast(glz::tuplet::get<1>(glz::tuplet::get(meta_v))), - sv(glz::tuplet::get<0>(glz::tuplet::get(meta_v))))...}); + {std::make_pair(static_cast(get_enum_value()), get_enum_key())...}); } template @@ -770,8 +844,7 @@ namespace glz constexpr auto make_enum_to_string_array() { std::array>> arr; - for_each>>( - [&](auto I) { arr[I] = enum_name_v(decltype(I)::value)>; }); + for_each>>([&](auto I) { arr[I] = get_enum_key(); }); return arr; } @@ -779,8 +852,7 @@ namespace glz constexpr auto make_string_to_enum_map_impl(std::index_sequence) { return normal_map>>( - {std::make_pair(sv(glz::tuplet::get<0>(glz::tuplet::get(meta_v))), - T(glz::tuplet::get<1>(glz::tuplet::get(meta_v))))...}); + {std::make_pair(get_enum_key(), T(get_enum_value()))...}); } template @@ -790,30 +862,67 @@ namespace glz return make_string_to_enum_map_impl(indices); } + // get a std::string_view from an enum value + template + requires(detail::glaze_t && std::is_enum_v>) + constexpr auto get_enum_name(T&& enum_value) + { + using V = std::decay_t; + using U = std::underlying_type_t; + constexpr auto arr = glz::detail::make_enum_to_string_array(); + return arr[static_cast(enum_value)]; + } + + template + constexpr size_t get_max_keys = [] { + size_t res{}; + for_each([&](auto I) { + using V = std::decay_t>; + if constexpr (reflectable) { + res += count_members; + } + else { + res += std::tuple_size_v>; + } + }); + return res; + }(); + template constexpr auto get_combined_keys_from_variant() { constexpr auto N = std::variant_size_v; - constexpr size_t max_keys = []() { - size_t res{}; - for_each([&](auto I) { - using V = std::decay_t>; - res += std::tuple_size_v>; - }); - return res; - }(); - std::array data{}; + std::array> data{}; + auto* data_ptr = + &data; // This intermediate pointer is necessary for GCC 13 (otherwise segfaults with reflection logic) size_t index = 0; for_each([&](auto I) { using V = std::decay_t>; - for_each>>( - [&](auto J) { data[index++] = glz::tuplet::get<0>(glz::tuplet::get(meta_v)); }); + if constexpr (reflectable) { + for_each)>>( + [&](auto J) { (*data_ptr)[index++] = glz::get(member_names); }); + } + else { + for_each>>([&](auto J) { + constexpr auto item = get(meta_v); + using T0 = std::decay_t(item))>; + auto key_getter = [&] { + if constexpr (std::is_member_object_pointer_v) { + return get_name(get(meta_v))>(); + } + else { + return get<0>(get(meta_v)); + } + }; + (*data_ptr)[index++] = key_getter(); + }); + } }); - std::sort(data.data(), data.data() + max_keys); - const auto end = std::unique(data.data(), data.data() + max_keys); - const auto size = std::distance(data.data(), end); + std::sort(data.begin(), data.end()); + const auto end = std::unique(data.begin(), data.end()); + const auto size = std::distance(data.begin(), end); return std::pair{data, size}; } @@ -836,9 +945,25 @@ namespace glz constexpr auto N = std::variant_size_v; for_each([&](auto I) { using V = std::decay_t>; - for_each>>([&](auto J) { - deduction_map.find(glz::tuplet::get<0>(glz::tuplet::get(meta_v)))->second[I] = true; - }); + if constexpr (reflectable) { + for_each)>>( + [&](auto J) { deduction_map.find(get(member_names))->second[I] = true; }); + } + else { + for_each>>([&](auto J) { + constexpr auto item = get(meta_v); + using T0 = std::decay_t(item))>; + auto key_getter = [&] { + if constexpr (std::is_member_object_pointer_v) { + return get_name(get(meta_v))>(); + } + else { + return get<0>(get(meta_v)); + } + }; + deduction_map.find(key_getter())->second[I] = true; + }); + } }); return deduction_map; @@ -858,7 +983,8 @@ namespace glz return make_variant_id_map_impl(indices, ids_v); } - inline decltype(auto) get_member(auto&& value, auto&& member_ptr) + template + inline decltype(auto) get_member(Value&& value, MemPtr&& member_ptr) { using V = std::decay_t; if constexpr (std::is_member_object_pointer_v) { @@ -867,11 +993,16 @@ namespace glz else if constexpr (std::is_member_function_pointer_v) { return member_ptr; } - else if constexpr (std::invocable) { - return std::invoke(member_ptr, value); + else if constexpr (std::invocable) { + return std::invoke(std::forward(member_ptr), std::forward(value)); } else if constexpr (std::is_pointer_v) { - return *member_ptr; + if constexpr (std::invocable) { + return std::invoke(*member_ptr, std::forward(value)); + } + else { + return *member_ptr; + } } else { return member_ptr; @@ -901,8 +1032,7 @@ namespace glz inline constexpr auto members_from_meta_impl() { if constexpr (glaze_object_t>) { - return glz::tuplet::tuple< - std::decay_t>>>>...>{}; + return glz::tuplet::tuple, I>::type>>...>{}; } else { return glz::tuplet::tuple{}; @@ -933,21 +1063,6 @@ namespace glz {}; template array_variant(T) -> array_variant; // Only needed on older compilers until we move to template alias deduction - - template - constexpr auto required_fields() - { - constexpr auto n = std::tuple_size_v>; - bit_array fields{}; - if constexpr (Opts.error_on_missing_keys) { - for_each([&](auto I) constexpr { - fields[I] = - !bool(Opts.skip_null_members) || - !null_t>>>>>; - }); - } - return fields; - } } // namespace detail constexpr decltype(auto) conv_sv(auto&& value) noexcept @@ -1040,12 +1155,27 @@ struct glz::meta "elements_not_convertible_to_design", glz::error_code::elements_not_convertible_to_design, // "unknown_distribution", glz::error_code::unknown_distribution, // "invalid_distribution_elements", glz::error_code::invalid_distribution_elements, // - "missing_key", glz::error_code::missing_key // + "missing_key", glz::error_code::missing_key, // + "hostname_failure", glz::error_code::hostname_failure // ); }; namespace glz { + template + requires(std::is_enum_v) + constexpr sv enum_name_v = []() -> std::string_view { + using T = std::decay_t; + + if constexpr (detail::glaze_t) { + using U = std::underlying_type_t; + return detail::get_enum_key(Enum)>(); + } + else { + return "glz::unknown"; + } + }(); + [[nodiscard]] inline std::string format_error(const parse_error& pe, const auto& buffer) { static constexpr auto arr = detail::make_enum_to_string_array(); @@ -1058,3 +1188,127 @@ namespace glz return std::string(error_type_str); } } + +namespace glz::detail +{ + // This useless code and the inclusion of N is required for MSVC to build, but not Clang or GCC + template + struct glaze_tuple_element + { + using V = std::decay_t; + using Item = tuplet::tuple<>; + using T0 = T; + static constexpr bool use_reflection = false; + static constexpr size_t member_index = 0; + using mptr_t = T; + using type = T; + }; + + // This shouldn't need the requires or the N template paramter, except for the current MSVC + template + requires(N > 0 && !reflectable) + struct glaze_tuple_element + { + using V = std::decay_t; + using Item = std::decay_t(meta_v))>; + using T0 = std::decay_t>; + static constexpr bool use_reflection = std::is_member_object_pointer_v; + static constexpr size_t member_index = use_reflection ? 0 : 1; + using mptr_t = std::decay_t>; + using type = member_t; + }; + + template + requires(N > 0) + struct glaze_tuple_element + { + using V = std::decay_t; + static constexpr bool use_reflection = false; + static constexpr size_t member_index = 0; + using Item = decltype(to_tuple(std::declval())); + using mptr_t = std::tuple_element_t; + using type = member_t; + using T0 = mptr_t; + }; + + template + using glaze_tuple_element_t = typename glaze_tuple_element::type; + + template + struct object_type_info + { + using V = std::decay_t; + + static constexpr auto N = [] { + if constexpr (reflectable) { + return count_members; + } + else { + return std::tuple_size_v>; + } + }(); + + // Allows us to remove a branch if the first item will always be written + static constexpr bool first_will_be_written = [] { + if constexpr (N > 0) { + using val_t = glaze_tuple_element_t<0, N, T>; + + if constexpr (null_t && Opts.skip_null_members) { + return false; + } + + // skip file_include + if constexpr (is_includer) { + return false; + } + else if constexpr (std::is_same_v