diff --git a/README.md b/README.md index 393213cc52..5ec6b2a05e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Glaze also supports: > Glaze `main` branch is currently in development for v5.0.0 > > v5.0.0 will remove the `detail` namespace from `to/from` specializations and many more internal functions, which will enable cleaner user customization and shorter compilation error messages. +> +> The `glz::opts` struct is also becoming the default options, and more specialized options can be added to custom option structs. See [Options](./docs/options.md) for new compile time option customization. This reduces the size of compiler errors and makes compilation faster. ## With compile time reflection for MSVC, Clang, and GCC! @@ -772,7 +774,7 @@ buffer.resize(n); ## Compile Time Options -The `glz::opts` struct defines compile time optional settings for reading/writing. +The `glz::opts` struct defines the default compile time options for reading/writing. Instead of calling `glz::read_json(...)`, you can call `glz::read(...)` and customize the options. @@ -783,13 +785,20 @@ For example: `glz::read(...)` will tu - `glz::read(...)` -> `glz::read_beve(...)` - `glz::read(...)` -> `glz::read_json(...)` -## Available Compile Time Options +> [!IMPORTANT] +> +> Many options for Glaze are not part of `glz::opts`. This keeps compiler errors shorter and makes options more manageable. See [Options](./docs/options.md) documentation for more details on available compile time options. + +### Available Default Compile Time Options -The struct below shows the available options and the default behavior. +The struct below shows the available options in `glz::opts` and the defaults. See [Options](./docs/options.md) for additional options for user customization. ```c++ -struct opts { - uint32_t format = json; +struct opts +{ + // USER CONFIGURABLE + uint32_t format = JSON; + bool null_terminated = true; // Whether the input buffer is null terminated bool comments = false; // Support reading in JSONC style comments bool error_on_unknown_keys = true; // Error when an unknown key is encountered bool skip_null_members = true; // Skip writing out params in an object if the value is null @@ -799,15 +808,13 @@ struct opts { char indentation_char = ' '; // Prettified JSON indentation char uint8_t indentation_width = 3; // Prettified JSON indentation size bool new_lines_in_arrays = true; // Whether prettified arrays should have new lines for each element + bool append_arrays = false; // When reading into an array the data will be appended if the type supports it bool shrink_to_fit = false; // Shrinks dynamic containers to new size to save memory bool write_type_info = true; // Write type info for meta objects in variants bool error_on_missing_keys = false; // Require all non nullable keys to be present in the object. Use - // skip_null_members = false to require nullable members + // skip_null_members = false to require nullable members bool error_on_const_read = false; // Error if attempt is made to read into a const value, by default the value is skipped without error - bool validate_skipped = false; // If full validation should be performed on skipped values - bool validate_trailing_whitespace = - false; // If, after parsing a value, we want to validate the trailing whitespace uint8_t layout = rowwise; // CSV row wise output/input @@ -817,22 +824,13 @@ struct opts { bool bools_as_numbers = false; // Read and write booleans with 1's and 0's bool quoted_num = false; // treat numbers as quoted or array-like types as having quoted numbers - bool number = false; // read numbers as strings and write these string as numbers + bool number = false; // treats all types like std::string as numbers: read/write these quoted numbers bool raw = false; // write out string like values without quotes - bool raw_string = - false; // do not decode/encode escaped characters for strings (improves read/write performance) + bool raw_string = false; // do not decode/encode escaped characters for strings (improves read/write performance) bool structs_as_arrays = false; // Handle structs (reading/writing) without keys, which applies - bool allow_conversions = true; // Whether conversions between convertible types are - // allowed in binary, e.g. double -> float bool partial_read = false; // Reads into the deepest structural object and then exits without parsing the rest of the input - - // glaze_object_t concepts - bool concatenate = true; // Concatenates ranges of std::pair into single objects when writing - - bool hide_non_invocable = - true; // Hides non-invocable members from the cli_menu (may be applied elsewhere in the future) }; ``` diff --git a/docs/options.md b/docs/options.md new file mode 100644 index 0000000000..7b830d58b7 --- /dev/null +++ b/docs/options.md @@ -0,0 +1,67 @@ +# Compile Time Options + +Glaze has a large number of available compile time options. These options are passed as a non-type template parameter to read/write calls. + +In order to keep template errors more succinct and allow more customization, Glaze allows users to build their own option structs. + +Example: + +```c++ +struct custom_opts : glz::opts // Inherit from glz::opts to get basic options +{ + // Add known Glaze options that do not exist in the base glz::opts + bool validate_trailing_whitespace = true; +}; +``` + +In use: + +```c++ +// Using custom_opts while specifying a base option (prettify) +glz::write(obj); +``` + +## How Custom Options Work + +Glaze uses C++20 concepts to detect if an option exists in the provided options struct, if it doesn't exist, a default value is returned. + +```c++ +// Code from Glaze demonstrating compile time option check: +consteval bool check_validate_trailing_whitespace(auto&& Opts) { + if constexpr (requires { Opts.validate_trailing_whitespace; }) { + return Opts.validate_trailing_whitespace; + } else { + return false; // Default value (may be different for various options) + } +} +``` + +## Available Options Outside of glz::opts + +In `glaze/core/opts.hpp` you can see the default provided fields in `glz::opts`. + +The supported, but not default provided options are listed after `glz::opts` and include: + +```c++ +bool validate_skipped = false; +// If full validation should be performed on skipped values + +bool validate_trailing_whitespace = false; +// If, after parsing a value, we want to validate the trailing whitespace + +bool concatenate = true; +// Concatenates ranges of std::pair into single objects when writing + +bool allow_conversions = true; +// Whether conversions between convertible types are allowed in binary, e.g. double -> float + +bool write_type_info = true; +// Write type info for meta objects in variants + +bool shrink_to_fit = false; +// Shrinks dynamic containers to new size to save memory + +bool hide_non_invocable = true; +// Hides non-invocable members from the cli_menu (may be applied elsewhere in the future) +``` + diff --git a/include/glaze/beve/beve_to_json.hpp b/include/glaze/beve/beve_to_json.hpp index 34cd7a51fd..0c51183016 100644 --- a/include/glaze/beve/beve_to_json.hpp +++ b/include/glaze/beve/beve_to_json.hpp @@ -10,7 +10,7 @@ namespace glz { namespace detail { - template + template inline void beve_to_json_number(auto&& tag, auto&& ctx, auto&& it, auto&& end, auto& out, auto&& ix) noexcept { const auto number_type = (tag & 0b000'11'000) >> 3; @@ -104,7 +104,7 @@ namespace glz } } - template + template inline void beve_to_json_value(auto&& ctx, auto&& it, auto&& end, Buffer& out, auto&& ix) { if (it >= end) [[unlikely]] { @@ -637,7 +637,7 @@ namespace glz } } - template + template [[nodiscard]] inline error_ctx beve_to_json(const BEVEBuffer& beve, JSONBuffer& out) { size_t ix{}; // write index diff --git a/include/glaze/beve/read.hpp b/include/glaze/beve/read.hpp index 2b1636775b..ca7ed8f0a4 100644 --- a/include/glaze/beve/read.hpp +++ b/include/glaze/beve/read.hpp @@ -196,7 +196,7 @@ namespace glz constexpr auto is_volatile = std::is_volatile_v>; if (tag != header) { - if constexpr (Opts.allow_conversions) { + if constexpr (check_allow_conversions(Opts)) { if constexpr (num_t) { if ((tag & 0b00000111) != tag::number) { ctx.error = error_code::syntax_error; @@ -728,7 +728,7 @@ namespace glz if constexpr (resizable) { value.resize(n); - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } } @@ -772,7 +772,7 @@ namespace glz if constexpr (resizable) { value.resize(n); - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } } @@ -787,9 +787,9 @@ namespace glz }; if (tag != header) { - if constexpr (Opts.allow_conversions) { + if constexpr (check_allow_conversions(Opts)) { if (tag != header) [[unlikely]] { - if constexpr (Opts.allow_conversions) { + if constexpr (check_allow_conversions(Opts)) { if ((tag & 0b00000111) != tag::typed_array) { ctx.error = error_code::syntax_error; return; @@ -885,7 +885,7 @@ namespace glz if constexpr (resizable) { value.resize(n); - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } } @@ -902,7 +902,7 @@ namespace glz x.resize(length); - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } @@ -948,7 +948,7 @@ namespace glz if constexpr (resizable) { value.resize(n); - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } } @@ -970,7 +970,7 @@ namespace glz return; } ++it; - std::conditional_t n = int_from_compressed(ctx, it, end); + std::conditional_t n = int_from_compressed(ctx, it, end); if (bool(ctx.error)) [[unlikely]] { return; } @@ -982,7 +982,7 @@ namespace glz if constexpr (resizable) { value.resize(n); - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } } @@ -996,7 +996,7 @@ namespace glz // for types like std::vector that can't look up with operator[] // Instead of hashing or linear searching, we just clear the input and overwrite the entire contents template - requires(pair_t> && Opts.concatenate == true) + requires(pair_t> && check_concatenate(Opts) == true) static void op(auto&& value, is_context auto&& ctx, auto&& it, auto&& end) { using Element = typename T::value_type; @@ -1011,7 +1011,7 @@ namespace glz } const auto tag = uint8_t(*it); if (tag != header) [[unlikely]] { - if constexpr (Opts.allow_conversions) { + if constexpr (check_allow_conversions(Opts)) { const auto key_type = tag & 0b000'11'000; if constexpr (str_t) { if (key_type != 0) { @@ -1115,7 +1115,7 @@ namespace glz } const auto tag = uint8_t(*it); if (tag != header) [[unlikely]] { - if constexpr (Opts.allow_conversions) { + if constexpr (check_allow_conversions(Opts)) { const auto key_type = tag & 0b000'11'000; if constexpr (str_t) { if (key_type != 0) { @@ -1555,7 +1555,7 @@ namespace glz return value; } - template + template requires(read_supported) [[nodiscard]] inline error_ctx read_file_beve(T& value, const sv file_name, auto&& buffer) { @@ -1591,7 +1591,7 @@ namespace glz return value; } - template + template requires(read_supported) [[nodiscard]] inline error_ctx read_file_beve_untagged(T& value, const std::string& file_name, auto&& buffer) { diff --git a/include/glaze/beve/skip.hpp b/include/glaze/beve/skip.hpp index 7aec9b0dbf..c486113511 100644 --- a/include/glaze/beve/skip.hpp +++ b/include/glaze/beve/skip.hpp @@ -14,7 +14,7 @@ namespace glz template <> struct skip_value { - template + template inline static void op(is_context auto&& ctx, auto&& it, auto&& end) noexcept; }; @@ -44,7 +44,7 @@ namespace glz it += byte_count; } - template + template inline void skip_object_beve(is_context auto&& ctx, auto&& it, auto&& end) noexcept { if (invalid_end(ctx, it, end)) { @@ -101,7 +101,7 @@ namespace glz } } - template + template inline void skip_typed_array_beve(is_context auto&& ctx, auto&& it, auto&& end) noexcept { const auto tag = uint8_t(*it); @@ -158,7 +158,7 @@ namespace glz } } - template + template inline void skip_untyped_array_beve(is_context auto&& ctx, auto&& it, auto&& end) noexcept { ++it; @@ -172,7 +172,7 @@ namespace glz } } - template + template requires(Opts.format == BEVE) void skip_array(is_context auto&& ctx, auto&& it, auto&& end) noexcept { @@ -190,14 +190,14 @@ namespace glz } } - template + template GLZ_ALWAYS_INLINE void skip_additional_beve(is_context auto&& ctx, auto&& it, auto&& end) noexcept { ++it; skip_value::op(ctx, it, end); } - template + template inline void skip_value::op(is_context auto&& ctx, auto&& it, auto&& end) noexcept { using namespace glz::detail; diff --git a/include/glaze/beve/write.hpp b/include/glaze/beve/write.hpp index 893efb5440..298d6ba64d 100644 --- a/include/glaze/beve/write.hpp +++ b/include/glaze/beve/write.hpp @@ -486,7 +486,7 @@ namespace glz static constexpr bool map_like_array = pair_t>; template - requires(map_like_array ? Opts.concatenate == false : true) + requires(map_like_array ? check_concatenate(Opts) == false : true) static void op(auto&& value, is_context auto&& ctx, auto&&... args) { using V = range_value_t>; @@ -612,7 +612,7 @@ namespace glz } template - requires(map_like_array && Opts.concatenate == true) + requires(map_like_array && check_concatenate(Opts) == true) static auto op(auto&& value, is_context auto&& ctx, auto&&... args) { using Element = typename T::value_type; @@ -913,7 +913,7 @@ namespace glz return write(std::forward(value), std::forward(buffer)); } - template + template requires(write_supported) [[nodiscard]] glz::expected write_beve(T&& value) { @@ -928,7 +928,7 @@ namespace glz } // requires file_name to be null terminated - template + template requires(write_supported) [[nodiscard]] error_ctx write_file_beve(T&& value, const sv file_name, auto&& buffer) { @@ -966,7 +966,7 @@ namespace glz return write(std::forward(value)); } - template + template requires(write_supported) [[nodiscard]] error_ctx write_file_beve_untagged(T&& value, const std::string& file_name, auto&& buffer) { diff --git a/include/glaze/core/common.hpp b/include/glaze/core/common.hpp index b6dd87d126..9d0f6367ea 100644 --- a/include/glaze/core/common.hpp +++ b/include/glaze/core/common.hpp @@ -548,7 +548,7 @@ namespace glz template unexpected_wrapper(T*) -> unexpected_wrapper; - template + template [[nodiscard]] GLZ_ALWAYS_INLINE constexpr bool skip_member(const Value& value) noexcept { if constexpr (null_t && Opts.skip_null_members) { diff --git a/include/glaze/core/feature_test.hpp b/include/glaze/core/feature_test.hpp index 016cea14b5..fefe4b2d47 100644 --- a/include/glaze/core/feature_test.hpp +++ b/include/glaze/core/feature_test.hpp @@ -3,11 +3,15 @@ #pragma once -// Glaze Feature Test Macros +// Glaze Feature Test Macros for breaking changes +// v5.0.0 removes many internal functions and concepts out of the detail namespace to enable cleaner customization +#define glaze_v5_0_0 // v5.0.0 moves to more generic read_supported and write_supported concepts // removes concepts like `read_json_supported` and uses `read_supported` #define glaze_v5_0_0_generic_supported +// v5.0.0 makes glz::opts the default options and moves some of the compile time options out of the struct +#define glaze_v5_0_0_customized_opts // v4.3.0 removed global glz::trace #define glaze_v4_3_0_trace diff --git a/include/glaze/core/opts.hpp b/include/glaze/core/opts.hpp index 245f4a4fd4..1e55779ef3 100644 --- a/include/glaze/core/opts.hpp +++ b/include/glaze/core/opts.hpp @@ -44,6 +44,25 @@ namespace glz #ifndef GLZ_NULL_TERMINATED #define GLZ_NULL_TERMINATED true #endif + + enum struct opts_internal : uint32_t { + none = 0, + opening_handled = 1 << 0, // the opening character has been handled + closing_handled = 1 << 1, // the closing character has been handled + ws_handled = 1 << 2, // whitespace has already been parsed + no_header = 1 << 3, // whether or not a binary header is needed + disable_write_unknown = + 1 << 4, // whether to turn off writing unknown fields for a glz::meta specialized for unknown writing + is_padded = 1 << 5, // whether or not the read buffer is padded + disable_padding = 1 << 6, // to explicitly disable padding for contexts like includers + write_unchecked = 1 << 7 // the write buffer has sufficient space and does not need to be checked + }; + + // NOTE TO USER: + // glz::opts are the default options for using Glaze + // You can create your own options struct with more or less fields as long as your struct has: + // - opts_internal internal{}; + // - uint32_t format struct opts { @@ -60,15 +79,10 @@ namespace glz uint8_t indentation_width = 3; // Prettified JSON indentation size bool new_lines_in_arrays = true; // Whether prettified arrays should have new lines for each element bool append_arrays = false; // When reading into an array the data will be appended if the type supports it - bool shrink_to_fit = false; // Shrinks dynamic containers to new size to save memory - bool write_type_info = true; // Write type info for meta objects in variants bool error_on_missing_keys = false; // Require all non nullable keys to be present in the object. Use // skip_null_members = false to require nullable members bool error_on_const_read = false; // Error if attempt is made to read into a const value, by default the value is skipped without error - bool validate_skipped = false; // If full validation should be performed on skipped values - bool validate_trailing_whitespace = - false; // If, after parsing a value, we want to validate the trailing whitespace uint8_t layout = rowwise; // CSV row wise output/input @@ -82,235 +96,309 @@ namespace glz bool raw = false; // write out string like values without quotes bool raw_string = false; // do not decode/encode escaped characters for strings (improves read/write performance) bool structs_as_arrays = false; // Handle structs (reading/writing) without keys, which applies - bool allow_conversions = true; // Whether conversions between convertible types are - // allowed in binary, e.g. double -> float bool partial_read = false; // Reads into the deepest structural object and then exits without parsing the rest of the input - - // glaze_object_t concepts - bool concatenate = true; // Concatenates ranges of std::pair into single objects when writing - - bool hide_non_invocable = - true; // Hides non-invocable members from the cli_menu (may be applied elsewhere in the future) - - enum struct internal : uint32_t { - none = 0, - opening_handled = 1 << 0, // the opening character has been handled - closing_handled = 1 << 1, // the closing character has been handled - ws_handled = 1 << 2, // whitespace has already been parsed - no_header = 1 << 3, // whether or not a binary header is needed - disable_write_unknown = - 1 << 4, // whether to turn off writing unknown fields for a glz::meta specialized for unknown writing - is_padded = 1 << 5, // whether or not the read buffer is padded - disable_padding = 1 << 6, // to explicitly disable padding for contexts like includers - write_unchecked = 1 << 7 // the write buffer has sufficient space and does not need to be checked - }; - // Sufficient space is only applicable to writing certain types and based on the write_padding_bytes - - // INTERNAL USE + + // INTERNAL OPTIONS uint32_t internal{}; // default should be 0 [[nodiscard]] constexpr bool operator==(const opts&) const noexcept = default; }; + + // Add these to a custom options struct if you want to use them + // OTHER AVAILABLE OPTIONS (and default values): + + // --- + // bool validate_skipped = false; + // If full validation should be performed on skipped values + + // --- + // bool validate_trailing_whitespace = false; + // If, after parsing a value, we want to validate the trailing whitespace + + // --- + // bool concatenate = true; + // Concatenates ranges of std::pair into single objects when writing + + // --- + // bool allow_conversions = true; + // Whether conversions between convertible types are allowed in binary, e.g. double -> float + + // --- + // bool write_type_info = true; + // Write type info for meta objects in variants + + // --- + // bool shrink_to_fit = false; + // Shrinks dynamic containers to new size to save memory + + // --- + // bool hide_non_invocable = true; + // Hides non-invocable members from the cli_menu (may be applied elsewhere in the future) + + consteval bool check_validate_skipped(auto&& Opts) { + if constexpr (requires { Opts.validate_skipped; }) { + return Opts.validate_skipped; + } else { + return false; + } + } + + consteval bool check_validate_trailing_whitespace(auto&& Opts) { + if constexpr (requires { Opts.validate_trailing_whitespace; }) { + return Opts.validate_trailing_whitespace; + } else { + return false; + } + } + + consteval bool check_partial_read(auto&& Opts) { + if constexpr (requires { Opts.partial_read; }) { + return Opts.partial_read; + } else { + return false; + } + } + + consteval bool check_concatenate(auto&& Opts) { + if constexpr (requires { Opts.concatenate; }) { + return Opts.concatenate; + } else { + return true; + } + } + + consteval bool check_allow_conversions(auto&& Opts) { + if constexpr (requires { Opts.allow_conversions; }) { + return Opts.allow_conversions; + } else { + return true; + } + } + + consteval bool check_write_type_info(auto&& Opts) { + if constexpr (requires { Opts.write_type_info; }) { + return Opts.write_type_info; + } else { + return true; + } + } + + consteval bool check_shrink_to_fit(auto&& Opts) { + if constexpr (requires { Opts.shrink_to_fit; }) { + return Opts.shrink_to_fit; + } else { + return false; + } + } + + consteval bool check_hide_non_invocable(auto&& Opts) { + if constexpr (requires { Opts.hide_non_invocable; }) { + return Opts.hide_non_invocable; + } else { + return true; + } + } - consteval bool has_opening_handled(opts o) { return o.internal & uint32_t(opts::internal::opening_handled); } + // TODO: These has_ checks should probably be changed to check_ + consteval bool has_opening_handled(auto&& o) { return o.internal & uint32_t(opts_internal::opening_handled); } - consteval bool has_closing_handled(opts o) { return o.internal & uint32_t(opts::internal::closing_handled); } + consteval bool has_closing_handled(auto&& o) { return o.internal & uint32_t(opts_internal::closing_handled); } - consteval bool has_ws_handled(opts o) { return o.internal & uint32_t(opts::internal::ws_handled); } + consteval bool has_ws_handled(auto&& o) { return o.internal & uint32_t(opts_internal::ws_handled); } - consteval bool has_no_header(opts o) { return o.internal & uint32_t(opts::internal::no_header); } + consteval bool has_no_header(auto&& o) { return o.internal & uint32_t(opts_internal::no_header); } - consteval bool has_disable_write_unknown(opts o) + consteval bool has_disable_write_unknown(auto&& o) { - return o.internal & uint32_t(opts::internal::disable_write_unknown); + return o.internal & uint32_t(opts_internal::disable_write_unknown); } - consteval bool has_is_padded(opts o) { return o.internal & uint32_t(opts::internal::is_padded); } + consteval bool has_is_padded(auto&& o) { return o.internal & uint32_t(opts_internal::is_padded); } - consteval bool has_disable_padding(opts o) { return o.internal & uint32_t(opts::internal::disable_padding); } + consteval bool has_disable_padding(auto&& o) { return o.internal & uint32_t(opts_internal::disable_padding); } - consteval bool has_write_unchecked(opts o) { return o.internal & uint32_t(opts::internal::write_unchecked); } + consteval bool has_write_unchecked(auto&& o) { return o.internal & uint32_t(opts_internal::write_unchecked); } - template + template constexpr auto opening_handled() { - opts ret = Opts; - ret.internal |= uint32_t(opts::internal::opening_handled); + auto ret = Opts; + ret.internal |= uint32_t(opts_internal::opening_handled); return ret; } - template + template constexpr auto opening_and_closing_handled() { - opts ret = Opts; - ret.internal |= (uint32_t(opts::internal::opening_handled) | uint32_t(opts::internal::closing_handled)); + auto ret = Opts; + ret.internal |= (uint32_t(opts_internal::opening_handled) | uint32_t(opts_internal::closing_handled)); return ret; } - template + template constexpr auto opening_handled_off() { - opts ret = Opts; - ret.internal &= ~uint32_t(opts::internal::opening_handled); + auto ret = Opts; + ret.internal &= ~uint32_t(opts_internal::opening_handled); return ret; } - template + template constexpr auto opening_and_closing_handled_off() { - opts ret = Opts; - ret.internal &= ~(uint32_t(opts::internal::opening_handled) | uint32_t(opts::internal::closing_handled)); + auto ret = Opts; + ret.internal &= ~(uint32_t(opts_internal::opening_handled) | uint32_t(opts_internal::closing_handled)); return ret; } - template + template constexpr auto ws_handled() { - opts ret = Opts; - ret.internal |= uint32_t(opts::internal::ws_handled); + auto ret = Opts; + ret.internal |= uint32_t(opts_internal::ws_handled); return ret; } - template + template constexpr auto ws_handled_off() { - opts ret = Opts; - ret.internal &= ~uint32_t(opts::internal::ws_handled); + auto ret = Opts; + ret.internal &= ~uint32_t(opts_internal::ws_handled); return ret; } - template + template constexpr auto no_header_on() { - opts ret = Opts; - ret.internal |= uint32_t(opts::internal::no_header); + auto ret = Opts; + ret.internal |= uint32_t(opts_internal::no_header); return ret; } - template + template constexpr auto no_header_off() { - opts ret = Opts; - ret.internal &= ~uint32_t(opts::internal::no_header); + auto ret = Opts; + ret.internal &= ~uint32_t(opts_internal::no_header); return ret; } - template + template constexpr auto is_padded_on() { - opts ret = Opts; - ret.internal |= uint32_t(opts::internal::is_padded); + auto ret = Opts; + ret.internal |= uint32_t(opts_internal::is_padded); return ret; } - template + template constexpr auto is_padded_off() { - opts ret = Opts; - ret.internal &= ~uint32_t(opts::internal::is_padded); + auto ret = Opts; + ret.internal &= ~uint32_t(opts_internal::is_padded); return ret; } - template + template constexpr auto disable_padding_on() { - opts ret = Opts; - ret.internal |= uint32_t(opts::internal::disable_padding); + auto ret = Opts; + ret.internal |= uint32_t(opts_internal::disable_padding); return ret; } - template + template constexpr auto disable_padding_off() { - opts ret = Opts; - ret.internal &= ~uint32_t(opts::internal::disable_padding); + auto ret = Opts; + ret.internal &= ~uint32_t(opts_internal::disable_padding); return ret; } - template + template constexpr auto write_unchecked_on() { - opts ret = Opts; - ret.internal |= uint32_t(opts::internal::write_unchecked); + auto ret = Opts; + ret.internal |= uint32_t(opts_internal::write_unchecked); return ret; } - template + template constexpr auto write_unchecked_off() { - opts ret = Opts; - ret.internal &= ~uint32_t(opts::internal::write_unchecked); + auto ret = Opts; + ret.internal &= ~uint32_t(opts_internal::write_unchecked); return ret; } - template + template constexpr auto set_opt(auto&& value) { - opts ret = Opts; + auto ret = Opts; ret.*member_ptr = value; return ret; } - template + template constexpr auto opt_on() { - opts ret = Opts; + auto ret = Opts; ret.*member_ptr = true; return ret; } - template + template inline constexpr auto opt_true = opt_on(); - template + template constexpr auto opt_off() { - opts ret = Opts; + auto ret = Opts; ret.*member_ptr = false; return ret; } - template + template inline constexpr auto opt_false = opt_off(); - template + template constexpr auto disable_write_unknown_off() { - opts ret = Opts; - ret.internal &= ~uint32_t(opts::internal::disable_write_unknown); + auto ret = Opts; + ret.internal &= ~uint32_t(opts_internal::disable_write_unknown); return ret; } - template + template constexpr auto disable_write_unknown_on() { - opts ret = Opts; - ret.internal |= uint32_t(opts::internal::disable_write_unknown); + auto ret = Opts; + ret.internal |= uint32_t(opts_internal::disable_write_unknown); return ret; } - template + template constexpr auto set_beve() { - opts ret = Opts; + auto ret = Opts; ret.format = BEVE; return ret; } - template + template constexpr auto set_json() { - opts ret = Opts; + auto ret = Opts; ret.format = JSON; return ret; } - template + template constexpr auto set_toml() { - opts ret = Opts; + auto ret = Opts; ret.format = TOML; return ret; } diff --git a/include/glaze/core/ptr.hpp b/include/glaze/core/ptr.hpp index ce71fb7be6..46ee20b00f 100644 --- a/include/glaze/core/ptr.hpp +++ b/include/glaze/core/ptr.hpp @@ -12,7 +12,7 @@ namespace glz { // Given a JSON pointer path, reads from the buffer into the object - template + template [[nodiscard]] error_ctx read_as(T&& root_value, const sv json_ptr, B&& buffer) { error_ctx pe{}; @@ -26,7 +26,7 @@ namespace glz } // Given a JSON pointer path, writes into a buffer the specified value - template + template [[nodiscard]] bool write_as(T&& root_value, const sv json_ptr, B&& buffer) { return detail::seek_impl( diff --git a/include/glaze/core/read.hpp b/include/glaze/core/read.hpp index 7508752afc..0b891b90bd 100644 --- a/include/glaze/core/read.hpp +++ b/include/glaze/core/read.hpp @@ -11,7 +11,7 @@ namespace glz { - template + template auto read_iterators(contiguous auto&& buffer) noexcept { static_assert(sizeof(decltype(*buffer.data())) == 1); @@ -29,7 +29,7 @@ namespace glz return std::pair{it, end}; } - template + template requires read_supported [[nodiscard]] error_ctx read(T& value, contiguous auto&& buffer, is_context auto&& ctx) { @@ -70,7 +70,7 @@ namespace glz // The JSON RFC 8259 defines: JSON-text = ws value ws // So, trailing whitespace is permitted and sometimes we want to // validate this, even though this memory will not affect Glaze. - if constexpr (Opts.validate_trailing_whitespace) { + if constexpr (check_validate_trailing_whitespace(Opts)) { if (it < end) { skip_ws(ctx, it, end); if (bool(ctx.error)) [[unlikely]] { @@ -84,7 +84,7 @@ namespace glz finish: // We don't do depth validation for partial reading - if constexpr (Opts.partial_read) { + if constexpr (check_partial_read(Opts)) { if (ctx.error == error_code::partial_read_complete) [[likely]] { ctx.error = error_code::none; } @@ -106,7 +106,7 @@ namespace glz return {ctx.error, ctx.custom_error_message, size_t(it - start), ctx.includer_error}; } - template + template requires read_supported [[nodiscard]] error_ctx read(T& value, contiguous auto&& buffer) { @@ -121,7 +121,7 @@ namespace glz concept is_buffer = c_style_char_buffer || contiguous; // for char array input - template + template requires read_supported [[nodiscard]] error_ctx read(T& value, Buffer&& buffer, auto&& ctx) { @@ -132,7 +132,7 @@ namespace glz return read(value, str, ctx); } - template + template requires read_supported [[nodiscard]] error_ctx read(T& value, Buffer&& buffer) { diff --git a/include/glaze/core/reflect.hpp b/include/glaze/core/reflect.hpp index 60bb8a854e..942464524e 100644 --- a/include/glaze/core/reflect.hpp +++ b/include/glaze/core/reflect.hpp @@ -214,7 +214,7 @@ namespace glz template using field_t = std::remove_cvref_t>; - template + template inline constexpr bool maybe_skipped = [] { if constexpr (reflect::size > 0) { constexpr auto N = reflect::size; diff --git a/include/glaze/core/write.hpp b/include/glaze/core/write.hpp index 7ce6b71d29..2fd0b560e1 100644 --- a/include/glaze/core/write.hpp +++ b/include/glaze/core/write.hpp @@ -11,7 +11,7 @@ namespace glz { // For writing to a std::string, std::vector, std::deque and the like - template + template requires write_supported [[nodiscard]] error_ctx write(T&& value, Buffer& buffer, is_context auto&& ctx) { @@ -30,7 +30,7 @@ namespace glz return {ctx.error, ctx.custom_error_message}; } - template + template requires write_supported [[nodiscard]] error_ctx write(T&& value, Buffer& buffer) { @@ -49,7 +49,7 @@ namespace glz return {ctx.error, ctx.custom_error_message}; } - template + template requires write_supported [[nodiscard]] glz::expected write(T&& value, Buffer& buffer) { @@ -62,7 +62,7 @@ namespace glz return {ix}; } - template + template requires write_supported [[nodiscard]] error_ctx write(T&& value, Buffer& buffer) { @@ -70,7 +70,7 @@ namespace glz return write(std::forward(value), buffer, ctx); } - template + template requires write_supported [[nodiscard]] glz::expected write(T&& value) { @@ -83,7 +83,7 @@ namespace glz return {buffer}; } - template + template requires write_supported [[nodiscard]] glz::expected write(T&& value, Buffer&& buffer, is_context auto&& ctx) { @@ -95,7 +95,7 @@ namespace glz return {ix}; } - template + template requires write_supported [[nodiscard]] glz::expected write(T&& value, Buffer&& buffer) { diff --git a/include/glaze/exceptions/core_exceptions.hpp b/include/glaze/exceptions/core_exceptions.hpp index b6cd802dea..8523fb13ad 100644 --- a/include/glaze/exceptions/core_exceptions.hpp +++ b/include/glaze/exceptions/core_exceptions.hpp @@ -10,7 +10,7 @@ namespace glz::ex { - template + template requires read_supported void read(T&& value, auto&& buffer) { @@ -24,7 +24,7 @@ namespace glz::ex namespace glz::ex { // For writing to a std::string, std::vector, std::deque and the like - template + template requires write_supported void write(T&& value, Buffer& buffer, is_context auto&& ctx) { @@ -34,7 +34,7 @@ namespace glz::ex } } - template + template requires write_supported void write(T&& value, Buffer& buffer) { @@ -44,7 +44,7 @@ namespace glz::ex } } - template + template requires write_supported void write(T&& value, Buffer& buffer) { @@ -52,7 +52,7 @@ namespace glz::ex glz::ex::write(std::forward(value), buffer, ctx); } - template + template requires write_supported [[nodiscard]] std::string write(T&& value) { @@ -63,7 +63,7 @@ namespace glz::ex return e.value(); } - template + template requires write_supported [[nodiscard]] size_t write(T&& value, Buffer&& buffer) { diff --git a/include/glaze/exceptions/json_schema_exceptions.hpp b/include/glaze/exceptions/json_schema_exceptions.hpp index db15810a8e..188cdbdb5a 100644 --- a/include/glaze/exceptions/json_schema_exceptions.hpp +++ b/include/glaze/exceptions/json_schema_exceptions.hpp @@ -10,7 +10,7 @@ namespace glz::ex { - template + template void write_json_schema(Buffer&& buffer) { const auto ec = glz::write_json_schema(buffer); @@ -19,7 +19,7 @@ namespace glz::ex } } - template + template [[nodiscard]] std::string write_json_schema() { std::string buffer{}; diff --git a/include/glaze/ext/cli_menu.hpp b/include/glaze/ext/cli_menu.hpp index 842c60ff21..7d8f3c4084 100644 --- a/include/glaze/ext/cli_menu.hpp +++ b/include/glaze/ext/cli_menu.hpp @@ -54,7 +54,7 @@ namespace glz } } - template + template requires(glaze_object_t || reflectable) inline void run_cli_menu(T& value, cli_menu_boolean auto& show_menu) { @@ -154,7 +154,7 @@ namespace glz run_cli_menu(v, menu_boolean); } } - else if constexpr (Opts.hide_non_invocable) { + else if constexpr (check_hide_non_invocable(Opts)) { } else { static_assert(false_v, "Your function is not invocable or not concrete"); @@ -204,7 +204,7 @@ namespace glz else if constexpr (is_invocable_concrete>) { std::printf(" %d %.*s\n", uint32_t(I + 1), int(key.size()), key.data()); } - else if constexpr (Opts.hide_non_invocable) { + else if constexpr (check_hide_non_invocable(Opts)) { // do not print non-invocable member } else { @@ -251,7 +251,7 @@ namespace glz } } - template + template requires(glaze_object_t || reflectable) inline void run_cli_menu(T& value) { diff --git a/include/glaze/ext/glaze_asio.hpp b/include/glaze/ext/glaze_asio.hpp index c58c182556..c0c624cfa6 100644 --- a/include/glaze/ext/glaze_asio.hpp +++ b/include/glaze/ext/glaze_asio.hpp @@ -80,7 +80,7 @@ namespace glz // Decodes a repe::message into a structure // Returns a std::string with a formatted error on error - template + template inline std::optional decode_message(T&& value, message& msg) { if (bool(msg.header.ec)) { @@ -304,7 +304,7 @@ namespace glz ~unique_socket() { pool->free(index); } }; - template + template struct asio_client { std::string host{"localhost"}; // host name @@ -381,7 +381,7 @@ namespace glz } }; - template + template struct asio_server { uint16_t port{}; // 0 will select a random free port diff --git a/include/glaze/file/read_directory.hpp b/include/glaze/file/read_directory.hpp index 185894a516..195fbd783e 100644 --- a/include/glaze/file/read_directory.hpp +++ b/include/glaze/file/read_directory.hpp @@ -37,7 +37,7 @@ namespace glz return {}; } - template + template [[nodiscard]] error_ctx read_directory(T& value, const sv directory_path, const sv target_extension = ".json") { std::unordered_map files{}; diff --git a/include/glaze/file/write_directory.hpp b/include/glaze/file/write_directory.hpp index 31592d9d9b..7950357d29 100644 --- a/include/glaze/file/write_directory.hpp +++ b/include/glaze/file/write_directory.hpp @@ -25,7 +25,7 @@ namespace glz return {}; } - template + template [[nodiscard]] error_ctx write_directory(T&& value, const sv directory_path) { namespace fs = std::filesystem; diff --git a/include/glaze/glaze_exceptions.hpp b/include/glaze/glaze_exceptions.hpp index b6bdc183de..3fd9687eb8 100644 --- a/include/glaze/glaze_exceptions.hpp +++ b/include/glaze/glaze_exceptions.hpp @@ -13,7 +13,7 @@ namespace glz::ex { - template + template void read(auto& value, auto&& buffer) { auto ec = glz::read(value, buffer); @@ -27,13 +27,13 @@ namespace glz::ex } } - template + template void write(T&& value, Buffer& buffer) noexcept { glz::write(std::forward(value), buffer); } - template + template size_t write(T&& value, Buffer&& buffer) noexcept { return glz::write(std::forward(value), std::forward(buffer)); diff --git a/include/glaze/json/jmespath.hpp b/include/glaze/json/jmespath.hpp index 9fadf6e27b..cf12beb4c5 100644 --- a/include/glaze/json/jmespath.hpp +++ b/include/glaze/json/jmespath.hpp @@ -423,14 +423,14 @@ namespace glz namespace detail { - template + template requires(Opts.format == JSON && not readable_array_t) inline void handle_slice(const jmespath::ArrayParseResult&, T&&, context& ctx, auto&&, auto&&) { ctx.error = error_code::syntax_error; } - template + template requires(Opts.format == JSON && readable_array_t) inline void handle_slice(const jmespath::ArrayParseResult& decomposed_key, T&& value, context& ctx, auto&& it, auto&& end) @@ -579,7 +579,7 @@ namespace glz } // Read into a C++ type given a path denoted by a JMESPath query - template + template requires(Options.format == JSON) [[nodiscard]] inline error_ctx read_jmespath(T&& value, Buffer&& buffer) { @@ -860,7 +860,7 @@ namespace glz // Read into a C++ type given a path denoted by a JMESPath query // This version supports a runtime path - template + template requires(Options.format == JSON) [[nodiscard]] inline error_ctx read_jmespath(const jmespath_expression& expression, T&& value, Buffer&& buffer) { diff --git a/include/glaze/json/json_format.hpp b/include/glaze/json/json_format.hpp index 93daca1973..d28a852fc1 100644 --- a/include/glaze/json/json_format.hpp +++ b/include/glaze/json/json_format.hpp @@ -65,7 +65,7 @@ namespace glz::detail } }; - template + template requires(has_is_padded(Opts)) sv read_json_string(auto&& it, auto&& end) noexcept { @@ -96,7 +96,7 @@ namespace glz::detail return {}; } - template + template requires(!has_is_padded(Opts)) sv read_json_string(auto&& it, auto&& end) noexcept { diff --git a/include/glaze/json/json_t.hpp b/include/glaze/json/json_t.hpp index 38ca650111..d2c645ea43 100644 --- a/include/glaze/json/json_t.hpp +++ b/include/glaze/json/json_t.hpp @@ -334,7 +334,7 @@ namespace glz { // These functions allow a json_t value to be read/written to a C++ struct - template + template requires read_supported [[nodiscard]] error_ctx read(T& value, const json_t& source) { diff --git a/include/glaze/json/minify.hpp b/include/glaze/json/minify.hpp index cbfbab821f..515c980113 100644 --- a/include/glaze/json/minify.hpp +++ b/include/glaze/json/minify.hpp @@ -12,7 +12,7 @@ namespace glz namespace detail { // We can use unchecked dumping to the output because we know minifying will not make the output any larger - template + template inline void minify_json(is_context auto&& ctx, auto&& it, auto&& end, auto&& b, auto&& ix) noexcept { using enum json_type; @@ -154,7 +154,7 @@ namespace glz } } - template + template requires(contiguous && resizable) inline void minify_json(is_context auto&& ctx, In&& in, Out&& out) { @@ -191,14 +191,14 @@ namespace glz // should not happen since we minify auto-generated JSON. // The detail version can be used if error context is needed - template + template inline void minify_json(resizable auto& in, auto& out) { context ctx{}; detail::minify_json(ctx, in, out); } - template + template inline std::string minify_json(resizable auto& in) { context ctx{}; @@ -207,14 +207,14 @@ namespace glz return out; } - template + template inline void minify_jsonc(const auto& in, auto& out) { context ctx{}; detail::minify_json>(ctx, in, out); } - template + template inline std::string minify_jsonc(resizable auto& in) { context ctx{}; diff --git a/include/glaze/json/ndjson.hpp b/include/glaze/json/ndjson.hpp index c9333da1f0..9b5af37816 100644 --- a/include/glaze/json/ndjson.hpp +++ b/include/glaze/json/ndjson.hpp @@ -45,7 +45,7 @@ namespace glz if constexpr (resizable) { value.clear(); - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } } @@ -78,7 +78,7 @@ namespace glz value.erase(value_it, value.end()); // use erase rather than resize for non-default constructible elements - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } } diff --git a/include/glaze/json/prettify.hpp b/include/glaze/json/prettify.hpp index 33b1008d44..827dcceb47 100644 --- a/include/glaze/json/prettify.hpp +++ b/include/glaze/json/prettify.hpp @@ -11,7 +11,7 @@ namespace glz { namespace detail { - template + template inline void prettify_json(is_context auto&& ctx, auto&& it, auto&& end, auto&& b, auto&& ix) { constexpr bool use_tabs = Opts.indentation_char == '\t'; @@ -171,7 +171,7 @@ namespace glz } } - template + template inline void prettify_json(is_context auto&& ctx, In&& in, Out&& out) { if constexpr (resizable) { @@ -204,7 +204,7 @@ namespace glz // should not happen since we prettify auto-generated JSON. // The detail version can be used if error context is needed - template + template inline void prettify_json(const auto& in, auto& out) { context ctx{}; @@ -214,7 +214,7 @@ namespace glz /// /// allocating version of prettify /// - template + template inline std::string prettify_json(const auto& in) { context ctx{}; @@ -223,7 +223,7 @@ namespace glz return out; } - template + template inline void prettify_jsonc(const auto& in, auto& out) { context ctx{}; @@ -233,7 +233,7 @@ namespace glz /// /// allocating version of prettify /// - template + template inline std::string prettify_jsonc(const auto& in) { context ctx{}; diff --git a/include/glaze/json/read.hpp b/include/glaze/json/read.hpp index 43779c53a9..e8c94dff3f 100644 --- a/include/glaze/json/read.hpp +++ b/include/glaze/json/read.hpp @@ -147,7 +147,7 @@ namespace glz return false; } - template + template requires(glaze_object_t || reflectable) void decode_index(Value&& value, is_context auto&& ctx, auto&& it, auto&& end, SelectedIndex&&... selected_index) { @@ -227,7 +227,7 @@ namespace glz } } - template + template requires(glaze_enum_t || (meta_keys && std::is_enum_v)) void decode_index(Value&& value, is_context auto&& ctx, auto&& it, auto&& end) noexcept { @@ -256,7 +256,7 @@ namespace glz } } - template + template requires(glaze_object_t || reflectable) GLZ_ALWAYS_INLINE constexpr void parse_and_invoke(Value&& value, is_context auto&& ctx, auto&& it, auto&& end, SelectedIndex&&... selected_index) @@ -1477,7 +1477,7 @@ namespace glz if constexpr (resizable && not Opts.append_arrays) { value.clear(); - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } } @@ -1521,7 +1521,7 @@ namespace glz value.erase(value_it, value.end()); // use erase rather than resize for non-default constructible elements - if constexpr (Opts.shrink_to_fit) { + if constexpr (check_shrink_to_fit(Opts)) { value.shrink_to_fit(); } } @@ -1582,7 +1582,7 @@ namespace glz // for types like std::vector that can't look up with operator[] // Intead of hashing or linear searching, we just clear the input and overwrite the entire contents template - requires(pair_t> && Options.concatenate == true) + requires(pair_t> && check_concatenate(Options) == true) static void op(auto&& value, is_context auto&& ctx, auto&& it, auto&& end) { static constexpr auto Opts = opening_handled_off()>(); @@ -1996,7 +1996,7 @@ namespace glz template struct from { - template + template static void op(T& value, is_context auto&& ctx, auto&& it, auto&& end) { constexpr auto Opts = opening_handled_off()>(); @@ -3220,13 +3220,19 @@ namespace glz value = buffer; } }; + + struct opts_validate : opts + { + bool validate_skipped = true; + bool validate_trailing_whitespace = true; + }; template [[nodiscard]] error_ctx validate_json(Buffer&& buffer) noexcept { context ctx{}; glz::skip skip_value{}; - return read( + return read( skip_value, std::forward(buffer), ctx); } @@ -3235,7 +3241,7 @@ namespace glz { context ctx{}; glz::skip skip_value{}; - return read( + return read( skip_value, std::forward(buffer), ctx); } diff --git a/include/glaze/json/schema.hpp b/include/glaze/json/schema.hpp index ae5254b534..e593967252 100644 --- a/include/glaze/json/schema.hpp +++ b/include/glaze/json/schema.hpp @@ -666,16 +666,20 @@ namespace glz } - template + template [[nodiscard]] error_ctx write_json_schema(Buffer&& buffer) { detail::schematic s{}; s.defs.emplace(); detail::to_json_schema>::template op(s, *s.defs); - return write>(std::move(s), std::forward(buffer)); + struct opts_write_type_info_off : std::decay_t + { + bool write_type_info = false; + }; + return write(std::move(s), std::forward(buffer)); } - template + template [[nodiscard]] glz::expected write_json_schema() { std::string buffer{}; diff --git a/include/glaze/json/skip.hpp b/include/glaze/json/skip.hpp index 99f3fd6012..0d77d823cf 100644 --- a/include/glaze/json/skip.hpp +++ b/include/glaze/json/skip.hpp @@ -10,19 +10,19 @@ namespace glz template <> struct skip_value { - template + template requires(not Opts.comments) GLZ_ALWAYS_INLINE static void op(is_context auto&& ctx, auto&& it, auto&& end) noexcept; - template + template requires(bool(Opts.comments)) GLZ_ALWAYS_INLINE static void op(is_context auto&& ctx, auto&& it, auto&& end) noexcept; }; - template + template void skip_object(is_context auto&& ctx, auto&& it, auto&& end) noexcept { - if constexpr (!Opts.validate_skipped) { + if constexpr (!check_validate_skipped(Opts)) { ++it; if constexpr (not Opts.null_terminated) { if (it == end) [[unlikely]] { @@ -107,11 +107,11 @@ namespace glz } } - template + template requires(Opts.format == JSON || Opts.format == NDJSON) void skip_array(is_context auto&& ctx, auto&& it, auto&& end) noexcept { - if constexpr (!Opts.validate_skipped) { + if constexpr (!check_validate_skipped(Opts)) { ++it; if constexpr (not Opts.null_terminated) { if (it == end) [[unlikely]] { @@ -184,21 +184,25 @@ namespace glz // we want the JSON pointer access to not care about trailing whitespace // so we use validate_skipped for precise validation and value skipping // expects opening whitespace to be handled - template + template GLZ_ALWAYS_INLINE auto parse_value(is_context auto&& ctx, auto&& it, auto&& end) noexcept { auto start = it; - skip_value::op>(ctx, it, end); + struct opts_validate_skipped : std::decay_t + { + bool validate_skipped = true; + }; + skip_value::op(ctx, it, end); return std::span{start, size_t(it - start)}; } - template + template requires(not Opts.comments) GLZ_ALWAYS_INLINE void skip_value::op(is_context auto&& ctx, auto&& it, auto&& end) noexcept { using namespace glz::detail; - if constexpr (not Opts.validate_skipped) { + if constexpr (not check_validate_skipped(Opts)) { if constexpr (not has_ws_handled(Opts)) { if (skip_ws(ctx, it, end)) { return; @@ -302,7 +306,7 @@ namespace glz } } - template + template requires(bool(Opts.comments)) GLZ_ALWAYS_INLINE void skip_value::op(is_context auto&& ctx, auto&& it, auto&& end) noexcept { diff --git a/include/glaze/json/write.hpp b/include/glaze/json/write.hpp index 64f37b80ba..e767e9c483 100644 --- a/include/glaze/json/write.hpp +++ b/include/glaze/json/write.hpp @@ -73,7 +73,7 @@ namespace glz } }; - template + template requires(Opts.format == JSON || Opts.format == NDJSON) GLZ_ALWAYS_INLINE void write_object_entry_separator(is_context auto&& ctx, B&& b, auto&& ix) { @@ -886,7 +886,7 @@ namespace glz } }; - template + template GLZ_ALWAYS_INLINE void write_array_entry_separator(is_context auto&& ctx, B&& b, auto&& ix) { if constexpr (Opts.prettify) { @@ -920,7 +920,7 @@ namespace glz } // "key":value pair output - template + template GLZ_ALWAYS_INLINE void write_pair_content(const Key& key, Value&& value, Ctx& ctx, B&& b, auto&& ix) { if constexpr (str_t || char_t || glaze_enum_t || Opts.quoted_num) { @@ -954,7 +954,7 @@ namespace glz static constexpr bool map_like_array = writable_array_t && pair_t>; template - requires(writable_array_t && (map_like_array ? Opts.concatenate == false : true)) + requires(writable_array_t && (map_like_array ? check_concatenate(Opts) == false : true)) GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix) { if (empty_range(value)) { @@ -1136,7 +1136,7 @@ namespace glz } template - requires(writable_map_t || (map_like_array && Opts.concatenate == true)) + requires(writable_map_t || (map_like_array && check_concatenate(Opts) == true)) static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix) { if constexpr (not has_opening_handled(Opts)) { @@ -1235,7 +1235,7 @@ namespace glz template struct to { - template + template static void op(const T& value, is_context auto&& ctx, B&& b, Ix&& ix) { const auto& [key, val] = value; @@ -1361,7 +1361,7 @@ namespace glz [&](auto&& val) { using V = std::decay_t; - if constexpr (Opts.write_type_info && not tag_v.empty() && glaze_object_t) { + if constexpr (check_write_type_info(Opts) && not tag_v.empty() && glaze_object_t) { constexpr auto N = reflect::size; // must first write out type @@ -2005,7 +2005,7 @@ namespace glz return write(std::forward(value)); } - template + template requires(write_supported) [[nodiscard]] error_ctx write_file_json(T&& value, const sv file_name, auto&& buffer) { diff --git a/include/glaze/rpc/repe/registry.hpp b/include/glaze/rpc/repe/registry.hpp index c2e7cc3466..52262364e9 100644 --- a/include/glaze/rpc/repe/registry.hpp +++ b/include/glaze/rpc/repe/registry.hpp @@ -34,7 +34,7 @@ namespace glz::repe }; } - template + template void write_response(Value&& value, is_state auto&& state) { auto& in = state.in; @@ -56,7 +56,7 @@ namespace glz::repe } } - template + template void write_response(is_state auto&& state) { auto& in = state.in; @@ -80,7 +80,7 @@ namespace glz::repe } // returns 0 on error - template + template size_t read_params(Value&& value, auto&& state) { glz::context ctx{}; @@ -118,7 +118,7 @@ namespace glz::repe namespace detail { - template + template struct request_impl { message operator()(const user_header& h) const @@ -169,7 +169,7 @@ namespace glz::repe }; } - template + template inline constexpr auto request = detail::request_impl{}; inline constexpr auto request_beve = request; @@ -188,7 +188,7 @@ namespace glz::repe } // This registry does not support adding methods from RPC calls or adding methods once RPC calls can be made. - template + template struct registry { using procedure = std::function; // RPC method diff --git a/include/glaze/stencil/stencil.hpp b/include/glaze/stencil/stencil.hpp index d4cb1e2d42..f46d791847 100644 --- a/include/glaze/stencil/stencil.hpp +++ b/include/glaze/stencil/stencil.hpp @@ -9,7 +9,7 @@ namespace glz { - template + template [[nodiscard]] error_ctx stencil(Template&& layout, T&& value, Buffer& buffer) { context ctx{}; @@ -241,7 +241,7 @@ namespace glz return {}; } - template + template [[nodiscard]] expected stencil(Template&& layout, T&& value) { std::string buffer{}; diff --git a/include/glaze/stencil/stencilcount.hpp b/include/glaze/stencil/stencilcount.hpp index d6c09bbe80..75a9d82a0a 100644 --- a/include/glaze/stencil/stencilcount.hpp +++ b/include/glaze/stencil/stencilcount.hpp @@ -10,7 +10,7 @@ namespace glz { - template + template [[nodiscard]] error_ctx stencilcount(Template&& layout, T&& value, Buffer& buffer) { context ctx{}; @@ -170,7 +170,7 @@ namespace glz return {}; } - template + template [[nodiscard]] expected stencilcount(Template&& layout, T&& value) { std::string buffer{}; diff --git a/include/glaze/toml/write.hpp b/include/glaze/toml/write.hpp index 33223dae1c..856576f94d 100644 --- a/include/glaze/toml/write.hpp +++ b/include/glaze/toml/write.hpp @@ -273,7 +273,7 @@ namespace glz } }; - template + template requires(Opts.format == TOML) GLZ_ALWAYS_INLINE void write_array_entry_separator(is_context auto&&, B&& b, auto&& ix) { @@ -288,7 +288,7 @@ namespace glz ix += 2; } - template + template requires(Opts.format == TOML) GLZ_ALWAYS_INLINE void write_object_entry_separator(is_context auto&&, B&& b, auto&& ix) { @@ -416,7 +416,7 @@ namespace glz // --- Array-like container writer --- template - requires(writable_array_t && (map_like_array ? Opts.concatenate == false : true)) + requires(writable_array_t && (map_like_array ? check_concatenate(Opts) == false : true)) GLZ_ALWAYS_INLINE static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix) { if (empty_range(value)) { @@ -468,7 +468,7 @@ namespace glz // --- Map-like container writer --- template - requires(writable_map_t || (map_like_array && Opts.concatenate == true)) + requires(writable_map_t || (map_like_array && check_concatenate(Opts) == true)) static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix) { bool first = true; @@ -571,7 +571,7 @@ namespace glz return write(std::forward(value)); } - template + template requires(write_supported) [[nodiscard]] error_ctx write_file_toml(T&& value, const sv file_name, auto&& buffer) { diff --git a/include/glaze/util/parse.hpp b/include/glaze/util/parse.hpp index d59910abaa..52069ebd5e 100644 --- a/include/glaze/util/parse.hpp +++ b/include/glaze/util/parse.hpp @@ -385,7 +385,7 @@ namespace glz } } - template + template requires(has_is_padded(Opts) && str.size() <= padding_bytes) GLZ_ALWAYS_INLINE void match(is_context auto&& ctx, auto&& it, auto&&) noexcept { @@ -398,7 +398,7 @@ namespace glz } } - template + template requires(!has_is_padded(Opts)) GLZ_ALWAYS_INLINE void match(is_context auto&& ctx, auto&& it, auto&& end) noexcept { @@ -479,7 +479,7 @@ namespace glz namespace glz { // skip whitespace - template + template GLZ_ALWAYS_INLINE bool skip_ws(is_context auto&& ctx, auto&& it, auto&& end) noexcept { using namespace glz::detail; @@ -650,7 +650,7 @@ namespace glz ctx.error = error_code::expected_quote; } - template + template GLZ_ALWAYS_INLINE void skip_string_view(is_context auto&& ctx, auto&& it, auto&& end) noexcept { while (it < end) [[likely]] { @@ -674,7 +674,7 @@ namespace glz ctx.error = error_code::expected_quote; } - template + template requires(has_is_padded(Opts)) GLZ_ALWAYS_INLINE void skip_string(is_context auto&& ctx, auto&& it, auto&& end) noexcept { @@ -682,7 +682,7 @@ namespace glz ++it; } - if constexpr (Opts.validate_skipped) { + if constexpr (check_validate_skipped(Opts)) { while (true) { uint64_t swar{}; std::memcpy(&swar, it, 8); @@ -764,7 +764,7 @@ namespace glz } } - template + template requires(not has_is_padded(Opts)) GLZ_ALWAYS_INLINE void skip_string(is_context auto&& ctx, auto&& it, auto&& end) noexcept { @@ -772,7 +772,7 @@ namespace glz ++it; } - if constexpr (Opts.validate_skipped) { + if constexpr (check_validate_skipped(Opts)) { while (true) { if ((*it & 0b11100000) == 0) [[unlikely]] { ctx.error = error_code::syntax_error; @@ -816,7 +816,7 @@ namespace glz } } - template + template requires(has_is_padded(Opts) && not bool(Opts.comments)) GLZ_ALWAYS_INLINE void skip_until_closed(is_context auto&& ctx, auto&& it, auto&& end) noexcept { @@ -864,7 +864,7 @@ namespace glz ctx.error = error_code::unexpected_end; } - template + template requires(has_is_padded(Opts) && bool(Opts.comments)) GLZ_ALWAYS_INLINE void skip_until_closed(is_context auto&& ctx, auto&& it, auto&& end) noexcept { @@ -919,7 +919,7 @@ namespace glz ctx.error = error_code::unexpected_end; } - template + template requires(!has_is_padded(Opts) && not bool(Opts.comments)) GLZ_ALWAYS_INLINE void skip_until_closed(is_context auto&& ctx, auto&& it, auto&& end) noexcept { @@ -1003,7 +1003,7 @@ namespace glz ctx.error = error_code::unexpected_end; } - template + template requires(!has_is_padded(Opts) && bool(Opts.comments)) GLZ_ALWAYS_INLINE void skip_until_closed(is_context auto&& ctx, auto&& it, auto&& end) noexcept { @@ -1152,10 +1152,10 @@ namespace glz } } - template + template GLZ_ALWAYS_INLINE void skip_number(is_context auto&& ctx, auto&& it, auto&& end) noexcept { - if constexpr (!Opts.validate_skipped) { + if constexpr (!check_validate_skipped(Opts)) { while (numeric_table[uint8_t(*it)]) { ++it; } diff --git a/tests/beve_test/beve_test.cpp b/tests/beve_test/beve_test.cpp index cb3dbebc3e..e8053ab912 100644 --- a/tests/beve_test/beve_test.cpp +++ b/tests/beve_test/beve_test.cpp @@ -2377,8 +2377,13 @@ suite custom_load_test = [] { }; }; +struct opts_concatenate : glz::opts +{ + bool concatenate = true; +}; + suite pair_ranges_tests = [] { - static constexpr glz::opts concatenate_off{.format = glz::BEVE, .concatenate = false}; + static constexpr opts_concatenate concatenate_off{{glz::BEVE}, false}; "vector pair"_test = [] { std::vector> v{{1, 2}, {3, 4}}; diff --git a/tests/json_conformance/json_conformance.cpp b/tests/json_conformance/json_conformance.cpp index f12ece0964..e3996926ac 100644 --- a/tests/json_conformance/json_conformance.cpp +++ b/tests/json_conformance/json_conformance.cpp @@ -29,7 +29,7 @@ struct nullable_object std::optional i{}; }; -template +template inline void should_fail() { "unclosed_array"_test = [] { @@ -108,7 +108,7 @@ inline void should_fail() } }; - if constexpr (Opts.validate_trailing_whitespace) { + if constexpr (check_validate_trailing_whitespace(Opts)) { "comma after close"_test = [] { constexpr sv s = R"(["Comma after the close"],)"; { @@ -319,7 +319,7 @@ inline void should_fail() }; } -template +template inline void should_pass() { "bool_object"_test = [] { @@ -341,6 +341,11 @@ inline void should_pass() }; } +struct opts_validate_trailing_whitespace : glz::opts +{ + bool validate_trailing_whitespace = true; +}; + suite json_conformance = [] { "error_on_unknown_keys = true"_test = [] { should_fail(); @@ -353,8 +358,8 @@ suite json_conformance = [] { }; "validate_trailing_whitespace = true"_test = [] { - should_fail(); - should_pass(); + should_fail(); + should_pass(); }; }; diff --git a/tests/json_performance/json_performance.cpp b/tests/json_performance/json_performance.cpp index ad7b6931f9..d46c6050ff 100644 --- a/tests/json_performance/json_performance.cpp +++ b/tests/json_performance/json_performance.cpp @@ -446,7 +446,7 @@ struct results } }; -template +template auto glaze_test() { std::string buffer{json_minified}; @@ -789,7 +789,7 @@ struct glz::meta> &T::o, &T::p, &T::q, &T::r, &T::s, &T::t, &T::u, &T::v, &T::w, &T::x, &T::y, &T::z); }; -template +template auto benchmark_tester() { std::string buffer{}; @@ -1203,7 +1203,7 @@ struct glz::meta object("t", &value_type::t, "d", &value_type::d, "op", &value_type::op, "s", &value_type::s); }; -template +template auto generic_tester() { T obj{}; diff --git a/tests/json_test/json_test.cpp b/tests/json_test/json_test.cpp index aee9819e01..8ad953c382 100644 --- a/tests/json_test/json_test.cpp +++ b/tests/json_test/json_test.cpp @@ -780,6 +780,11 @@ suite basic_types = [] { }; }; +struct opts_concatenate : glz::opts +{ + bool concatenate = true; +}; + suite container_types = [] { using namespace ut; "vector int roundtrip"_test = [] { @@ -832,14 +837,15 @@ suite container_types = [] { }; "vector pair"_test = [] { std::vector> v; - expect(!glz::read(v, R"([{"1":2},{"3":4}])")); - const auto s = glz::write(v).value_or("error"); + expect(!glz::read(v, R"([{"1":2},{"3":4}])")); + const auto s = glz::write(v).value_or("error"); expect(s == R"([{"1":2},{"3":4}])") << s; }; "vector pair"_test = [] { std::vector> v; - expect(!glz::read(v, R"([{"1":2},{"3":4}])")); - const auto s = glz::write(v).value_or("error"); + expect(!glz::read(v, R"([{"1":2},{"3":4}])")); + //constexpr glz::opts opts{.prettify = true}; + const auto s = glz::write(v).value_or("error"); expect(s == R"([ { "1": 2 @@ -4997,6 +5003,11 @@ suite no_except_tests = [] { }; }; +struct opts_validate_trailing_whitespace : glz::opts +{ + bool validate_trailing_whitespace = false; +}; + suite validation_tests = [] { "validate_json"_test = [] { glz::json_t json{}; @@ -5004,7 +5015,7 @@ suite validation_tests = [] { // Tests are taken from the https://www.json.org/JSON_checker/ test suite std::string fail10 = R"({"Extra value after close": true} "misplaced quoted value")"; - auto ec_fail10 = glz::read(json, fail10); + auto ec_fail10 = glz::read(json, fail10); expect(ec_fail10 != glz::error_code::none); expect(glz::validate_json(fail10) != glz::error_code::none); @@ -5146,12 +5157,12 @@ break"])"; expect(glz::validate_json(fail6) != glz::error_code::none); std::string fail7 = R"(["Comma after the close"],)"; - auto ec_fail7 = glz::read(json, fail7); + auto ec_fail7 = glz::read(json, fail7); expect(ec_fail7 != glz::error_code::none); expect(glz::validate_json(fail7) != glz::error_code::none); std::string fail8 = R"(["Extra close"]])"; - auto ec_fail8 = glz::read(json, fail8); + auto ec_fail8 = glz::read(json, fail8); expect(ec_fail8 != glz::error_code::none); expect(glz::validate_json(fail8) != glz::error_code::none); @@ -9560,6 +9571,11 @@ struct TestSettingsData std::string username = "MISSING"; }; +struct opts_allow_conversions : glz::opts +{ + bool allow_conversions = true; +}; + suite TestSettingsData_test = [] { "TestSettingsData"_test = [] { TestSettingsData obj{}; @@ -9569,12 +9585,11 @@ suite TestSettingsData_test = [] { expect(not ec) << glz::format_error(ec, buffer); }; - static constexpr glz::opts write_options{.comments = 1U, .prettify = 1U, .allow_conversions = 1U}; - static constexpr glz::opts read_options{.comments = 1U, - .error_on_unknown_keys = 0U, - .skip_null_members = 1U, - .error_on_missing_keys = 0U, - .allow_conversions = 1U}; + static constexpr opts_allow_conversions write_options{{glz::opts{.comments = true, .prettify = true}}}; + static constexpr opts_allow_conversions read_options{{glz::opts{.comments = true, + .error_on_unknown_keys = false, + .skip_null_members = true, + .error_on_missing_keys = false}}}; "TestSettingsData options"_test = [] { TestSettingsData obj{}; @@ -10008,8 +10023,12 @@ struct Foo suite ndjson_options = [] { "ndjson_options"_test = [] { std::vector assets{}; - const auto ec = - glz::read( + + struct opts : glz::opts { + bool validate_skipped = true; + }; + + const auto ec = glz::read( assets, "{\"x\":1}\n{\"x\":2}"); expect(not ec); }; diff --git a/tests/roundtrip/roundtrip.cpp b/tests/roundtrip/roundtrip.cpp index 3f1eccb152..aba1ec9fa9 100644 --- a/tests/roundtrip/roundtrip.cpp +++ b/tests/roundtrip/roundtrip.cpp @@ -11,13 +11,13 @@ using namespace ut; // By changing the GLZ_TEST_FORMAT macro we are able to change what format is // tested in CMake. -template +template decltype(auto) read(T&& value, Buffer&& buffer) { return glz::read(std::forward(value), std::forward(buffer)); } -template +template decltype(auto) write(T&& value, Buffer&& buffer) { return glz::write(std::forward(value), std::forward(buffer)); @@ -34,7 +34,7 @@ struct my_struct std::map map{{"one", 1}, {"two", 2}}; }; -template +template void roundtrip(T& v) { std::string buffer{};