diff --git a/README.md b/README.md index caefe50..786c30f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ - Proper UTF-8 handling (incl. BOM) - Works with or without exceptions - Doesn't require RTTI - - First-class support for serializing to JSON + - First-class support for serializing to JSON and YAML - Tested on Clang (6+), GCC (7+) and MSVC (VS2019) - Tested on x64, x86 and ARM @@ -79,6 +79,8 @@ std::cout << config << "\n"; // re-serialize as JSON std::cout << toml::json_formatter{ config } << "\n"; +// re-serialize as YAML +std::cout << toml::yaml_formatter{ config } << "\n"; ``` You'll find some more code examples in the `examples` directory, and plenty more as part of the [API documentation]. @@ -97,13 +99,13 @@ You'll find some more code examples in the `examples` directory, and plenty more 2. `#include ` ### Conan -Add `tomlplusplus/2.5.0` to your conanfile. +Add `tomlplusplus/3.0.0` to your conanfile. ### DDS Add `tomlpp` to your `package.json5`, e.g.: ``` depends: [ - 'tomlpp^2.5.0', + 'tomlpp^3.0.0', ] ``` > ℹ️ _[What is DDS?](https://dds.pizza/)_ @@ -119,7 +121,7 @@ include(FetchContent) FetchContent_Declare( tomlplusplus GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git - GIT_TAG v2.5.0 + GIT_TAG v3.0.0 ) FetchContent_MakeAvailable(tomlplusplus) ``` @@ -136,22 +138,21 @@ assured that I fully support it being added, and welcome [pull requests](./CONTR A number of configurable options are exposed in the form of preprocessor `#defines` Most likely you won't need to mess with these at all, but if you do, set them before including toml++. -| Option | Type | Default | Description | -|-----------------------------------|:--------------:|--------------------------|----------------------------------------------------------------------------------------------------------------------| -| `TOML_API` | define | undefined | API annotation to add to public symbols (e.g. `__declspec(dllexport)` on Windows). | -| `TOML_ASSERT(expr)` | function macro | `assert()` | Sets the assert function used by the library. | -| `TOML_CONFIG_HEADER` | string literal | undefined | Includes the given header file before the rest of the library. | -| `TOML_ENABLE_JSON_FORMATTER` | boolean | `1` | Enables the JSON formatter. Set to `0` if you don't need it to improve compile times and binary size. | -| `TOML_ENABLE_TOML_FORMATTER` | boolean | `1` | Enables the TOML formatter. Set to `0` if you don't need it to improve compile times and binary size. | -| `TOML_ENABLE_PARSER` | boolean | `1` | Enables the parser. Set to `0` if you don't need it to improve compile times and binary size. | -| `TOML_ENABLE_UNRELEASED_FEATURES` | boolean | `0` | Enables support for [unreleased TOML language features. | -| `TOML_ENABLE_WINDOWS_COMPAT` | boolean | `1` on Windows | Enables support for transparent conversion between wide and narrow strings. | -| `TOML_EXCEPTIONS` | boolean | your compiler's settings | Sets whether the library uses exceptions. | -| `TOML_HEADER_ONLY` | boolean | `1` | Disable this to explicitly control where toml++'s implementation is compiled (e.g. as part of a library). | -| `TOML_IMPLEMENTATION` | define | undefined | Define this to enable compilation of the library's implementation when `TOML_HEADER_ONLY` == `0`. | -| `TOML_OPTIONAL_TYPE` | type name | undefined | Overrides the `optional` type used by the library if you need [something better than std::optional]. | -| `TOML_SMALL_FLOAT_TYPE` | type name | undefined | If your codebase has an additional 'small' float type (e.g. half-precision), this tells toml++ about it. | -| `TOML_SMALL_INT_TYPE` | type name | undefined | If your codebase has an additional 'small' integer type (e.g. 24-bits), this tells toml++ about it. | +| Option | Type | Default | Description | +|-----------------------------------|:--------------:|--------------------------|---------------------------------------------------------------------------------------------------------------| +| `TOML_API` | define | undefined | API annotation to add to public symbols (e.g. `__declspec(dllexport)` on Windows). | +| `TOML_ASSERT(expr)` | function macro | `assert()` | Sets the assert function used by the library. | +| `TOML_CONFIG_HEADER` | string literal | undefined | Includes the given header file before the rest of the library. | +| `TOML_ENABLE_FORMATTERS` | boolean | `1` | Enables the formatters. Set to `0` if you don't need them to improve compile times and binary size. | +| `TOML_ENABLE_PARSER` | boolean | `1` | Enables the parser. Set to `0` if you don't need it to improve compile times and binary size. | +| `TOML_ENABLE_UNRELEASED_FEATURES` | boolean | `0` | Enables support for [unreleased TOML language features. | +| `TOML_ENABLE_WINDOWS_COMPAT` | boolean | `1` on Windows | Enables support for transparent conversion between wide and narrow strings. | +| `TOML_EXCEPTIONS` | boolean | match compiler settings | Sets whether the library uses exceptions. | +| `TOML_HEADER_ONLY` | boolean | `1` | Disable this to explicitly control where toml++'s implementation is compiled (e.g. as part of a library). | +| `TOML_IMPLEMENTATION` | define | undefined | Define this to enable compilation of the library's implementation when `TOML_HEADER_ONLY` == `0`. | +| `TOML_OPTIONAL_TYPE` | type name | undefined | Overrides the `optional` type used by the library if you need [something better than std::optional]. | +| `TOML_SMALL_FLOAT_TYPE` | type name | undefined | If your codebase has an additional 'small' float type (e.g. half-precision), this tells toml++ about it. | +| `TOML_SMALL_INT_TYPE` | type name | undefined | If your codebase has an additional 'small' integer type (e.g. 24-bits), this tells toml++ about it. | > ℹ️ _A number of these have ABI implications; the library uses inline namespaces to prevent you from accidentally linking incompatible combinations together._ diff --git a/cpp.hint b/cpp.hint index f8e2a8a..d769b89 100644 --- a/cpp.hint +++ b/cpp.hint @@ -9,6 +9,7 @@ #define TOML_CONST_GETTER #define TOML_CONST_INLINE_GETTER inline #define TOML_CONSTRAINED_TEMPLATE(cond, ...) template <__VA_ARGS__> +#define TOML_HIDDEN_CONSTRAINT(cond, ...) template <__VA_ARGS__> #define TOML_EMPTY_BASES #define TOML_EXTERN #define TOML_EXTERN_NOEXCEPT(...) diff --git a/docs/pages/main_page.dox b/docs/pages/main_page.dox index cf629aa..0824c71 100644 --- a/docs/pages/main_page.dox +++ b/docs/pages/main_page.dox @@ -9,7 +9,7 @@ \section mainpage-features Features - Supports the latest [TOML](https://toml.io/) release ([v1.0.0](https://toml.io/en/v1.0.0)), plus optional support for some unreleased TOML features - - Supports serializing to JSON + - Supports serializing to JSON and YAML - Proper UTF-8 handling (incl. BOM) - C++17 (plus some C++20 features where available, e.g. experimental support for char8_t strings) - Header-only (optional!) @@ -130,7 +130,7 @@ \subsection mainpage-example-parsing-without-exceptions Handling errors without exceptions Can't (or won't) use exceptions? That's fine too. You can disable exceptions in your compiler flags and/or - explicitly disable the library's use of them by setting the option \ref TOML_EXCEPTIONS to `0`. In either case, + explicitly disable the library's use of them by setting the option #TOML_EXCEPTIONS to `0`. In either case, the parsing functions return a toml::parse_result instead of a toml::table: \cpp @@ -290,10 +290,10 @@ - \subsection mainpage-example-serialization Serializing as TOML and JSON + \subsection mainpage-example-serialization Serializing as TOML, JSON and YAML All toml++ data types have overloaded `operator<<` for ostreams, so 'serializing' a set of TOML data to actual - TOML is done just by printing it to an ostream. Converting it to JSON is done in the same way, - but via a toml::json_formatter. + TOML is done just by printing it to an ostream. Converting it to JSON and YAML is done in much the same way, + but via a toml::json_formatter and toml::yaml_formatter. \godbolt{MMNoW4} @@ -323,6 +323,10 @@ // serializing as JSON using toml::json_formatter: std::cout << "###### JSON ######" << "\n\n"; std::cout << toml::json_formatter{ tbl } << "\n\n"; + + // serializing as YAML using toml::yaml_formatter: + std::cout << "###### YAML ######" << "\n\n"; + std::cout << toml::yaml_formatter{ tbl } << "\n\n"; return 0; } \ecpp @@ -360,10 +364,14 @@ "and beyond" ] } + + ###### YAML ###### + \eout \see - toml::toml_formatter - toml::json_formatter + - toml::yaml_formatter @@ -373,7 +381,7 @@ a large number of translation units. You can counter this by disabling header-only mode and explicitly controlling where the library's implementation is compiled. - Step 1: Set \ref TOML_HEADER_ONLY to [code]0[/code] before including toml++ + Step 1: Set #TOML_HEADER_ONLY to [code]0[/code] before including toml++ This must be the same everywhere, so either set it as a global `#define` in your build system, or do it manually before including toml++ in some global header that's used everywhere in your project: @@ -384,7 +392,7 @@ #include \ecpp - Step 2: Define \ref TOML_IMPLEMENTATION before including toml++ in one specific translation unit + Step 2: Define #TOML_IMPLEMENTATION before including toml++ in one specific translation unit \cpp // some_code_file.cpp @@ -397,7 +405,7 @@ Some library features can be disabled wholesale so you can avoid paying their the compilation cost if you don't need them. For example, if all you need to do is serialize some code-generated TOML and don't actually need the parser at all you, can - set \ref TOML_ENABLE_PARSER to `0` to disable the parser altogether. This can yield fairly significant compilation + set #TOML_ENABLE_PARSER to `0` to disable the parser altogether. This can yield fairly significant compilation speedups since the parser accounts for a good chunk of the library's code. @@ -428,7 +436,7 @@ \subsection mainpage-adding-lib-conan Conan - Add `tomlplusplus/2.5.0` to your conanfile. + Add `tomlplusplus/3.0.0` to your conanfile. @@ -436,7 +444,7 @@ Add `tomlpp` to your `package.json5`, e.g.: \bash depends: [ - 'tomlpp^2.5.0', + 'tomlpp^3.0.0', ] \ebash @@ -463,7 +471,7 @@ FetchContent_Declare( tomlplusplus GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git - GIT_TAG v2.5.0 + GIT_TAG v3.0.0 ) FetchContent_MakeAvailable(tomlplusplus) \endcode diff --git a/include/toml++/impl/array.h b/include/toml++/impl/array.h index 97fa067..14499aa 100644 --- a/include/toml++/impl/array.h +++ b/include/toml++/impl/array.h @@ -1358,11 +1358,11 @@ TOML_NAMESPACE_START /// @} -#if TOML_ENABLE_TOML_FORMATTER +#if TOML_ENABLE_FORMATTERS /// \brief Prints the array out to a stream as formatted TOML. /// - /// \availability This operator is only available when #TOML_ENABLE_TOML_FORMATTER is enabled. + /// \availability This operator is only available when #TOML_ENABLE_FORMATTERS is enabled. friend std::ostream& operator<<(std::ostream& lhs, const array& rhs) { impl::print_to_stream(lhs, rhs); diff --git a/include/toml++/impl/formatter.h b/include/toml++/impl/formatter.h index 27e0f67..d4be068 100644 --- a/include/toml++/impl/formatter.h +++ b/include/toml++/impl/formatter.h @@ -4,6 +4,9 @@ // SPDX-License-Identifier: MIT #pragma once +#include "preprocessor.h" +#if TOML_ENABLE_FORMATTERS + #include "forward_declarations.h" #include "print_to_stream.h" #include "header_start.h" @@ -13,9 +16,15 @@ TOML_IMPL_NAMESPACE_START { struct formatter_constants { - std::string_view pos_inf; - std::string_view neg_inf; - std::string_view nan; + format_flags mandatory_flags; + format_flags ignored_flags; + + std::string_view float_pos_inf; + std::string_view float_neg_inf; + std::string_view float_nan; + + std::string_view bool_true; + std::string_view bool_false; }; struct formatter_config @@ -34,6 +43,7 @@ TOML_IMPL_NAMESPACE_START const formatter_constants* constants_; formatter_config config_; size_t indent_columns_; + format_flags int_format_mask_; std::ostream* stream_; // int indent_; // these are set in attach() bool naked_newline_; // @@ -90,47 +100,12 @@ TOML_IMPL_NAMESPACE_START return !!(config_.flags & format_flags::indent_sub_tables); } - TOML_PURE_INLINE_GETTER - bool quote_dates_and_times() const noexcept - { - return !!(config_.flags & format_flags::quote_dates_and_times); - } - - TOML_PURE_INLINE_GETTER - bool quote_infinities_and_nans() const noexcept - { - return !!(config_.flags & format_flags::quote_infinities_and_nans); - } - TOML_PURE_INLINE_GETTER bool literal_strings_allowed() const noexcept { return !!(config_.flags & format_flags::allow_literal_strings); } - TOML_PURE_INLINE_GETTER - bool multi_line_strings_allowed() const noexcept - { - return !!(config_.flags & format_flags::allow_multi_line_strings); - } - - TOML_PURE_INLINE_GETTER - bool value_format_flags_allowed() const noexcept - { - return !!(config_.flags & format_flags::allow_value_format_flags); - } - - TOML_PURE_INLINE_GETTER - bool naked_newline() const noexcept - { - return naked_newline_; - } - - void clear_naked_newline() noexcept - { - naked_newline_ = false; - } - TOML_API void attach(std::ostream& stream) noexcept; @@ -143,6 +118,12 @@ TOML_IMPL_NAMESPACE_START TOML_API void print_indent(); + TOML_API + void print_unformatted(char); + + TOML_API + void print_unformatted(std::string_view); + TOML_API void print_string(std::string_view str, bool allow_multi_line = true, bool allow_bare = false); @@ -183,3 +164,4 @@ TOML_IMPL_NAMESPACE_END; /// \endcond #include "header_end.h" +#endif // TOML_ENABLE_FORMATTERS diff --git a/include/toml++/impl/formatter.inl b/include/toml++/impl/formatter.inl index 73fed42..ac7fe69 100644 --- a/include/toml++/impl/formatter.inl +++ b/include/toml++/impl/formatter.inl @@ -4,12 +4,13 @@ // SPDX-License-Identifier: MIT #pragma once -//# {{ #include "preprocessor.h" +//# {{ #if !TOML_IMPLEMENTATION #error This is an implementation-only header. #endif //# }} +#if TOML_ENABLE_FORMATTERS #include "formatter.h" #include "print_to_stream.h" @@ -38,9 +39,15 @@ TOML_IMPL_NAMESPACE_START { TOML_ASSERT(source_ != nullptr); + config_.flags = (config_.flags | constants_->mandatory_flags) & ~constants_->ignored_flags; + indent_columns_ = {}; for (auto c : config_.indent) indent_columns_ += c == '\t' ? 4u : 1u; + + int_format_mask_ = config_.flags + & (format_flags::allow_binary_integers | format_flags::allow_octal_integers + | format_flags::allow_hexadecimal_integers); } TOML_EXTERNAL_LINKAGE @@ -77,6 +84,20 @@ TOML_IMPL_NAMESPACE_START } } + TOML_EXTERNAL_LINKAGE + void formatter::print_unformatted(char c) + { + print_to_stream(*stream_, c); + naked_newline_ = false; + } + + TOML_EXTERNAL_LINKAGE + void formatter::print_unformatted(std::string_view str) + { + print_to_stream(*stream_, str); + naked_newline_ = false; + } + TOML_EXTERNAL_LINKAGE void formatter::print_string(std::string_view str, bool allow_multi_line, bool allow_bare) { @@ -88,7 +109,7 @@ TOML_IMPL_NAMESPACE_START return; } - auto multi_line = allow_multi_line && multi_line_strings_allowed(); + auto multi_line = allow_multi_line && !!(config_.flags & format_flags::allow_multi_line_strings); if (multi_line || literals || allow_bare) { utf8_decoder decoder; @@ -148,26 +169,49 @@ TOML_IMPL_NAMESPACE_START TOML_EXTERNAL_LINKAGE void formatter::print(const value& val) { - if (value_format_flags_allowed() && *val >= 0) - { - static constexpr auto value_format_flags = - value_flags::format_as_binary | value_flags::format_as_octal | value_flags::format_as_hexadecimal; - const auto fmt = val.flags() & value_format_flags; - if (fmt != value_flags::none) - { - switch (fmt) - { - case value_flags::format_as_binary: print_to_stream(*stream_, "0b"sv); break; - case value_flags::format_as_octal: print_to_stream(*stream_, "0o"sv); break; - case value_flags::format_as_hexadecimal: print_to_stream(*stream_, "0x"sv); break; - default: TOML_UNREACHABLE; - } - } - print_to_stream(*stream_, *val, fmt); - } - else - print_to_stream(*stream_, *val); naked_newline_ = false; + + if (*val >= 0 && !!int_format_mask_) + { + static constexpr auto value_flags_mask = + value_flags::format_as_binary | value_flags::format_as_octal | value_flags::format_as_hexadecimal; + + const auto fmt = val.flags() & value_flags_mask; + switch (fmt) + { + case value_flags::format_as_binary: + if (!!(int_format_mask_ & format_flags::allow_binary_integers)) + { + print_to_stream(*stream_, "0b"sv); + print_to_stream(*stream_, *val, fmt); + return; + } + break; + + case value_flags::format_as_octal: + if (!!(int_format_mask_ & format_flags::allow_octal_integers)) + { + print_to_stream(*stream_, "0o"sv); + print_to_stream(*stream_, *val, fmt); + return; + } + break; + + case value_flags::format_as_hexadecimal: + if (!!(int_format_mask_ & format_flags::allow_hexadecimal_integers)) + { + print_to_stream(*stream_, "0x"sv); + print_to_stream(*stream_, *val, fmt); + return; + } + break; + + default: break; + } + } + + // fallback to decimal + print_to_stream(*stream_, *val); } TOML_EXTERNAL_LINKAGE @@ -176,16 +220,16 @@ TOML_IMPL_NAMESPACE_START const std::string_view* inf_nan = nullptr; switch (fpclassify(*val)) { - case fp_class::neg_inf: inf_nan = &constants_->neg_inf; break; - case fp_class::pos_inf: inf_nan = &constants_->pos_inf; break; - case fp_class::nan: inf_nan = &constants_->nan; break; + case fp_class::neg_inf: inf_nan = &constants_->float_neg_inf; break; + case fp_class::pos_inf: inf_nan = &constants_->float_pos_inf; break; + case fp_class::nan: inf_nan = &constants_->float_nan; break; case fp_class::ok: print_to_stream(*stream_, *val); break; default: TOML_UNREACHABLE; } if (inf_nan) { - if (quote_infinities_and_nans()) + if (!!(config_.flags & format_flags::quote_infinities_and_nans)) print_to_stream_bookended(*stream_, *inf_nan, '"'); else print_to_stream(*stream_, *inf_nan); @@ -197,14 +241,13 @@ TOML_IMPL_NAMESPACE_START TOML_EXTERNAL_LINKAGE void formatter::print(const value& val) { - print_to_stream(*stream_, *val); - naked_newline_ = false; + print_unformatted(*val ? constants_->bool_true : constants_->bool_false); } TOML_EXTERNAL_LINKAGE void formatter::print(const value& val) { - if (quote_dates_and_times()) + if (!!(config_.flags & format_flags::quote_dates_and_times)) print_to_stream_bookended(*stream_, *val, literal_strings_allowed() ? '\'' : '"'); else print_to_stream(*stream_, *val); @@ -214,7 +257,7 @@ TOML_IMPL_NAMESPACE_START TOML_EXTERNAL_LINKAGE void formatter::print(const value