From de07ba718794718a767cfc03dfc14b8d6a20ea95 Mon Sep 17 00:00:00 2001 From: Mark Gillard Date: Mon, 13 Jul 2020 21:18:04 +0300 Subject: [PATCH] string value serialization now emits literals where possible (closes #43) also added support for wide strings on Windows (closes #42): - added wide-string path arg overloads of `parse()` and `parse_file()` - added wide-string support to all relevant `table` and `array` ops - added `std::wstring` support to `node::value()` and `node::value_or()` - added `std::wstring` support to `node_view::value()` and `node_view::value_or()` - added wide-string overloads of `table::operator[]` - added wide-string overloads of `node_view::operator[]` - added `source_region::wide_path()` - added `TOML_WINDOWS_COMPAT` switch for explicitly enabling/disabling this stuff also: - fixed internal macro `assert_or_assume` leaking out of `toml_parser.hpp` - deprecated `node_view::get()` in favour of `node_view::node()` - minor documentation fixes - minor cleanup --- .editorconfig | 5 +- .gitattributes | 1 + README.md | 32 +- docs/Doxyfile | 90 +- docs/main_page.dox | 10 +- examples/example.toml | 11 +- examples/utf8_console.h | 2 +- include/toml++/toml.h | 8 +- include/toml++/toml_array.h | 13 +- include/toml++/toml_common.h | 94 +- include/toml++/toml_date_time.h | 8 +- include/toml++/toml_default_formatter.h | 27 +- include/toml++/toml_default_formatter.hpp | 88 ++ include/toml++/toml_formatter.h | 112 ++- include/toml++/toml_instantiations.hpp | 51 +- include/toml++/toml_json_formatter.h | 7 +- include/toml++/toml_json_formatter.hpp | 2 +- include/toml++/toml_node_view.h | 48 +- include/toml++/toml_parse_error.h | 6 +- include/toml++/toml_parser.h | 234 ++++- include/toml++/toml_parser.hpp | 87 +- include/toml++/toml_preprocessor.h | 52 +- include/toml++/toml_print_to_stream.h | 2 +- include/toml++/toml_table.h | 308 +++++- include/toml++/toml_table.hpp | 51 + include/toml++/toml_utf8_streams.h | 13 +- include/toml++/toml_value.h | 78 +- include/toml++/toml_version.h | 2 +- meson.build | 2 +- tests/impl_toml.cpp | 4 + tests/manipulating_values.cpp | 6 +- tests/meson.build | 1 + tests/settings.h | 2 +- tests/tests.h | 14 +- tests/windows_compat.cpp | 77 ++ toml.hpp | 1028 +++++++++++++++------ vs/test_char.vcxproj | 1 + vs/test_char8.vcxproj | 1 + vs/test_char8_noexcept.vcxproj | 1 + vs/test_char8_strict.vcxproj | 1 + vs/test_char8_strict_noexcept.vcxproj | 1 + vs/test_char_noexcept.vcxproj | 1 + vs/test_char_strict.vcxproj | 1 + vs/test_char_strict_noexcept.vcxproj | 1 + vs/test_x86_char.vcxproj | 1 + vs/test_x86_char8.vcxproj | 1 + vs/test_x86_char8_noexcept.vcxproj | 1 + vs/test_x86_char8_strict.vcxproj | 1 + vs/test_x86_char8_strict_noexcept.vcxproj | 1 + vs/test_x86_char_noexcept.vcxproj | 1 + vs/test_x86_char_strict.vcxproj | 1 + vs/test_x86_char_strict_noexcept.vcxproj | 1 + 52 files changed, 1980 insertions(+), 612 deletions(-) create mode 100644 tests/windows_compat.cpp diff --git a/.editorconfig b/.editorconfig index c421736..278b023 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,10 +10,13 @@ end_of_line = lf [*.{gitattributes, yml, props, vcxproj}] indent_style = space +[{Doxyfile, Doxyfile-mcss}] +indent_style = space + [*.{natvis, props, vcxproj, sln, runsettings}] end_of_line = crlf -[*.{c, cpp, cxx, h, hpp, hxx, inl, toml, xml, py, natvis, props, build, vcxproj, runsettings, yml, ini, json}] +[*.{c, cpp, cxx, dox, h, hpp, hxx, inl, toml, xml, py, natvis, props, build, vcxproj, runsettings, yml, ini, json}] charset = utf-8 trim_trailing_whitespace = true diff --git a/.gitattributes b/.gitattributes index 20f6035..30085cf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,6 +2,7 @@ *.c text encoding=UTF-8 eol=lf *.cpp text encoding=UTF-8 eol=lf *.cxx text encoding=UTF-8 eol=lf +*.dox text encoding=UTF-8 eol=lf *.h text encoding=UTF-8 eol=lf *.hpp text encoding=UTF-8 eol=lf *.hxx text encoding=UTF-8 eol=lf diff --git a/README.md b/README.md index 0295228..08c09f4 100644 --- a/README.md +++ b/README.md @@ -113,21 +113,22 @@ single-header flavour, however specifying the option `"multiple_headers": True` 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_ALL_INLINE` | boolean | `1` | Disable this to explicitly control where toml++'s implementation is compiled (e.g. as part of a library). | -| `TOML_API` | define | undefined | API annotation to add to public symbols (e.g. `__declspec(dllexport)` on Windows). | -| `TOML_ASSERT(expr)` | function macro | `assert(expr)`
(or undefined) | Sets the assert function used by the library. | -| `TOML_CHAR_8_STRINGS` | boolean | `0` | Uses C++20 [char8_t]-based strings as the toml string data type. **_Experimental!_** | -| `TOML_CONFIG_HEADER` | string literal | undefined | Includes the given header file before the rest of the library. | -| `TOML_EXCEPTIONS` | boolean | per your compiler's settings | Sets whether the library uses exceptions. | -| `TOML_IMPLEMENTATION` | define | undefined | Define this to enable compilation of the library's implementation. Meaningless if `TOML_ALL_INLINE` is `1`.| -| `TOML_LARGE_FILES` | boolean | `0` | Uses 32-bit integers for line and column indices (instead of 16-bit). | -| `TOML_OPTIONAL_TYPE` | type name | undefined | Overrides the `optional` type used by the library if you need [something better than std::optional]. | -| `TOML_PARSER` | boolean | `1` | Disable this to prevent inclusion of the parser-related parts of the library if you don't need them. | -| `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. | -| `TOML_UNRELEASED_FEATURES` | boolean | `0` | Enables support for [unreleased TOML language features] not yet part of a [numbered version]. | +| Option | Type | Default | Description | +|----------------------------|:--------------:|-----------------------------------|----------------------------------------------------------------------------------------------------------------------| +| `TOML_ALL_INLINE` | boolean | `1` | Disable this to explicitly control where toml++'s implementation is compiled (e.g. as part of a library). | +| `TOML_API` | define | undefined | API annotation to add to public symbols (e.g. `__declspec(dllexport)` on Windows). | +| `TOML_ASSERT(expr)` | function macro | `assert(expr)`
(or undefined) | Sets the assert function used by the library. | +| `TOML_CHAR_8_STRINGS` | boolean | `0` | Uses C++20 [char8_t]-based strings as the toml string data type. **_Experimental!_** | +| `TOML_CONFIG_HEADER` | string literal | undefined | Includes the given header file before the rest of the library. | +| `TOML_EXCEPTIONS` | boolean | per your compiler's settings | Sets whether the library uses exceptions. | +| `TOML_IMPLEMENTATION` | define | undefined | Define this to enable compilation of the library's implementation. Meaningless if `TOML_ALL_INLINE` is `1`. | +| `TOML_LARGE_FILES` | boolean | `0` | Uses 32-bit integers for line and column indices (instead of 16-bit). | +| `TOML_OPTIONAL_TYPE` | type name | undefined | Overrides the `optional` type used by the library if you need [something better than std::optional]. | +| `TOML_PARSER` | boolean | `1` | Disable this to prevent inclusion of the parser-related parts of the library if you don't need them. | +| `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. | +| `TOML_UNRELEASED_FEATURES` | boolean | `0` | Enables support for [unreleased TOML language features] not yet part of a [numbered version]. | +| `TOML_WINDOWS_COMPAT` | boolean | `1` on Windows | Enables support for transparent conversion between wide and narrow strings in some places when building for Windows. | > ℹ _A number of these have ABI implications; the library uses inline namespaces to prevent you from accidentally linking incompatible combinations together._ @@ -180,6 +181,7 @@ UTF-8 decoding is performed using a state machine based on Bjoern Hoehrmann's '[ - **[@bjadamson](https://github.com/bjadamson)** - Reported some bugs and helped design a new feature - **[@bobfang1992](https://github.com/bobfang1992)** - Reported a bug and created a [wrapper in python](https://github.com/bobfang1992/pytomlpp) - **[@GiulioRomualdi](https://github.com/GiulioRomualdi)** - Added cmake+meson support +- **[@levicki](https://github.com/levicki)** - Helped design some new features - **[@mosra](https://github.com/mosra)** - Created the awesome [m.css] used to generate the API docs - **[@ned14](https://github.com/ned14)** - Reported a bunch of bugs and helped design some new features - **[@okureta](https://github.com/okureta)** - Reported a bug diff --git a/docs/Doxyfile b/docs/Doxyfile index 931f816..db7182c 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -40,26 +40,26 @@ INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = \ - "cpp=@code{.cpp}" \ - "ecpp=@endcode" \ - "out=@code{.shell-session}" \ - "eout=@endcode" \ - "bash=@code{.sh}" \ - "ebash=@endcode" \ - "detail=@details" \ - "gh{1}=\1" \ - "gh2{2}=\2" \ - "godbolt{1}=Try this code on Compiler Explorer" \ - "m_div{1}=@xmlonly@endxmlonly" \ - "m_enddiv=@xmlonly@endxmlonly" \ - "m_span{1}=@xmlonly@endxmlonly" \ - "m_endspan=@xmlonly@endxmlonly" \ - "m_class{1}=@xmlonly@endxmlonly" \ - "m_footernavigation=@xmlonly@endxmlonly" \ - "m_examplenavigation{2}=@xmlonly@endxmlonly" \ - "m_keywords{1}=@xmlonly@endxmlonly" \ - "m_keyword{3}=@xmlonly@endxmlonly" \ - "m_enum_values_as_keywords=@xmlonly@endxmlonly" + "cpp=@code{.cpp}" \ + "ecpp=@endcode" \ + "out=@code{.shell-session}" \ + "eout=@endcode" \ + "bash=@code{.sh}" \ + "ebash=@endcode" \ + "detail=@details" \ + "gh{1}=\1" \ + "gh2{2}=\2" \ + "godbolt{1}=Try this code on Compiler Explorer" \ + "m_div{1}=@xmlonly@endxmlonly" \ + "m_enddiv=@xmlonly@endxmlonly" \ + "m_span{1}=@xmlonly@endxmlonly" \ + "m_endspan=@xmlonly@endxmlonly" \ + "m_class{1}=@xmlonly@endxmlonly" \ + "m_footernavigation=@xmlonly@endxmlonly" \ + "m_examplenavigation{2}=@xmlonly@endxmlonly" \ + "m_keywords{1}=@xmlonly@endxmlonly" \ + "m_keyword{3}=@xmlonly@endxmlonly" \ + "m_enum_values_as_keywords=@xmlonly@endxmlonly" TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO @@ -309,30 +309,32 @@ EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = NO INCLUDE_PATH = INCLUDE_FILE_PATTERNS = -PREDEFINED = TOML_DOXYGEN=1 \ - TOML_ALWAYS_INLINE=inline \ - TOML_MAY_THROW= \ - TOML_NODISCARD_CTOR= \ - TOML_EXTERNAL_LINKAGE= \ - TOML_API= \ - TOML_ABI_NAMESPACE_START(x)= \ - TOML_ABI_NAMESPACE_END= \ - TOML_ATTR(...)= \ - TOML_PUSH_WARNINGS= \ - TOML_DISABLE_SWITCH_WARNINGS= \ - TOML_DISABLE_INIT_WARNINGS= \ - TOML_DISABLE_VTABLE_WARNINGS= \ - TOML_DISABLE_PADDING_WARNINGS= \ - TOML_DISABLE_FLOAT_WARNINGS= \ - TOML_DISABLE_SHADOW_WARNINGS= \ - TOML_DISABLE_SUGGEST_WARNINGS= \ - TOML_DISABLE_ALL_WARNINGS= \ - TOML_POP_WARNINGS= \ - TOML_ASYMMETRICAL_EQUALITY_OPS(...)= \ - __cplusplus=201703L \ - __has_include(x)=0 \ - __has_attribute(x)=0 \ - __cpp_lib_char8_t=201811L +PREDEFINED = DOXYGEN=1 \ + NDEBUG=1 \ + __cplusplus=201703L \ + __has_include(x)=0 \ + __has_attribute(x)=0 \ + __cpp_lib_char8_t=201811L \ + TOML_ALWAYS_INLINE=inline \ + TOML_MAY_THROW= \ + TOML_NODISCARD_CTOR= \ + TOML_EXTERNAL_LINKAGE= \ + TOML_API= \ + TOML_ABI_NAMESPACE_START(...)= \ + TOML_ABI_NAMESPACE_BOOL(...)= \ + TOML_ABI_NAMESPACE_END= \ + TOML_ATTR(...)= \ + TOML_PUSH_WARNINGS= \ + TOML_DISABLE_SWITCH_WARNINGS= \ + TOML_DISABLE_INIT_WARNINGS= \ + TOML_DISABLE_VTABLE_WARNINGS= \ + TOML_DISABLE_PADDING_WARNINGS= \ + TOML_DISABLE_FLOAT_WARNINGS= \ + TOML_DISABLE_SHADOW_WARNINGS= \ + TOML_DISABLE_SUGGEST_WARNINGS= \ + TOML_DISABLE_ALL_WARNINGS= \ + TOML_POP_WARNINGS= \ + TOML_ASYMMETRICAL_EQUALITY_OPS(...)= EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = NO #--------------------------------------------------------------------------- diff --git a/docs/main_page.dox b/docs/main_page.dox index 3efd11c..7ec5fdd 100644 --- a/docs/main_page.dox +++ b/docs/main_page.dox @@ -227,10 +227,10 @@ /// /// // different ways of directly querying data /// std::optional str1 = tbl["str"].value(); -/// std::optional str2 = tbl["str"].value(); -/// std::string_view str3 = tbl["str"].value_or(""); -/// std::string str4 = tbl["str"].value_or(std::string{}); -/// std::string& str5 = tbl["str"].ref(); // ~~dangerous~~, but fast +/// std::optional str2 = tbl["str"].value(); +/// std::string_view str3 = tbl["str"].value_or(""); +/// std::string str4 = tbl["str"].value_or(std::string{}); +/// std::string& str5 = tbl["str"].ref(); // ~~dangerous~~, but fast /// /// std::cout << *str1 << "\n"; /// std::cout << *str2 << "\n"; @@ -443,7 +443,7 @@ /// /// \subsection mainpage-adding-lib-conan Conan /// Add `tomlplusplus/1.3.3` to your conanfile. This adds the single-header version by default, but you can specify the -/// regular version using `multiple_headers": True`. +/// regular version using `"multiple_headers": True`. /// ////////////////////////////////// /// diff --git a/examples/example.toml b/examples/example.toml index e9102d1..9612b53 100644 --- a/examples/example.toml +++ b/examples/example.toml @@ -34,8 +34,15 @@ odt1 = 1979-05-27T07:32:00Z odt2 = 1979-05-27T00:32:00-07:00 odt3 = 1979-05-27T00:32:00.999999-07:00 -# unicode -kosme = "κόσμε" +# strings +str1 = """ +This is a +multi-line +string. +""" +str2 = "This is also\na multi-line\nstring." +str3 = 'C:\highway\to\the\danger\zone' +kosme = "κόσμε" # unicode! arr = [ 'this', 'is', 'a', 'long', 'array', 'with', 16, 'elements.', 'it', 'should', 'be', 'printed', 'as', 'a', 'multiline', 'array.'] diff --git a/examples/utf8_console.h b/examples/utf8_console.h index dc3d7b7..f519760 100644 --- a/examples/utf8_console.h +++ b/examples/utf8_console.h @@ -29,7 +29,7 @@ #define NOMETAFILE // - typedef METAFILEPICT #define NOMINMAX // - Macros min(a,b) and max(a,b) #define NOMSG // - typedef MSG and associated routines - #define NONLS // - All NLS defines and routines + //#define NONLS // - All NLS defines and routines #define NOOPENFILE // - OpenFile(), OemToAnsi, AnsiToOem, and OF_* #define NOPROFILER // - Profiler interface. #define NORASTEROPS // - Binary and Tertiary raster ops diff --git a/include/toml++/toml.h b/include/toml++/toml.h index bea5002..af39ec9 100644 --- a/include/toml++/toml.h +++ b/include/toml++/toml.h @@ -69,7 +69,6 @@ #undef TOML_UNREACHABLE #undef TOML_INTERFACE #undef TOML_EMPTY_BASES - #undef TOML_CPP_VERSION #undef TOML_CPP #undef TOML_MAY_THROW #undef TOML_NO_DEFAULT_CASE @@ -82,7 +81,6 @@ #undef TOML_LANG_HIGHER_THAN #undef TOML_LANG_AT_LEAST #undef TOML_LANG_UNRELEASED - #undef TOML_STRING_PREFIX_1 #undef TOML_STRING_PREFIX #undef TOML_UNDEF_MACROS #undef TOML_RELOPS_REORDERING @@ -96,9 +94,15 @@ #undef TOML_TRIVIAL_ABI #undef TOML_ABI_NAMESPACES #undef TOML_ABI_NAMESPACE_START + #undef TOML_ABI_NAMESPACE_BOOL #undef TOML_ABI_NAMESPACE_END #undef TOML_PARSER_TYPENAME #undef TOML_LAUNDER + #undef TOML_CONCAT_1 + #undef TOML_CONCAT + #undef TOML_EVAL_BOOL_1 + #undef TOML_EVAL_BOOL_0 + #undef TOML_HAS_CUSTOM_OPTIONAL_TYPE #endif //# {{ diff --git a/include/toml++/toml_array.h b/include/toml++/toml_array.h index 48c7f8d..c3f1d38 100644 --- a/include/toml++/toml_array.h +++ b/include/toml++/toml_array.h @@ -185,11 +185,20 @@ namespace toml::impl } else { + static_assert( + !is_wide_string || TOML_WINDOWS_COMPAT, + "Instantiating values from wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); static_assert( is_value_or_promotable, "Value initializers must be (or be promotable to) one of the TOML value types" ); - return new value{ std::forward(val) }; + #if TOML_WINDOWS_COMPAT + if constexpr (is_wide_string) + return new value{ narrow(std::forward(val)) }; + else + #endif + return new value{ std::forward(val) }; } } @@ -656,7 +665,6 @@ namespace toml /// \returns Iterator to the first node immediately following the last removed node. iterator erase(const_iterator first, const_iterator last) noexcept; - /// \brief Resizes the array. /// /// \detail \cpp @@ -943,6 +951,7 @@ namespace toml return static_cast(static_cast(*this).flatten()); } + /// \brief Prints the array out to a stream as formatted TOML. template friend std::basic_ostream& operator << (std::basic_ostream&, const array&); }; diff --git a/include/toml++/toml_common.h b/include/toml++/toml_common.h index 2590041..ecf466d 100644 --- a/include/toml++/toml_common.h +++ b/include/toml++/toml_common.h @@ -22,7 +22,7 @@ TOML_DISABLE_ALL_WARNINGS #include #include #include -#ifndef TOML_OPTIONAL_TYPE +#if !TOML_HAS_CUSTOM_OPTIONAL_TYPE #include #endif @@ -40,6 +40,10 @@ TOML_POP_WARNINGS ////////// FORWARD DECLARATIONS & TYPEDEFS +TOML_PUSH_WARNINGS +TOML_DISABLE_PADDING_WARNINGS +TOML_DISABLE_SHADOW_WARNINGS + /// \brief The root namespace for all toml++ functions and types. namespace toml { @@ -78,9 +82,30 @@ namespace toml #endif - TOML_PUSH_WARNINGS - TOML_DISABLE_PADDING_WARNINGS - TOML_DISABLE_SHADOW_WARNINGS // false positive on gcc + #if TOML_WINDOWS_COMPAT + namespace impl + { + [[nodiscard]] TOML_API std::string narrow_char(std::wstring_view) noexcept; + #ifdef __cpp_lib_char8_t + [[nodiscard]] TOML_API std::u8string narrow_char8(std::wstring_view) noexcept; + #endif + template + [[nodiscard]] auto narrow(std::wstring_view str) noexcept + { + #ifdef __cpp_lib_char8_t + if constexpr (std::is_same_v) + return narrow_char8(str); + else + #endif + return narrow_char(str); + } + + [[nodiscard]] TOML_API std::wstring widen(std::string_view) noexcept; + #ifdef __cpp_lib_char8_t + [[nodiscard]] TOML_API std::wstring widen(std::u8string_view) noexcept; + #endif + } + #endif #if !TOML_DOXYGEN @@ -97,15 +122,11 @@ namespace toml template class default_formatter; template class json_formatter; - #ifdef TOML_OPTIONAL_TYPE - TOML_ABI_NAMESPACE_START(custopt) - #else - TOML_ABI_NAMESPACE_START(stdopt) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt) struct date_time; - TOML_ABI_NAMESPACE_END // TOML_OPTIONAL_TYPE + TOML_ABI_NAMESPACE_END // TOML_HAS_CUSTOM_OPTIONAL_TYPE #endif // !TOML_DOXYGEN @@ -124,9 +145,7 @@ namespace toml date_time ///< The node is a toml::value. }; - TOML_POP_WARNINGS // TOML_DISABLE_PADDING_WARNINGS, TOML_DISABLE_SHADOW_WARNINGS - - #ifdef TOML_OPTIONAL_TYPE + #if TOML_HAS_CUSTOM_OPTIONAL_TYPE template using optional = TOML_OPTIONAL_TYPE; @@ -157,11 +176,7 @@ namespace toml /// \brief A pointer to a shared string resource containing a source path. using source_path_ptr = std::shared_ptr; - #if TOML_LARGE_FILES - TOML_ABI_NAMESPACE_START(lf) - #else - TOML_ABI_NAMESPACE_START(sf) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_LARGE_FILES, lf, sf) /// \brief A source document line-and-column pair. /// @@ -276,6 +291,23 @@ namespace toml /// /// \remarks This will be `nullptr` if no path was provided to toml::parse(). source_path_ptr path; + + #if TOML_WINDOWS_COMPAT + + /// \brief The path to the corresponding source document as a wide-string. + /// + /// \remarks This will return an empty optional if no path was provided to toml::parse(). + /// + /// \attention This function is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] + optional wide_path() const noexcept + { + if (!path || path->empty()) + return {}; + return { impl::widen(*path) }; + } + + #endif }; TOML_ABI_NAMESPACE_END // TOML_LARGE_FILES @@ -346,6 +378,15 @@ namespace toml::impl || std::is_same_v || std::is_same_v; + template + inline constexpr bool is_wide_string = is_one_of< + std::decay_t, + const wchar_t*, + wchar_t*, + std::wstring_view, + std::wstring + >; + template inline constexpr bool is_value_or_promotable = is_value @@ -358,6 +399,9 @@ namespace toml::impl || std::is_same_v || std::is_same_v || std::is_same_v + #if TOML_WINDOWS_COMPAT + || is_wide_string + #endif #ifdef TOML_SMALL_FLOAT_TYPE || std::is_same_v #endif @@ -399,6 +443,18 @@ namespace toml::impl template struct value_promoter { using type = string; }; template <> struct value_promoter { using type = string; }; template <> struct value_promoter { using type = string; }; + #if TOML_WINDOWS_COMPAT + template struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template <> struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template <> struct value_promoter { using type = string; }; + template <> struct value_promoter { using type = string; }; + template <> struct value_promoter { using type = string; }; + #endif template <> struct value_promoter { using type = int64_t; }; template <> struct value_promoter { using type = int64_t; }; template <> struct value_promoter { using type = int64_t; }; @@ -592,3 +648,5 @@ namespace toml }; template inserter(T&&) -> inserter; } + +TOML_POP_WARNINGS // TOML_DISABLE_PADDING_WARNINGS, TOML_DISABLE_SHADOW_WARNINGS diff --git a/include/toml++/toml_date_time.h b/include/toml++/toml_date_time.h index c00c3cf..1272dcd 100644 --- a/include/toml++/toml_date_time.h +++ b/include/toml++/toml_date_time.h @@ -300,11 +300,7 @@ namespace toml extern template TOML_API std::ostream& operator << (std::ostream&, const time_offset&); #endif - #ifdef TOML_OPTIONAL_TYPE - TOML_ABI_NAMESPACE_START(custopt) - #else - TOML_ABI_NAMESPACE_START(stdopt) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt) /// \brief A date-time. struct date_time @@ -407,7 +403,7 @@ namespace toml } }; - TOML_ABI_NAMESPACE_END // TOML_OPTIONAL_TYPE + TOML_ABI_NAMESPACE_END // TOML_HAS_CUSTOM_OPTIONAL_TYPE /// \brief Prints a date_time out to a stream in RFC 3339 format. /// \detail \cpp diff --git a/include/toml++/toml_default_formatter.h b/include/toml++/toml_default_formatter.h index 89da694..c120a32 100644 --- a/include/toml++/toml_default_formatter.h +++ b/include/toml++/toml_default_formatter.h @@ -88,7 +88,11 @@ namespace toml } if (requiresQuotes) - base::print_quoted_string(str); + { + impl::print_to_stream('"', base::stream()); + impl::print_to_stream_with_escapes(str, base::stream()); + impl::print_to_stream('"', base::stream()); + } else impl::print_to_stream(str, base::stream()); } @@ -320,12 +324,16 @@ namespace toml public: + /// \brief The default flags for a default_formatter. + static constexpr format_flags default_flags + = format_flags::allow_literal_strings | format_flags::allow_multi_line_strings; + /// \brief Constructs a default formatter and binds it to a TOML object. /// /// \param source The source TOML object. /// \param flags Format option flags. TOML_NODISCARD_CTOR - explicit default_formatter(const toml::node& source, format_flags flags = {}) noexcept + explicit default_formatter(const toml::node& source, format_flags flags = default_flags) noexcept : base{ source, flags } {} @@ -377,12 +385,27 @@ namespace toml return lhs << default_formatter{ rhs }; } + template + TOML_EXTERNAL_LINKAGE + std::basic_ostream& operator << (std::basic_ostream& lhs, const value& rhs) + { + return lhs << default_formatter{ rhs }; + } + #if !TOML_ALL_INLINE extern template TOML_API std::ostream& operator << (std::ostream&, default_formatter&); extern template TOML_API std::ostream& operator << (std::ostream&, default_formatter&&); extern template TOML_API std::ostream& operator << (std::ostream&, const table&); extern template TOML_API std::ostream& operator << (std::ostream&, const array&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); #endif } TOML_POP_WARNINGS // TOML_DISABLE_SWITCH_WARNINGS, TOML_DISABLE_PADDING_WARNINGS + diff --git a/include/toml++/toml_default_formatter.hpp b/include/toml++/toml_default_formatter.hpp index 275b687..e41a295 100644 --- a/include/toml++/toml_default_formatter.hpp +++ b/include/toml++/toml_default_formatter.hpp @@ -206,3 +206,91 @@ namespace toml } TOML_POP_WARNINGS // TOML_DISABLE_SWITCH_WARNINGS, TOML_DISABLE_FLOAT_WARNINGS + +// implementations of windows wide string nonsense +#if TOML_WINDOWS_COMPAT + +TOML_PUSH_WARNINGS +TOML_DISABLE_ALL_WARNINGS +#include // fuckkkk :( +TOML_POP_WARNINGS + +namespace toml::impl +{ + TOML_API + TOML_EXTERNAL_LINKAGE + std::string narrow_char(std::wstring_view str) noexcept + { + if (str.empty()) + return {}; + + std::string s; + const auto len = WideCharToMultiByte( + 65001, 0, str.data(), static_cast(str.length()), nullptr, 0, nullptr, nullptr + ); + if (len) + { + s.resize(static_cast(len)); + WideCharToMultiByte(65001, 0, str.data(), static_cast(str.length()), s.data(), len, nullptr, nullptr); + } + return s; + } + + #ifdef __cpp_lib_char8_t + + TOML_API + TOML_EXTERNAL_LINKAGE + std::u8string narrow_char8(std::wstring_view str) noexcept + { + if (str.empty()) + return {}; + + std::u8string s; + const auto len = WideCharToMultiByte( + 65001, 0, str.data(), static_cast(str.length()), nullptr, 0, nullptr, nullptr + ); + if (len) + { + s.resize(static_cast(len)); + WideCharToMultiByte( + 65001, 0, str.data(), static_cast(str.length()), reinterpret_cast(s.data()), len, nullptr, nullptr + ); + } + return s; + } + + #endif // __cpp_lib_char8_t + + TOML_API + TOML_EXTERNAL_LINKAGE + std::wstring widen(std::string_view str) noexcept + { + if (str.empty()) + return {}; + + std::wstring s; + const auto len = MultiByteToWideChar(65001, 0, str.data(), static_cast(str.length()), nullptr, 0); + if (len) + { + s.resize(static_cast(len)); + MultiByteToWideChar(65001, 0, str.data(), static_cast(str.length()), s.data(), len); + } + return s; + } + + #ifdef __cpp_lib_char8_t + + TOML_API + TOML_EXTERNAL_LINKAGE + std::wstring widen(std::u8string_view str) noexcept + { + if (str.empty()) + return {}; + + return widen(std::string_view{ reinterpret_cast(str.data()), str.length() }); + } + + #endif // __cpp_lib_char8_t +} + +#endif // TOML_WINDOWS_COMPAT diff --git a/include/toml++/toml_formatter.h b/include/toml++/toml_formatter.h index f69648b..c613290 100644 --- a/include/toml++/toml_formatter.h +++ b/include/toml++/toml_formatter.h @@ -15,8 +15,17 @@ namespace toml /// \brief Format flags for modifying how TOML data is printed to streams. enum class format_flags : uint8_t { + /// \brief None. none, - quote_dates_and_times = 1 + + /// \brief Sates and times will be emitted as quoted strings. + quote_dates_and_times = 1, + + /// \brief Strings will be emitted as single-quoted literal strings where possible. + allow_literal_strings = 2, + + /// \brief Strings containing newlines will be emitted as triple-quoted 'multi-line' strings where possible. + allow_multi_line_strings = 4, }; [[nodiscard]] @@ -51,7 +60,6 @@ namespace toml::impl protected: [[nodiscard]] const toml::node& source() const noexcept { return *source_; } - [[nodiscard]] format_flags flags() const noexcept { return flags_; } [[nodiscard]] std::basic_ostream& stream() const noexcept { return *stream_; } static constexpr size_t indent_columns = 4; @@ -61,8 +69,28 @@ namespace toml::impl void increase_indent() noexcept { indent_++; } void decrease_indent() noexcept { indent_--; } - TOML_ALWAYS_INLINE - void clear_naked_newline() noexcept { naked_newline_ = false; } + [[nodiscard]] + bool quote_dates_and_times() const noexcept + { + return (flags_ & format_flags::quote_dates_and_times) != format_flags::none; + } + + [[nodiscard]] + bool literal_strings_allowed() const noexcept + { + return (flags_ & format_flags::allow_literal_strings) != format_flags::none; + } + + [[nodiscard]] + bool multi_line_strings_allowed() const noexcept + { + return (flags_ & format_flags::allow_multi_line_strings) != format_flags::none; + } + + void clear_naked_newline() noexcept + { + naked_newline_ = false; + } void attach(std::basic_ostream& stream) noexcept { @@ -94,17 +122,62 @@ namespace toml::impl } } - void print_quoted_string(toml::string_view str) + void print_quoted_string(toml::string_view str, bool allow_multi_line = true) { + auto literals = literal_strings_allowed(); if (str.empty()) - print_to_stream("\"\""sv, *stream_); + { + print_to_stream(literals ? "''"sv : "\"\""sv, *stream_); + clear_naked_newline(); + return; + } + + auto multi_line = allow_multi_line && multi_line_strings_allowed(); + if (multi_line || literals) + { + utf8_decoder decoder; + bool has_line_breaks = false; + bool has_control_chars = false; + bool has_single_quotes = false; + for (size_t i = 0; i < str.length() && !(has_line_breaks && has_control_chars && has_single_quotes); i++) + { + decoder(static_cast(str[i])); + if (decoder.error()) + { + has_line_breaks = false; + has_control_chars = true; //force "" + has_single_quotes = true; + break; + } + else if (decoder.has_code_point()) + { + if (is_line_break(decoder.codepoint)) + has_line_breaks = true; + else if (is_nontab_control_character(decoder.codepoint)) + has_control_chars = true; + else if (decoder.codepoint == U'\'') + has_single_quotes = true; + } + } + multi_line = multi_line && has_line_breaks; + literals = literals && !has_control_chars && !(!multi_line && has_single_quotes); + } + + if (literals) + { + const auto quot = multi_line ? "'''"sv : "'"sv; + print_to_stream(quot, *stream_); + print_to_stream(str, *stream_); + print_to_stream(quot, *stream_); + } else { - print_to_stream('"', *stream_); + const auto quot = multi_line ? R"(""")"sv : R"(")"sv; + print_to_stream(quot, *stream_); print_to_stream_with_escapes(str, *stream_); - print_to_stream('"', *stream_); + print_to_stream(quot, *stream_); } - naked_newline_ = false; + clear_naked_newline(); } template @@ -123,17 +196,18 @@ namespace toml::impl if constexpr (is_dt) { - if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none) - print_to_stream('"', *stream_); - } - - *stream_ << val; - - if constexpr (is_dt) - { - if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none) - print_to_stream('"', *stream_); + if (quote_dates_and_times()) + { + const auto quot = literal_strings_allowed() ? '\'' : '"'; + print_to_stream(quot, *stream_); + print_to_stream(*val, *stream_); + print_to_stream(quot, *stream_); + } + else + print_to_stream(*val, *stream_); } + else + print_to_stream(*val, *stream_); naked_newline_ = false; } diff --git a/include/toml++/toml_instantiations.hpp b/include/toml++/toml_instantiations.hpp index f736916..4917a79 100644 --- a/include/toml++/toml_instantiations.hpp +++ b/include/toml++/toml_instantiations.hpp @@ -24,6 +24,7 @@ TOML_POP_WARNINGS #include "toml_default_formatter.h" #include "toml_json_formatter.h" +// template instantiations namespace toml { // value<> @@ -77,26 +78,32 @@ namespace toml template TOML_API void print_floating_point_to_stream(float, std::ostream&, bool); template TOML_API void print_floating_point_to_stream(double, std::ostream&, bool); } - - // parser machinery - #if TOML_PARSER - - // parse error ostream - template TOML_API std::ostream& operator << (std::ostream&, const parse_error&); - - // parse() and parse_file() - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(parse_ex) - #else - TOML_ABI_NAMESPACE_START(parse_noex) - #endif - template TOML_API parse_result parse(std::istream&, std::string_view) TOML_MAY_THROW; - template TOML_API parse_result parse(std::istream&, std::string&&) TOML_MAY_THROW; - template TOML_API parse_result parse_file(std::string_view) TOML_MAY_THROW; - #ifdef __cpp_lib_char8_t - template TOML_API parse_result parse_file(std::u8string_view) TOML_MAY_THROW; - #endif - TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS - - #endif // TOML_PARSER } + + +// parser instantiations +#if TOML_PARSER + +#include "toml_parser.h" + +namespace toml +{ + // parse error ostream + template TOML_API std::ostream& operator << (std::ostream&, const parse_error&); + + // parse() and parse_file() + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, parse_ex, parse_noex) + + template TOML_API parse_result parse(std::istream&, std::string_view) TOML_MAY_THROW; + template TOML_API parse_result parse(std::istream&, std::string&&) TOML_MAY_THROW; + template TOML_API parse_result parse_file(std::string_view) TOML_MAY_THROW; + #ifdef __cpp_lib_char8_t + template TOML_API parse_result parse_file(std::u8string_view) TOML_MAY_THROW; + #endif + #if TOML_WINDOWS_COMPAT + template TOML_API parse_result parse_file(std::wstring_view) TOML_MAY_THROW; + #endif + + TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS +} +#endif // TOML_PARSER diff --git a/include/toml++/toml_json_formatter.h b/include/toml++/toml_json_formatter.h index 657b24e..231ad23 100644 --- a/include/toml++/toml_json_formatter.h +++ b/include/toml++/toml_json_formatter.h @@ -104,13 +104,16 @@ namespace toml public: + /// \brief The default flags for a json_formatter. + static constexpr format_flags default_flags = format_flags::quote_dates_and_times; + /// \brief Constructs a JSON formatter and binds it to a TOML object. /// /// \param source The source TOML object. /// \param flags Format option flags. TOML_NODISCARD_CTOR - explicit json_formatter(const toml::node& source, format_flags flags = {}) noexcept - : base{ source, flags | format_flags::quote_dates_and_times } + explicit json_formatter(const toml::node& source, format_flags flags = default_flags) noexcept + : base{ source, flags } {} template diff --git a/include/toml++/toml_json_formatter.hpp b/include/toml++/toml_json_formatter.hpp index ee895ed..b7c3abe 100644 --- a/include/toml++/toml_json_formatter.hpp +++ b/include/toml++/toml_json_formatter.hpp @@ -37,7 +37,7 @@ namespace toml base::print_newline(true); base::print_indent(); - base::print_quoted_string(k); + base::print_quoted_string(k, false); impl::print_to_stream(" : "sv, base::stream()); const auto type = v.type(); diff --git a/include/toml++/toml_node_view.h b/include/toml++/toml_node_view.h index 8b392aa..257c47c 100644 --- a/include/toml++/toml_node_view.h +++ b/include/toml++/toml_node_view.h @@ -48,7 +48,7 @@ namespace toml /// std::cout << tbl["products"][0]["keywords"] << std::endl; /// std::cout << "has product[2]: "sv << !!tbl["products"][2] << std::endl; /// std::cout << "product[2]: "sv << tbl["products"][2] << std::endl; - /// \ecpp + /// \ecpp /// /// \out /// "my hardware store" @@ -89,7 +89,10 @@ namespace toml /// \brief Returns true if the view references a node. [[nodiscard]] explicit operator bool() const noexcept { return node_ != nullptr; } /// \brief Returns the node that's being referenced by the view. - [[nodiscard]] viewed_type* get() const noexcept { return node_; } + [[nodiscard]] viewed_type* node() const noexcept { return node_; } + + [[nodiscard, deprecated("use node_view::node() instead (the name is better)")]] + viewed_type* get() const noexcept { return node_; } /// \brief Returns the type identifier for the viewed node. [[nodiscard]] node_type type() const noexcept { return node_ ? node_->type() : node_type::none; } @@ -296,13 +299,25 @@ namespace toml template >> [[nodiscard]] friend bool operator == (const node_view& lhs, const U& rhs) noexcept { - const auto val = lhs.as>(); - return val && *val == rhs; + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Comparison with wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); + + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return lhs == impl::narrow(rhs); + else + #endif + { + const auto val = lhs.as>(); + return val && *val == rhs; + } } TOML_ASYMMETRICAL_EQUALITY_OPS( - const node_view&, - const U&, - template >> + const node_view&, + const U&, + template >> ) /// \brief Returns true if the viewed node is an array with the same contents as the RHS initializer list. @@ -336,6 +351,25 @@ namespace toml return { nullptr }; } + #if TOML_WINDOWS_COMPAT + + /// \brief Returns a view of the selected subnode. + /// + /// \param key The key of the node to retrieve + /// + /// \returns A view of the selected node if this node represented a table and it contained a + /// value at the given key, or an empty view. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] node_view operator[] (std::wstring_view key) const noexcept + { + if (auto tbl = this->as_table()) + return { tbl->get(key) }; + return { nullptr }; + } + + #endif // TOML_WINDOWS_COMPAT + /// \brief Returns a view of the selected subnode. /// /// \param index The index of the node to retrieve diff --git a/include/toml++/toml_parse_error.h b/include/toml++/toml_parse_error.h index 9f07dba..58047e1 100644 --- a/include/toml++/toml_parse_error.h +++ b/include/toml++/toml_parse_error.h @@ -23,11 +23,7 @@ TOML_DISABLE_VTABLE_WARNINGS namespace toml { - #if TOML_LARGE_FILES - TOML_ABI_NAMESPACE_START(lf) - #else - TOML_ABI_NAMESPACE_START(sf) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_LARGE_FILES, lf, sf) #if TOML_DOXYGEN || !TOML_EXCEPTIONS diff --git a/include/toml++/toml_parser.h b/include/toml++/toml_parser.h index 1b25ae2..0bddfa8 100644 --- a/include/toml++/toml_parser.h +++ b/include/toml++/toml_parser.h @@ -86,7 +86,7 @@ namespace toml [[nodiscard]] bool succeeded() const noexcept { return !is_err; } /// \brief Returns true if parsing failed. [[nodiscard]] bool failed() const noexcept { return is_err; } - /// \brief Returns true if parsing succeeeded. + /// \brief Returns true if parsing succeeded. [[nodiscard]] explicit operator bool() const noexcept { return !is_err; } /// \brief Returns the internal toml::table. @@ -208,11 +208,52 @@ namespace toml } /// \brief Gets a node_view for the selected key-value pair in the wrapped table (const overload). + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if parsing was successful and a matching key existed, + /// or an empty node view. + /// + /// \see toml::node_view [[nodiscard]] node_view operator[] (string_view key) const noexcept { return is_err ? node_view{} : get()[key]; } + #if TOML_WINDOWS_COMPAT + + /// \brief Gets a node_view for the selected key-value pair in the wrapped table. + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if parsing was successful and a matching key existed, + /// or an empty node view. + /// + /// \see toml::node_view + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] node_view operator[] (std::wstring_view key) noexcept + { + return is_err ? node_view{} : get()[key]; + } + + /// \brief Gets a node_view for the selected key-value pair in the wrapped table (const overload). + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if parsing was successful and a matching key existed, + /// or an empty node view. + /// + /// \see toml::node_view + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] node_view operator[] (std::wstring_view key) const noexcept + { + return is_err ? node_view{} : get()[key]; + } + + #endif // TOML_WINDOWS_COMPAT + /// \brief Returns an iterator to the first key-value pair in the wrapped table. /// \remarks Returns a default-constructed 'nothing' iterator if the parsing failed. [[nodiscard]] table_iterator begin() noexcept @@ -265,14 +306,9 @@ namespace toml #endif } - namespace toml::impl { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(impl_ex) - #else - TOML_ABI_NAMESPACE_START(impl_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex) [[nodiscard]] TOML_API parse_result do_parse(utf8_reader_interface&&) TOML_MAY_THROW; @@ -280,14 +316,21 @@ namespace toml::impl TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS } +#if TOML_EXCEPTIONS + #define TOML_THROW_PARSE_ERROR(msg, path) \ + throw parse_error{ \ + msg, source_position{}, std::make_shared(std::move(path)) \ + } +#else + #define TOML_THROW_PARSE_ERROR(msg, path) \ + return parse_result{ parse_error{ \ + msg, source_position{}, std::make_shared(std::move(path)) \ + }} +#endif namespace toml { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(parse_ex) - #else - TOML_ABI_NAMESPACE_START(parse_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, parse_ex, parse_noex) /// \brief Parses a TOML document from a string view. /// @@ -326,6 +369,8 @@ namespace toml /// /// \param doc The TOML document to parse. Must be valid UTF-8. /// \param source_path The path used to initialize each node's `source().path`. + /// If you don't have a path (or you have no intention of using paths in diagnostics) + /// then this parameter can safely be left blank. /// /// \returns With exceptions: A toml::table.
/// Without exceptions: A toml::parse_result detailing the parsing outcome. @@ -333,6 +378,35 @@ namespace toml TOML_API parse_result parse(std::string_view doc, std::string&& source_path) TOML_MAY_THROW; + #if TOML_WINDOWS_COMPAT + + /// \brief Parses a TOML document from a string view. + /// + /// \detail \cpp + /// auto tbl = toml::parse("a = 3"sv, L"foo.toml"); + /// std::cout << tbl["a"] << std::endl; + /// + /// \ecpp + /// + /// \out + /// 3 + /// \eout + /// + /// \param doc The TOML document to parse. Must be valid UTF-8. + /// \param source_path The path used to initialize each node's `source().path`. + /// If you don't have a path (or you have no intention of using paths in diagnostics) + /// then this parameter can safely be left blank. + /// + /// \returns With exceptions: A toml::table.
+ /// Without exceptions: A toml::parse_result detailing the parsing outcome. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] + TOML_API + parse_result parse(std::string_view doc, std::wstring_view source_path) TOML_MAY_THROW; + + #endif // TOML_WINDOWS_COMPAT + #ifdef __cpp_lib_char8_t /// \brief Parses a TOML document from a char8_t string view. @@ -374,6 +448,8 @@ namespace toml /// /// \param doc The TOML document to parse. Must be valid UTF-8. /// \param source_path The path used to initialize each node's `source().path`. + /// If you don't have a path (or you have no intention of using paths in diagnostics) + /// then this parameter can safely be left blank. /// /// \returns With exceptions: A toml::table.
/// Without exceptions: A toml::parse_result detailing the parsing outcome. @@ -383,6 +459,36 @@ namespace toml TOML_API parse_result parse(std::u8string_view doc, std::string&& source_path) TOML_MAY_THROW; + #if TOML_WINDOWS_COMPAT + + /// \brief Parses a TOML document from a char8_t string view. + /// + /// \detail \cpp + /// auto tbl = toml::parse(u8"a = 3"sv, L"foo.toml"); + /// std::cout << tbl["a"] << std::endl; + /// + /// \ecpp + /// + /// \out + /// 3 + /// \eout + /// + /// \param doc The TOML document to parse. Must be valid UTF-8. + /// \param source_path The path used to initialize each node's `source().path`. + /// If you don't have a path (or you have no intention of using paths in diagnostics) + /// then this parameter can safely be left blank. + /// + /// \returns With exceptions: A toml::table.
+ /// Without exceptions: A toml::parse_result detailing the parsing outcome. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled and your compiler + /// supports char8_t-based strings. + [[nodiscard]] + TOML_API + parse_result parse(std::u8string_view doc, std::wstring_view source_path) TOML_MAY_THROW; + + #endif // TOML_WINDOWS_COMPAT + #endif // __cpp_lib_char8_t /// \brief Parses a TOML document from a stream. @@ -439,6 +545,8 @@ namespace toml /// \tparam Char The stream's underlying character type. Must be 1 byte in size. /// \param doc The TOML document to parse. Must be valid UTF-8. /// \param source_path The path used to initialize each node's `source().path`. + /// If you don't have a path (or you have no intention of using paths in diagnostics) + /// then this parameter can safely be left blank. /// /// \returns With exceptions: A toml::table.
/// Without exceptions: A toml::parse_result detailing the parsing outcome. @@ -455,6 +563,43 @@ namespace toml return impl::do_parse(impl::utf8_reader{ doc, std::move(source_path) }); } + #if TOML_WINDOWS_COMPAT + + /// \brief Parses a TOML document from a stream. + /// + /// \detail \cpp + /// std::stringstream ss; + /// ss << "a = 3"sv; + /// + /// auto tbl = toml::parse(ss); + /// std::cout << tbl["a"] << std::endl; + /// + /// \ecpp + /// + /// \out + /// 3 + /// \eout + /// + /// \tparam Char The stream's underlying character type. Must be 1 byte in size. + /// \param doc The TOML document to parse. Must be valid UTF-8. + /// \param source_path The path used to initialize each node's `source().path`. + /// If you don't have a path (or you have no intention of using paths in diagnostics) + /// then this parameter can safely be left blank. + /// + /// \returns With exceptions: A toml::table.
+ /// Without exceptions: A toml::parse_result detailing the parsing outcome. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + template + [[nodiscard]] + TOML_EXTERNAL_LINKAGE + parse_result parse(std::basic_istream& doc, std::wstring_view source_path) TOML_MAY_THROW + { + return parse(doc, impl::narrow(source_path)); + } + + #endif // TOML_WINDOWS_COMPAT + // Q: "why are the parse_file functions templated??" // A: I don't want to force users to drag in if they're not going to do @@ -474,7 +619,7 @@ namespace toml /// } /// \ecpp /// - /// \tparam Char The path's character type. Must be 1 byte in size. + /// \tparam Char The path's character type. /// \param file_path The TOML document to parse. Must be valid UTF-8. /// /// \returns With exceptions: A toml::table.
@@ -487,25 +632,32 @@ namespace toml parse_result parse_file(std::basic_string_view file_path) TOML_MAY_THROW { static_assert( - sizeof(Char) == 1, - "The path's character type must be 1 byte in size." + !std::is_same_v || TOML_WINDOWS_COMPAT, + "Wide-character file paths are only supported on Windows with TOML_WINDOWS_COMPAT enabled." ); - static_assert( - sizeof(StreamChar) == 1, - "The stream's character type must be 1 byte in size." - ); - - #if TOML_EXCEPTIONS - #define TOML_PARSE_FILE_ERROR(msg, pos) \ - throw parse_error{ msg, pos, std::make_shared(std::move(file_path_str)) } + #if TOML_WINDOWS_COMPAT + static_assert( + sizeof(Char) == 1 || std::is_same_v, + "The file path's underlying character type must be wchar_t or be 1 byte in size." + ); #else - #define TOML_PARSE_FILE_ERROR(msg, pos) \ - return parse_result{ \ - parse_error{ msg, pos, std::make_shared(std::move(file_path_str)) } \ - } + static_assert( + sizeof(Char) == 1, + "The file path's underlying character type must be 1 byte in size." + ); #endif + static_assert( + std::is_same_v, + "StreamChar must be 'char' (it is as an instantiation-delaying hack and is not user-configurable)." + ); - auto file_path_str = std::string(reinterpret_cast(file_path.data()), file_path.length()); + std::string file_path_str; + #if TOML_WINDOWS_COMPAT + if constexpr (std::is_same_v) + file_path_str = impl::narrow(file_path); + else + #endif + file_path_str = std::string_view{ reinterpret_cast(file_path.data()), file_path.length() }; // open file with a custom-sized stack buffer using ifstream = std::basic_ifstream; @@ -514,12 +666,12 @@ namespace toml file.rdbuf()->pubsetbuf(file_buffer, sizeof(file_buffer)); file.open(file_path_str, ifstream::in | ifstream::binary | ifstream::ate); if (!file.is_open()) - TOML_PARSE_FILE_ERROR("File could not be opened for reading", source_position{}); + TOML_THROW_PARSE_ERROR("File could not be opened for reading", file_path_str); // get size const auto file_size = file.tellg(); if (file_size == -1) - TOML_PARSE_FILE_ERROR("Could not determine file size", source_position{}); + TOML_THROW_PARSE_ERROR("Could not determine file size", file_path_str); file.seekg(0, std::ios::beg); // read the whole file into memory first if the file isn't too large @@ -535,8 +687,6 @@ namespace toml // otherwise parse it using the streams else return parse(file, std::move(file_path_str)); - - #undef TOML_PARSE_FILE_ERROR } #if !TOML_ALL_INLINE @@ -546,6 +696,9 @@ namespace toml #ifdef __cpp_lib_char8_t extern template TOML_API parse_result parse_file(std::u8string_view) TOML_MAY_THROW; #endif + #if TOML_WINDOWS_COMPAT + extern template TOML_API parse_result parse_file(std::wstring_view) TOML_MAY_THROW; + #endif #endif template @@ -584,13 +737,9 @@ namespace toml /// inline namespace literals { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(lit_ex) - #else - TOML_ABI_NAMESPACE_START(lit_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, lit_ex, lit_noex) - /// \brief Parses TOML data from a string. + /// \brief Parses TOML data from a string literal. /// /// \detail \cpp /// using namespace toml::literals; @@ -604,7 +753,7 @@ namespace toml /// 3 /// \eout /// - /// \param str The string data. + /// \param str The string data. Must be valid UTF-8. /// \param len The string length. /// /// \returns With exceptions: A toml::table.
@@ -615,7 +764,7 @@ namespace toml #ifdef __cpp_lib_char8_t - /// \brief Parses TOML data from a string. + /// \brief Parses TOML data from a utf8 string literal. /// /// \detail \cpp /// using namespace toml::literals; @@ -629,7 +778,7 @@ namespace toml /// 3 /// \eout /// - /// \param str The string data. + /// \param str The string data. Must be valid UTF-8. /// \param len The string length. /// /// \returns With exceptions: A toml::table.
@@ -644,6 +793,9 @@ namespace toml TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS } + } +#undef TOML_THROW_PARSE_ERROR + TOML_POP_WARNINGS // TOML_DISABLE_PADDING_WARNINGS diff --git a/include/toml++/toml_parser.hpp b/include/toml++/toml_parser.hpp index eb8b7fd..4dfb932 100644 --- a/include/toml++/toml_parser.hpp +++ b/include/toml++/toml_parser.hpp @@ -257,12 +257,6 @@ namespace TOML_INTERNAL_NAMESPACE namespace toml::impl { - #if defined(NDEBUG) || !defined(_DEBUG) - #define assert_or_assume(cond) TOML_ASSUME(cond) - #else - #define assert_or_assume(cond) TOML_ASSERT(cond) - #endif - // Q: "what the fuck is this? MACROS????" // A: The parser needs to work in exceptionless mode (returning error objects directly) // and exception mode (reporting parse failures by throwing). Two totally different control flows. @@ -271,6 +265,12 @@ namespace toml::impl // They're all #undef'd at the bottom of the parser's implementation so they should be harmless outside // of toml++. + #if defined(NDEBUG) || !defined(_DEBUG) + #define assert_or_assume(cond) TOML_ASSUME(cond) + #else + #define assert_or_assume(cond) TOML_ASSERT(cond) + #endif + #define is_eof() !cp #define assert_not_eof() assert_or_assume(cp) #define return_if_eof(...) do { if (is_eof()) return __VA_ARGS__; } while(false) @@ -302,11 +302,7 @@ namespace toml::impl set_error_and_return_if_eof(__VA_ARGS__); \ } while (false) - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(impl_ex) - #else - TOML_ABI_NAMESPACE_START(impl_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex) class parser final { @@ -2909,33 +2905,30 @@ namespace toml::impl TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS + #undef push_parse_scope_2 + #undef push_parse_scope_1 + #undef push_parse_scope + #undef TOML_RETURNS_BY_THROWING + #undef is_eof + #undef assert_not_eof + #undef return_if_eof + #undef is_error + #undef return_after_error + #undef assert_not_error + #undef return_if_error + #undef return_if_error_or_eof + #undef set_error_and_return + #undef set_error_and_return_default + #undef set_error_and_return_if_eof + #undef advance_and_return_if_error + #undef advance_and_return_if_error_or_eof + #undef assert_or_assume } -#undef push_parse_scope_2 -#undef push_parse_scope_1 -#undef push_parse_scope -#undef TOML_RETURNS_BY_THROWING -#undef is_eof -#undef assert_not_eof -#undef return_if_eof -#undef is_error -#undef return_after_error -#undef assert_not_error -#undef return_if_error -#undef return_if_error_or_eof -#undef set_error_and_return -#undef set_error_and_return_default -#undef set_error_and_return_if_eof -#undef advance_and_return_if_error -#undef advance_and_return_if_error_or_eof namespace toml { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(parse_ex) - #else - TOML_ABI_NAMESPACE_START(parse_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, parse_ex, parse_noex) TOML_API TOML_EXTERNAL_LINKAGE @@ -2951,6 +2944,17 @@ namespace toml return impl::do_parse(impl::utf8_reader{ doc, std::move(source_path) }); } + #if TOML_WINDOWS_COMPAT + + TOML_API + TOML_EXTERNAL_LINKAGE + parse_result parse(std::string_view doc, std::wstring_view source_path) TOML_MAY_THROW + { + return impl::do_parse(impl::utf8_reader{ doc, impl::narrow(source_path) }); + } + + #endif // TOML_WINDOWS_COMPAT + #ifdef __cpp_lib_char8_t TOML_API @@ -2967,17 +2971,24 @@ namespace toml return impl::do_parse(impl::utf8_reader{ doc, std::move(source_path) }); } + #if TOML_WINDOWS_COMPAT + + TOML_API + TOML_EXTERNAL_LINKAGE + parse_result parse(std::u8string_view doc, std::wstring_view source_path) TOML_MAY_THROW + { + return impl::do_parse(impl::utf8_reader{ doc, impl::narrow(source_path) }); + } + + #endif // TOML_WINDOWS_COMPAT + #endif // __cpp_lib_char8_t TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS inline namespace literals { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(lit_ex) - #else - TOML_ABI_NAMESPACE_START(lit_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, lit_ex, lit_noex) TOML_API TOML_EXTERNAL_LINKAGE diff --git a/include/toml++/toml_preprocessor.h b/include/toml++/toml_preprocessor.h index 7aee1f5..3eb49f4 100644 --- a/include/toml++/toml_preprocessor.h +++ b/include/toml++/toml_preprocessor.h @@ -48,11 +48,29 @@ #define TOML_PARSER 1 #endif +#if (defined(_WIN32) || defined(DOXYGEN)) && !defined(TOML_WINDOWS_COMPAT) + #define TOML_WINDOWS_COMPAT 1 +#endif +#if !(defined(_WIN32) || defined(DOXYGEN)) || !defined(TOML_WINDOWS_COMPAT) + #undef TOML_WINDOWS_COMPAT + #define TOML_WINDOWS_COMPAT 0 +#endif + +#ifdef TOML_OPTIONAL_TYPE + #define TOML_HAS_CUSTOM_OPTIONAL_TYPE 1 +#else + #define TOML_HAS_CUSTOM_OPTIONAL_TYPE 0 +#endif + ////////// COMPILER & ENVIRONMENT #ifndef __cplusplus #error toml++ is a C++ library. #endif +#ifdef DOXYGEN + #undef TOML_DOXYGEN + #define TOML_DOXYGEN 1 +#endif #ifndef TOML_DOXYGEN #define TOML_DOXYGEN 0 #endif @@ -192,7 +210,6 @@ #ifndef TOML_CPP_VERSION #define TOML_CPP_VERSION __cplusplus #endif - #if TOML_CPP_VERSION < 201103L #error toml++ requires C++17 or higher. For a TOML library supporting pre-C++11 see https://github.com/ToruNiina/Boost.toml #elif TOML_CPP_VERSION < 201703L @@ -206,12 +223,14 @@ #elif TOML_CPP_VERSION >= 201703L #define TOML_CPP 17 #endif +#undef TOML_CPP_VERSION #ifndef TOML_COMPILER_EXCEPTIONS #define TOML_COMPILER_EXCEPTIONS 1 #endif #if TOML_COMPILER_EXCEPTIONS - #ifndef TOML_EXCEPTIONS + #if !defined(TOML_EXCEPTIONS) || (defined(TOML_EXCEPTIONS) && TOML_EXCEPTIONS) + #undef TOML_EXCEPTIONS #define TOML_EXCEPTIONS 1 #endif #else @@ -381,13 +400,21 @@ #define TOML_LANG_UNRELEASED \ TOML_LANG_HIGHER_THAN(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH) +#define TOML_CONCAT_1(x, y) x##y +#define TOML_CONCAT(x, y) TOML_CONCAT_1(x, y) + +#define TOML_EVAL_BOOL_1(T, F) T +#define TOML_EVAL_BOOL_0(T, F) F + #if TOML_DOXYGEN || defined(__INTELLISENSE__) #define TOML_ABI_NAMESPACES 0 + #define TOML_ABI_NAMESPACE_BOOL(cond, T, F) #define TOML_ABI_NAMESPACE_START(name) #define TOML_ABI_NAMESPACE_END #else #define TOML_ABI_NAMESPACES 1 - #define TOML_ABI_NAMESPACE_START(name) inline namespace abi_##name { + #define TOML_ABI_NAMESPACE_START(name) inline namespace TOML_CONCAT(abi_, name) { + #define TOML_ABI_NAMESPACE_BOOL(cond, T, F) TOML_ABI_NAMESPACE_START(TOML_CONCAT(TOML_EVAL_BOOL_, cond)(T, F)) #define TOML_ABI_NAMESPACE_END } #endif @@ -404,8 +431,7 @@ TOML_DISABLE_ALL_WARNINGS TOML_POP_WARNINGS #if TOML_CHAR_8_STRINGS - #define TOML_STRING_PREFIX_1(S) u8##S - #define TOML_STRING_PREFIX(S) TOML_STRING_PREFIX_1(S) + #define TOML_STRING_PREFIX(S) TOML_CONCAT(u8, S) #else #define TOML_STRING_PREFIX(S) S #endif @@ -424,7 +450,7 @@ TOML_POP_WARNINGS /// \def TOML_ALL_INLINE /// \brief Sets whether the library is entirely inline. /// \detail Defaults to `1`. -/// \remark Disabling this means that you must define `TOML_IMPLEMENTATION` in +/// \remark Disabling this means that you must define #TOML_IMPLEMENTATION in /// exactly one translation unit in your project: /// \cpp /// // global_header_that_includes_toml++.h @@ -512,6 +538,20 @@ TOML_POP_WARNINGS /// \see [TOML Language Support](https://github.com/marzer/tomlplusplus/blob/master/README.md#toml-language-support) +/// \def TOML_WINDOWS_COMPAT +/// \brief Enables the use of wide strings (wchar_t, std::wstring) in various places throughout the library +/// when building for Windows. +/// \detail Defaults to `1` when building for Windows, `0` otherwise. Has no effect when building for anything other +/// than Windows. +/// \attention This does not change the underlying string type used to represent TOML keys and string values; +/// that will still be std::string or std::u8string according to whatever #TOML_CHAR_8_STRINGS is set to. +/// This setting simply enables some narrow <=> wide string conversions when necessary at +/// various interface boundaries. +///

+/// If you're building for Windows and you have no need for Windows' "Pretends-to-be-unicode" wide strings, +/// you can safely set this to `0`. + + /// @} #endif // TOML_DOXYGEN //# }} diff --git a/include/toml++/toml_print_to_stream.h b/include/toml++/toml_print_to_stream.h index 2a5ca91..e5aaed6 100644 --- a/include/toml++/toml_print_to_stream.h +++ b/include/toml++/toml_print_to_stream.h @@ -68,7 +68,7 @@ namespace toml::impl stream.write(reinterpret_cast(str), static_cast(len)); } - #if defined(__cpp_lib_char8_t) + #ifdef __cpp_lib_char8_t template TOML_ALWAYS_INLINE diff --git a/include/toml++/toml_table.h b/include/toml++/toml_table.h index 761ac8c..ef0ea8d 100644 --- a/include/toml++/toml_table.h +++ b/include/toml++/toml_table.h @@ -145,23 +145,45 @@ namespace toml::impl string key; std::unique_ptr value; - template - table_init_pair(string&& k, T && v) noexcept + template + table_init_pair(string&& k, V&& v) noexcept : key{ std::move(k) }, - value{ make_node(std::forward(v)) } + value{ make_node(std::forward(v)) } {} - template - table_init_pair(string_view k, T&& v) noexcept + template + table_init_pair(string_view k, V&& v) noexcept : key{ k }, - value{ make_node(std::forward(v)) } + value{ make_node(std::forward(v)) } {} - template - table_init_pair(const string_char* k, T&& v) noexcept + template + table_init_pair(const string_char* k, V&& v) noexcept : key{ k }, - value{ make_node(std::forward(v)) } + value{ make_node(std::forward(v)) } {} + + #if TOML_WINDOWS_COMPAT + + template + table_init_pair(std::wstring&& k, V&& v) noexcept + : key{ narrow(k) }, + value{ make_node(std::forward(v)) } + {} + + template + table_init_pair(std::wstring_view k, V&& v) noexcept + : key{ narrow(k) }, + value{ make_node(std::forward(v)) } + {} + + template + table_init_pair(const wchar_t* k, V&& v) noexcept + : key{ narrow(std::wstring_view{ k }) }, + value{ make_node(std::forward(v)) } + {} + + #endif }; } @@ -319,8 +341,52 @@ namespace toml [[nodiscard]] node_view operator[] (string_view key) noexcept; /// \brief Gets a node_view for the selected key-value pair (const overload). + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if one existed, or an empty node view. + /// + /// \remarks std::map::operator[]'s behaviour of default-constructing a value at a key if it + /// didn't exist is a crazy bug factory so I've deliberately chosen not to emulate it. + /// This is not an error. + /// + /// \see toml::node_view [[nodiscard]] node_view operator[] (string_view key) const noexcept; + #if TOML_WINDOWS_COMPAT + + /// \brief Gets a node_view for the selected key-value pair. + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if one existed, or an empty node view. + /// + /// \remarks std::map::operator[]'s behaviour of default-constructing a value at a key if it + /// didn't exist is a crazy bug factory so I've deliberately chosen not to emulate it. + /// This is not an error. + /// + /// \see toml::node_view + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] node_view operator[] (std::wstring_view key) noexcept; + + /// \brief Gets a node_view for the selected key-value pair (const overload). + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if one existed, or an empty node view. + /// + /// \remarks std::map::operator[]'s behaviour of default-constructing a value at a key if it + /// didn't exist is a crazy bug factory so I've deliberately chosen not to emulate it. + /// This is not an error. + /// + /// \see toml::node_view + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] node_view operator[] (std::wstring_view key) const noexcept; + + #endif // TOML_WINDOWS_COMPAT + /// \brief Returns an iterator to the first key-value pair. [[nodiscard]] iterator begin() noexcept; /// \brief Returns an iterator to the first key-value pair. @@ -378,16 +444,29 @@ namespace toml /// - A boolean indicating if the insertion was successful. template + || impl::is_wide_string >> std::pair insert(K&& key, V&& val) noexcept { - auto ipos = values.lower_bound(key); - if (ipos == values.end() || ipos->first != key) + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); + + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return insert(impl::narrow(std::forward(key)), std::forward(val)); + else + #endif { - ipos = values.emplace_hint(ipos, std::forward(key), impl::make_node(std::forward(val))); - return { ipos, true }; + auto ipos = values.lower_bound(key); + if (ipos == values.end() || ipos->first != key) + { + ipos = values.emplace_hint(ipos, std::forward(key), impl::make_node(std::forward(val))); + return { ipos, true }; + } + return { ipos, false }; } - return { ipos, false }; } /// \brief Inserts a series of key-value pairs into the table. @@ -422,7 +501,8 @@ namespace toml /// key-value pair covered by the iterator range, so any values with keys already found in the /// table will not be replaced. template + !std::is_convertible_v + && !impl::is_wide_string >> void insert(Iter first, Iter last) noexcept { @@ -475,16 +555,28 @@ namespace toml template std::pair insert_or_assign(K&& key, V&& val) noexcept { - auto ipos = values.lower_bound(key); - if (ipos == values.end() || ipos->first != key) - { - ipos = values.emplace_hint(ipos, std::forward(key), impl::make_node(std::forward(val))); - return { ipos, true }; - } + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); + + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return insert_or_assign(impl::narrow(std::forward(key)), std::forward(val)); else + #endif { - (*ipos).second.reset(impl::make_node(std::forward(val))); - return { ipos, false }; + auto ipos = values.lower_bound(key); + if (ipos == values.end() || ipos->first != key) + { + ipos = values.emplace_hint(ipos, std::forward(key), impl::make_node(std::forward(val))); + return { ipos, true }; + } + else + { + (*ipos).second.reset(impl::make_node(std::forward(val))); + return { ipos, false }; + } } } @@ -529,23 +621,36 @@ namespace toml template std::pair emplace(K&& key, V&&... args) noexcept { - using type = impl::unwrapped; static_assert( - impl::is_value_or_node, - "Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array" + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Emplacement using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." ); - auto ipos = values.lower_bound(key); - if (ipos == values.end() || ipos->first != key) + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return emplace(impl::narrow(std::forward(key)), std::forward(args)...); + else + #endif { - ipos = values.emplace_hint( - ipos, - std::forward(key), - new impl::node_of{ std::forward(args)... } + + using type = impl::unwrapped; + static_assert( + impl::is_value_or_node, + "Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array" ); - return { ipos, true }; + + auto ipos = values.lower_bound(key); + if (ipos == values.end() || ipos->first != key) + { + ipos = values.emplace_hint( + ipos, + std::forward(key), + new impl::node_of{ std::forward(args)... } + ); + return { ipos, true }; + } + return { ipos, false }; } - return { ipos, false }; } /// \brief Removes the specified key-value pair from the table. @@ -653,20 +758,40 @@ namespace toml /// \returns True if any values with matching keys were found and erased. bool erase(string_view key) noexcept; + #if TOML_WINDOWS_COMPAT + + /// \brief Removes the value with the given key from the table. + /// + /// \param key Key to erase. + /// + /// \returns True if any values with matching keys were found and erased. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + bool erase(std::wstring_view key) noexcept; + + #endif + private: template [[nodiscard]] static auto do_get(Map& vals, const Key& key) noexcept + -> std::conditional_t, const node*, node*> { - using return_type = std::conditional_t< - std::is_const_v, - const node*, - node* - >; + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Retrieval using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); - if (auto it = vals.find(key); it != vals.end()) - return return_type{ it->second.get() }; - return return_type{}; + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return do_get(vals, impl::narrow(key)); + else + #endif + { + if (auto it = vals.find(key); it != vals.end()) + return { it->second.get() }; + return {}; + } } template @@ -680,11 +805,7 @@ namespace toml [[nodiscard]] TOML_ALWAYS_INLINE static bool do_contains(Map& vals, const Key& key) noexcept { - #if TOML_CPP >= 20 - return vals.contains(key); - #else - return do_get(vals, key) != nullptr; - #endif + return do_get(vals, key) != nullptr; } public: @@ -723,9 +844,68 @@ namespace toml /// \returns A pointer to the node at the specified key, or nullptr. [[nodiscard]] const node* get(string_view key) const noexcept; + /// \brief Gets an iterator to the node at a specific key. + /// + /// \param key The node's key. + /// + /// \returns An iterator to the node at the specified key, or end(). [[nodiscard]] iterator find(string_view key) noexcept; + + /// \brief Gets an iterator to the node at a specific key (const overload) + /// + /// \param key The node's key. + /// + /// \returns A const iterator to the node at the specified key, or cend(). [[nodiscard]] const_iterator find(string_view key) const noexcept; + /// \brief Returns true if the table contains a node at the given key. + [[nodiscard]] bool contains(string_view key) const noexcept; + + #if TOML_WINDOWS_COMPAT + + /// \brief Gets the node at a specific key. + /// + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key, or nullptr. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] node* get(std::wstring_view key) noexcept; + + /// \brief Gets the node at a specific key (const overload). + /// + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key, or nullptr. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] const node* get(std::wstring_view key) const noexcept; + + /// \brief Gets an iterator to the node at a specific key. + /// + /// \param key The node's key. + /// + /// \returns An iterator to the node at the specified key, or end(). + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] iterator find(std::wstring_view key) noexcept; + + /// \brief Gets an iterator to the node at a specific key (const overload). + /// + /// \param key The node's key. + /// + /// \returns A const iterator to the node at the specified key, or cend(). + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] const_iterator find(std::wstring_view key) const noexcept; + + /// \brief Returns true if the table contains a node at the given key. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + [[nodiscard]] bool contains(std::wstring_view key) const noexcept; + + #endif // TOML_WINDOWS_COMPAT + /// \brief Gets the node at a specific key if it is a particular type. /// /// \detail \cpp @@ -764,8 +944,37 @@ namespace toml return do_get_as(values, key); } - /// \brief Returns true if the table contains a node at the given key. - [[nodiscard]] bool contains(string_view key) const noexcept; + #if TOML_WINDOWS_COMPAT + + /// \brief Gets the node at a specific key if it is a particular type. + /// + /// \tparam T The node's type. + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + template + [[nodiscard]] impl::node_of* get_as(std::wstring_view key) noexcept + { + return get_as(impl::narrow(key)); + } + + /// \brief Gets the node at a specific key if it is a particular type (const overload). + /// + /// \tparam T The node's type. + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. + /// + /// \attention This overload is only available when #TOML_WINDOWS_COMPAT is enabled. + template + [[nodiscard]] const impl::node_of* get_as(std::wstring_view key) const noexcept + { + return get_as(impl::narrow(key)); + } + + #endif // TOML_WINDOWS_COMPAT /// \brief Equality operator. /// @@ -783,6 +992,7 @@ namespace toml /// \returns True if the tables did not contain the same keys and values. friend bool operator != (const table& lhs, const table& rhs) noexcept; + /// \brief Prints the table out to a stream as formatted TOML. template friend std::basic_ostream& operator << (std::basic_ostream&, const table&); }; diff --git a/include/toml++/toml_table.hpp b/include/toml++/toml_table.hpp index be896cf..d681ba4 100644 --- a/include/toml++/toml_table.hpp +++ b/include/toml++/toml_table.hpp @@ -145,6 +145,57 @@ namespace toml return do_contains(values, key); } + #if TOML_WINDOWS_COMPAT + + TOML_EXTERNAL_LINKAGE + node_view table::operator[] (std::wstring_view key) noexcept + { + return { this->get(key) }; + } + TOML_EXTERNAL_LINKAGE + node_view table::operator[] (std::wstring_view key) const noexcept + { + return { this->get(key) }; + } + + TOML_EXTERNAL_LINKAGE + bool table::erase(std::wstring_view key) noexcept + { + return erase(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + node* table::get(std::wstring_view key) noexcept + { + return get(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + const node* table::get(std::wstring_view key) const noexcept + { + return get(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + table::iterator table::find(std::wstring_view key) noexcept + { + return find(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + table::const_iterator table::find(std::wstring_view key) const noexcept + { + return find(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + bool table::contains(std::wstring_view key) const noexcept + { + return contains(impl::narrow(key)); + } + + #endif // TOML_WINDOWS_COMPAT + TOML_API TOML_EXTERNAL_LINKAGE bool operator == (const table& lhs, const table& rhs) noexcept diff --git a/include/toml++/toml_utf8_streams.h b/include/toml++/toml_utf8_streams.h index c775f30..9ac939b 100644 --- a/include/toml++/toml_utf8_streams.h +++ b/include/toml++/toml_utf8_streams.h @@ -135,11 +135,7 @@ namespace toml::impl } }; - #if TOML_LARGE_FILES - TOML_ABI_NAMESPACE_START(impl_lf) - #else - TOML_ABI_NAMESPACE_START(impl_sf) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_LARGE_FILES, impl_lf, impl_sf) struct utf8_codepoint final { @@ -369,13 +365,10 @@ namespace toml::impl template utf8_reader(std::basic_string_view, std::string_view) -> utf8_reader>; - - template - utf8_reader(std::basic_istream&, std::string_view) -> utf8_reader>; - template utf8_reader(std::basic_string_view, std::string&&) -> utf8_reader>; - + template + utf8_reader(std::basic_istream&, std::string_view) -> utf8_reader>; template utf8_reader(std::basic_istream&, std::string&&) -> utf8_reader>; diff --git a/include/toml++/toml_value.h b/include/toml++/toml_value.h index 11efc61..dd176ff 100644 --- a/include/toml++/toml_value.h +++ b/include/toml++/toml_value.h @@ -172,6 +172,7 @@ namespace toml /// \brief Returns a reference to the underlying value (const overload). [[nodiscard]] explicit operator const T& () const& noexcept { return val_; } + /// \brief Prints the value out to a stream as formatted TOML. template friend std::basic_ostream& operator << (std::basic_ostream& lhs, const value& rhs); @@ -366,36 +367,6 @@ namespace toml value(TOML_SMALL_INT_TYPE) -> value; #endif - /// \brief Prints the value out to a stream. - template - TOML_EXTERNAL_LINKAGE - std::basic_ostream& operator << (std::basic_ostream& lhs, const value& rhs) - { - // this is the same behaviour as default_formatter, but it's so simple that there's - // no need to spin up a new instance of it just for individual values. - - if constexpr (std::is_same_v) - { - impl::print_to_stream('"', lhs); - impl::print_to_stream_with_escapes(rhs.val_, lhs); - impl::print_to_stream('"', lhs); - } - else - impl::print_to_stream(rhs.val_, lhs); - - return lhs; - } - - #if !TOML_ALL_INLINE - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - #endif - TOML_PUSH_WARNINGS TOML_DISABLE_INIT_WARNINGS @@ -403,7 +374,13 @@ namespace toml inline optional node::value() const noexcept { static_assert( - impl::is_value || std::is_same_v, + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Retrieving values as wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); + static_assert( + impl::is_value + || std::is_same_v + || (TOML_WINDOWS_COMPAT && std::is_same_v), "Value type must be one of the TOML value types (or string_view)" ); @@ -418,6 +395,12 @@ namespace toml { if constexpr (std::is_same_v || std::is_same_v) return { T{ ref_cast().get() } }; + + #if TOML_WINDOWS_COMPAT + else if constexpr (std::is_same_v) + return { impl::widen(ref_cast().get()) }; + #endif + else return {}; } @@ -485,21 +468,36 @@ namespace toml template inline auto node::value_or(T&& default_value) const noexcept { + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Retrieving values as wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); static_assert( impl::is_value_or_promotable>, "Default value type must be (or be promotable to) one of the TOML value types" ); - using value_type = impl::promoted>; - using return_type = std::conditional_t< - std::is_same_v, - std::conditional_t, string>, string, string_view>, - value_type - >; + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + { + if (this->type() == node_type::string) + return impl::widen(ref_cast().get()); + return std::wstring{ std::forward(default_value) }; + } + else + #endif + { + using value_type = impl::promoted>; + using return_type = std::conditional_t< + std::is_same_v, + std::conditional_t, string>, string, string_view>, + value_type + >; - if (auto val = this->value()) - return *val; - return return_type{ std::forward(default_value) }; + if (auto val = this->value()) + return *val; + return return_type{ std::forward(default_value) }; + } } } diff --git a/include/toml++/toml_version.h b/include/toml++/toml_version.h index 0ae8132..ee2c221 100644 --- a/include/toml++/toml_version.h +++ b/include/toml++/toml_version.h @@ -7,7 +7,7 @@ #define TOML_LIB_MAJOR 1 #define TOML_LIB_MINOR 3 -#define TOML_LIB_PATCH 3 +#define TOML_LIB_PATCH 4 #define TOML_LANG_MAJOR 1 #define TOML_LANG_MINOR 0 diff --git a/meson.build b/meson.build index 4a91eef..5b5a9d7 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'tomlplusplus', 'cpp', - version : '1.3.3', + version : '1.3.4', license : 'MIT', default_options : [ 'cpp_std=c++17', diff --git a/tests/impl_toml.cpp b/tests/impl_toml.cpp index caf6c29..5349b45 100644 --- a/tests/impl_toml.cpp +++ b/tests/impl_toml.cpp @@ -26,6 +26,10 @@ #error TOML_EXCEPTIONS does not match TOML_COMPILER_EXCEPTIONS (default behaviour should be to match) #endif +#if (defined(_WIN32) && !TOML_WINDOWS_COMPAT) || (!defined(_WIN32) && TOML_WINDOWS_COMPAT) + #error TOML_WINDOWS_COMPAT does not match _WIN32 (default behaviour should be to match) +#endif + namespace toml { using std::declval; diff --git a/tests/manipulating_values.cpp b/tests/manipulating_values.cpp index 6e203ad..03bd28f 100644 --- a/tests/manipulating_values.cpp +++ b/tests/manipulating_values.cpp @@ -47,6 +47,10 @@ TEST_CASE("values - printing") // large floats might get output as scientific notation and that's fine CHECK(print_value(10000000000) == "10000000000"); CHECK(print_value(100000000000000) == "100000000000000"); - } +static_assert(std::is_same_v().value_or(S(""s)))>); +static_assert(std::is_same_v().value_or(S(""sv)))>); +static_assert(std::is_same_v().value_or(S("")))>); + + diff --git a/tests/meson.build b/tests/meson.build index f1d700e..2f7fae1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -21,6 +21,7 @@ test_sources = [ 'manipulating_values.cpp', 'unicode.cpp', 'unicode_generated.cpp', + 'windows_compat.cpp' ] compiler_supports_char8_strings = compiler.compiles(''' diff --git a/tests/settings.h b/tests/settings.h index e5744e6..a2b4556 100644 --- a/tests/settings.h +++ b/tests/settings.h @@ -65,7 +65,7 @@ #define NOMETAFILE // - typedef METAFILEPICT #define NOMINMAX // - Macros min(a,b) and max(a,b) #define NOMSG // - typedef MSG and associated routines - #define NONLS // - All NLS defines and routines + //#define NONLS // - All NLS defines and routines #define NOOPENFILE // - OpenFile(), OemToAnsi, AnsiToOem, and OF_* #define NOPROFILER // - Profiler interface. #define NORASTEROPS // - Binary and Tertiary raster ops diff --git a/tests/tests.h b/tests/tests.h index 08874d6..df1a849 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -168,16 +168,16 @@ inline bool parse_expected_value( auto nv = tbl[S("val"sv)]; REQUIRE(nv); REQUIRE(nv.as()); - REQUIRE(nv.get()->type() == impl::node_type_of); + REQUIRE(nv.node()->type() == impl::node_type_of); // check the raw value - REQUIRE(nv.get()->value() == expected); - REQUIRE(nv.get()->value_or(T{}) == expected); + REQUIRE(nv.node()->value() == expected); + REQUIRE(nv.node()->value_or(T{}) == expected); REQUIRE(nv.as()->get() == expected); REQUIRE(nv.value() == expected); REQUIRE(nv.value_or(T{}) == expected); REQUIRE(nv.ref() == expected); - REQUIRE(nv.get()->ref() == expected); + REQUIRE(nv.node()->ref() == expected); // check the table relops REQUIRE(tbl == table{ { { S("val"sv), expected } } }); @@ -196,8 +196,8 @@ inline bool parse_expected_value( REQUIRE(!(expected != nv)); // make sure source info is correct - REQUIRE(nv.get()->source().begin == begin); - REQUIRE(nv.get()->source().end == end); + REQUIRE(nv.node()->source().begin == begin); + REQUIRE(nv.node()->source().end == end); // steal the val for round-trip tests if (!stolen_value) @@ -235,7 +235,7 @@ inline bool parse_expected_value( auto nv = tbl[S("val"sv)]; REQUIRE(nv); REQUIRE(nv.as()); - REQUIRE(nv.get()->type() == impl::node_type_of); + REQUIRE(nv.node()->type() == impl::node_type_of); if (value_ok && nv.ref() != expected) { diff --git a/tests/windows_compat.cpp b/tests/windows_compat.cpp new file mode 100644 index 0000000..b57802d --- /dev/null +++ b/tests/windows_compat.cpp @@ -0,0 +1,77 @@ +// This file is a part of toml++ and is subject to the the terms of the MIT license. +// Copyright (c) 2019-2020 Mark Gillard +// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT + +#include "tests.h" + +#if TOML_WINDOWS_COMPAT + +TEST_CASE("windows compat") +{ + static constexpr auto toml_text = R"( + [library] + name = "toml++" + authors = ["Mark Gillard "] + + [dependencies] + cpp = 17 + )"sv; + + auto res = toml::parse(toml_text, L"kek.toml"); + #if !TOML_EXCEPTIONS + REQUIRE(res.succeeded()); + #endif + toml::table& tbl = res; + + // source paths + REQUIRE(tbl.source().path != nullptr); + CHECK(*tbl.source().path == "kek.toml"sv); + CHECK(tbl.source().wide_path().has_value()); + CHECK(tbl.source().wide_path().value() == L"kek.toml"sv); + + // direct lookups from tables + REQUIRE(tbl.get(S("library")) != nullptr); + CHECK(tbl.get(S("library")) == tbl.get(S("library"sv))); + CHECK(tbl.get(S("library")) == tbl.get(S("library"s))); + CHECK(tbl.get(L"library") != nullptr); + CHECK(tbl.get(L"library") == tbl.get(L"library"sv)); + CHECK(tbl.get(L"library") == tbl.get(L"library"s)); + CHECK(tbl.get(L"library") == tbl.get(S("library"))); + + // node-view lookups + CHECK(tbl[L"library"].node() != nullptr); + CHECK(tbl[L"library"].node() == tbl.get(L"library")); + + // value queries + REQUIRE(tbl[L"library"][L"name"].as_string() != nullptr); + CHECK(tbl[L"library"][L"name"].value() == L"toml++"s); + CHECK(tbl[L"library"][L"name"].value_or(L""sv) == L"toml++"s); + CHECK(tbl[L"library"][L"name"].value_or(L""s) == L"toml++"s); + CHECK(tbl[L"library"][L"name"].value_or(L"") == L"toml++"s); + + // node-view comparisons + CHECK(tbl[L"library"][L"name"] == S("toml++"sv)); + CHECK(tbl[L"library"][L"name"] == S("toml++"s)); + CHECK(tbl[L"library"][L"name"] == S("toml++")); + CHECK(tbl[L"library"][L"name"] == L"toml++"sv); + CHECK(tbl[L"library"][L"name"] == L"toml++"s); + CHECK(tbl[L"library"][L"name"] == L"toml++"); + + // table manipulation + tbl.insert(L"foo", L"bar"); + REQUIRE(tbl.contains(S("foo"))); + REQUIRE(tbl.contains(L"foo")); + CHECK(tbl[S("foo")] == S("bar")); + tbl.insert_or_assign(L"foo", L"kek"); + CHECK(tbl[S("foo")] == S("kek")); + tbl.erase(L"foo"); + REQUIRE(!tbl.contains(S("foo"))); + REQUIRE(!tbl.contains(L"foo")); +} + +static_assert(std::is_same_v().value_or(L""s))>); +static_assert(std::is_same_v().value_or(L""sv))>); +static_assert(std::is_same_v().value_or(L""))>); + +#endif // TOML_WINDOWS_COMPAT diff --git a/toml.hpp b/toml.hpp index e7eb13c..4edfb67 100644 --- a/toml.hpp +++ b/toml.hpp @@ -1,6 +1,6 @@ //---------------------------------------------------------------------------------------------------------------------- // -// toml++ v1.3.3 +// toml++ v1.3.4 // https://github.com/marzer/tomlplusplus // SPDX-License-Identifier: MIT // @@ -90,9 +90,27 @@ #define TOML_PARSER 1 #endif +#if (defined(_WIN32) || defined(DOXYGEN)) && !defined(TOML_WINDOWS_COMPAT) + #define TOML_WINDOWS_COMPAT 1 +#endif +#if !(defined(_WIN32) || defined(DOXYGEN)) || !defined(TOML_WINDOWS_COMPAT) + #undef TOML_WINDOWS_COMPAT + #define TOML_WINDOWS_COMPAT 0 +#endif + +#ifdef TOML_OPTIONAL_TYPE + #define TOML_HAS_CUSTOM_OPTIONAL_TYPE 1 +#else + #define TOML_HAS_CUSTOM_OPTIONAL_TYPE 0 +#endif + #ifndef __cplusplus #error toml++ is a C++ library. #endif +#ifdef DOXYGEN + #undef TOML_DOXYGEN + #define TOML_DOXYGEN 1 +#endif #ifndef TOML_DOXYGEN #define TOML_DOXYGEN 0 #endif @@ -232,7 +250,6 @@ #ifndef TOML_CPP_VERSION #define TOML_CPP_VERSION __cplusplus #endif - #if TOML_CPP_VERSION < 201103L #error toml++ requires C++17 or higher. For a TOML library supporting pre-C++11 see https://github.com/ToruNiina/Boost.toml #elif TOML_CPP_VERSION < 201703L @@ -246,12 +263,14 @@ #elif TOML_CPP_VERSION >= 201703L #define TOML_CPP 17 #endif +#undef TOML_CPP_VERSION #ifndef TOML_COMPILER_EXCEPTIONS #define TOML_COMPILER_EXCEPTIONS 1 #endif #if TOML_COMPILER_EXCEPTIONS - #ifndef TOML_EXCEPTIONS + #if !defined(TOML_EXCEPTIONS) || (defined(TOML_EXCEPTIONS) && TOML_EXCEPTIONS) + #undef TOML_EXCEPTIONS #define TOML_EXCEPTIONS 1 #endif #else @@ -398,7 +417,7 @@ #define TOML_LIB_MAJOR 1 #define TOML_LIB_MINOR 3 -#define TOML_LIB_PATCH 3 +#define TOML_LIB_PATCH 4 #define TOML_LANG_MAJOR 1 #define TOML_LANG_MINOR 0 @@ -424,13 +443,21 @@ #define TOML_LANG_UNRELEASED \ TOML_LANG_HIGHER_THAN(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH) +#define TOML_CONCAT_1(x, y) x##y +#define TOML_CONCAT(x, y) TOML_CONCAT_1(x, y) + +#define TOML_EVAL_BOOL_1(T, F) T +#define TOML_EVAL_BOOL_0(T, F) F + #if TOML_DOXYGEN || defined(__INTELLISENSE__) #define TOML_ABI_NAMESPACES 0 + #define TOML_ABI_NAMESPACE_BOOL(cond, T, F) #define TOML_ABI_NAMESPACE_START(name) #define TOML_ABI_NAMESPACE_END #else #define TOML_ABI_NAMESPACES 1 - #define TOML_ABI_NAMESPACE_START(name) inline namespace abi_##name { + #define TOML_ABI_NAMESPACE_START(name) inline namespace TOML_CONCAT(abi_, name) { + #define TOML_ABI_NAMESPACE_BOOL(cond, T, F) TOML_ABI_NAMESPACE_START(TOML_CONCAT(TOML_EVAL_BOOL_, cond)(T, F)) #define TOML_ABI_NAMESPACE_END } #endif @@ -447,8 +474,7 @@ TOML_DISABLE_ALL_WARNINGS TOML_POP_WARNINGS #if TOML_CHAR_8_STRINGS - #define TOML_STRING_PREFIX_1(S) u8##S - #define TOML_STRING_PREFIX(S) TOML_STRING_PREFIX_1(S) + #define TOML_STRING_PREFIX(S) TOML_CONCAT(u8, S) #else #define TOML_STRING_PREFIX(S) S #endif @@ -473,7 +499,7 @@ TOML_DISABLE_ALL_WARNINGS #include #include #include -#ifndef TOML_OPTIONAL_TYPE +#if !TOML_HAS_CUSTOM_OPTIONAL_TYPE #include #endif @@ -489,6 +515,10 @@ TOML_POP_WARNINGS #define TOML_LAUNDER(x) x #endif +TOML_PUSH_WARNINGS +TOML_DISABLE_PADDING_WARNINGS +TOML_DISABLE_SHADOW_WARNINGS + namespace toml { using namespace std::string_literals; @@ -518,9 +548,30 @@ namespace toml #endif - TOML_PUSH_WARNINGS - TOML_DISABLE_PADDING_WARNINGS - TOML_DISABLE_SHADOW_WARNINGS // false positive on gcc + #if TOML_WINDOWS_COMPAT + namespace impl + { + [[nodiscard]] TOML_API std::string narrow_char(std::wstring_view) noexcept; + #ifdef __cpp_lib_char8_t + [[nodiscard]] TOML_API std::u8string narrow_char8(std::wstring_view) noexcept; + #endif + template + [[nodiscard]] auto narrow(std::wstring_view str) noexcept + { + #ifdef __cpp_lib_char8_t + if constexpr (std::is_same_v) + return narrow_char8(str); + else + #endif + return narrow_char(str); + } + + [[nodiscard]] TOML_API std::wstring widen(std::string_view) noexcept; + #ifdef __cpp_lib_char8_t + [[nodiscard]] TOML_API std::wstring widen(std::u8string_view) noexcept; + #endif + } + #endif #if !TOML_DOXYGEN @@ -537,15 +588,11 @@ namespace toml template class default_formatter; template class json_formatter; - #ifdef TOML_OPTIONAL_TYPE - TOML_ABI_NAMESPACE_START(custopt) - #else - TOML_ABI_NAMESPACE_START(stdopt) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt) struct date_time; - TOML_ABI_NAMESPACE_END // TOML_OPTIONAL_TYPE + TOML_ABI_NAMESPACE_END // TOML_HAS_CUSTOM_OPTIONAL_TYPE #endif // !TOML_DOXYGEN @@ -563,9 +610,7 @@ namespace toml date_time }; - TOML_POP_WARNINGS // TOML_DISABLE_PADDING_WARNINGS, TOML_DISABLE_SHADOW_WARNINGS - - #ifdef TOML_OPTIONAL_TYPE + #if TOML_HAS_CUSTOM_OPTIONAL_TYPE template using optional = TOML_OPTIONAL_TYPE; @@ -589,11 +634,7 @@ namespace toml using source_path_ptr = std::shared_ptr; - #if TOML_LARGE_FILES - TOML_ABI_NAMESPACE_START(lf) - #else - TOML_ABI_NAMESPACE_START(sf) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_LARGE_FILES, lf, sf) struct TOML_TRIVIAL_ABI source_position { @@ -640,6 +681,18 @@ namespace toml source_position begin; source_position end; source_path_ptr path; + + #if TOML_WINDOWS_COMPAT + + [[nodiscard]] + optional wide_path() const noexcept + { + if (!path || path->empty()) + return {}; + return { impl::widen(*path) }; + } + + #endif }; TOML_ABI_NAMESPACE_END // TOML_LARGE_FILES @@ -710,6 +763,15 @@ namespace toml::impl || std::is_same_v || std::is_same_v; + template + inline constexpr bool is_wide_string = is_one_of< + std::decay_t, + const wchar_t*, + wchar_t*, + std::wstring_view, + std::wstring + >; + template inline constexpr bool is_value_or_promotable = is_value @@ -722,6 +784,9 @@ namespace toml::impl || std::is_same_v || std::is_same_v || std::is_same_v + #if TOML_WINDOWS_COMPAT + || is_wide_string + #endif #ifdef TOML_SMALL_FLOAT_TYPE || std::is_same_v #endif @@ -763,6 +828,18 @@ namespace toml::impl template struct value_promoter { using type = string; }; template <> struct value_promoter { using type = string; }; template <> struct value_promoter { using type = string; }; + #if TOML_WINDOWS_COMPAT + template struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template <> struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template struct value_promoter { using type = string; }; + template <> struct value_promoter { using type = string; }; + template <> struct value_promoter { using type = string; }; + template <> struct value_promoter { using type = string; }; + #endif template <> struct value_promoter { using type = int64_t; }; template <> struct value_promoter { using type = int64_t; }; template <> struct value_promoter { using type = int64_t; }; @@ -916,6 +993,8 @@ namespace toml template inserter(T&&) -> inserter; } +TOML_POP_WARNINGS // TOML_DISABLE_PADDING_WARNINGS, TOML_DISABLE_SHADOW_WARNINGS + #endif //------------------------------------------ ↑ toml_common.h --------------------------------------------------------- @@ -1133,11 +1212,7 @@ namespace toml extern template TOML_API std::ostream& operator << (std::ostream&, const time_offset&); #endif - #ifdef TOML_OPTIONAL_TYPE - TOML_ABI_NAMESPACE_START(custopt) - #else - TOML_ABI_NAMESPACE_START(stdopt) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt) struct date_time { @@ -1217,7 +1292,7 @@ namespace toml } }; - TOML_ABI_NAMESPACE_END // TOML_OPTIONAL_TYPE + TOML_ABI_NAMESPACE_END // TOML_HAS_CUSTOM_OPTIONAL_TYPE template TOML_EXTERNAL_LINKAGE @@ -1303,7 +1378,7 @@ namespace toml::impl stream.write(reinterpret_cast(str), static_cast(len)); } - #if defined(__cpp_lib_char8_t) + #ifdef __cpp_lib_char8_t template TOML_ALWAYS_INLINE @@ -2246,35 +2321,6 @@ namespace toml value(TOML_SMALL_INT_TYPE) -> value; #endif - template - TOML_EXTERNAL_LINKAGE - std::basic_ostream& operator << (std::basic_ostream& lhs, const value& rhs) - { - // this is the same behaviour as default_formatter, but it's so simple that there's - // no need to spin up a new instance of it just for individual values. - - if constexpr (std::is_same_v) - { - impl::print_to_stream('"', lhs); - impl::print_to_stream_with_escapes(rhs.val_, lhs); - impl::print_to_stream('"', lhs); - } - else - impl::print_to_stream(rhs.val_, lhs); - - return lhs; - } - - #if !TOML_ALL_INLINE - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - extern template TOML_API std::ostream& operator << (std::ostream&, const value&); - #endif - TOML_PUSH_WARNINGS TOML_DISABLE_INIT_WARNINGS @@ -2282,7 +2328,13 @@ namespace toml inline optional node::value() const noexcept { static_assert( - impl::is_value || std::is_same_v, + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Retrieving values as wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); + static_assert( + impl::is_value + || std::is_same_v + || (TOML_WINDOWS_COMPAT && std::is_same_v), "Value type must be one of the TOML value types (or string_view)" ); @@ -2297,6 +2349,12 @@ namespace toml { if constexpr (std::is_same_v || std::is_same_v) return { T{ ref_cast().get() } }; + + #if TOML_WINDOWS_COMPAT + else if constexpr (std::is_same_v) + return { impl::widen(ref_cast().get()) }; + #endif + else return {}; } @@ -2364,21 +2422,36 @@ namespace toml template inline auto node::value_or(T&& default_value) const noexcept { + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Retrieving values as wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); static_assert( impl::is_value_or_promotable>, "Default value type must be (or be promotable to) one of the TOML value types" ); - using value_type = impl::promoted>; - using return_type = std::conditional_t< - std::is_same_v, - std::conditional_t, string>, string, string_view>, - value_type - >; + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + { + if (this->type() == node_type::string) + return impl::widen(ref_cast().get()); + return std::wstring{ std::forward(default_value) }; + } + else + #endif + { + using value_type = impl::promoted>; + using return_type = std::conditional_t< + std::is_same_v, + std::conditional_t, string>, string, string_view>, + value_type + >; - if (auto val = this->value()) - return *val; - return return_type{ std::forward(default_value) }; + if (auto val = this->value()) + return *val; + return return_type{ std::forward(default_value) }; + } } } @@ -2569,11 +2642,20 @@ namespace toml::impl } else { + static_assert( + !is_wide_string || TOML_WINDOWS_COMPAT, + "Instantiating values from wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); static_assert( is_value_or_promotable, "Value initializers must be (or be promotable to) one of the TOML value types" ); - return new value{ std::forward(val) }; + #if TOML_WINDOWS_COMPAT + if constexpr (is_wide_string) + return new value{ narrow(std::forward(val)) }; + else + #endif + return new value{ std::forward(val) }; } } @@ -3040,23 +3122,45 @@ namespace toml::impl string key; std::unique_ptr value; - template - table_init_pair(string&& k, T && v) noexcept + template + table_init_pair(string&& k, V&& v) noexcept : key{ std::move(k) }, - value{ make_node(std::forward(v)) } + value{ make_node(std::forward(v)) } {} - template - table_init_pair(string_view k, T&& v) noexcept + template + table_init_pair(string_view k, V&& v) noexcept : key{ k }, - value{ make_node(std::forward(v)) } + value{ make_node(std::forward(v)) } {} - template - table_init_pair(const string_char* k, T&& v) noexcept + template + table_init_pair(const string_char* k, V&& v) noexcept : key{ k }, - value{ make_node(std::forward(v)) } + value{ make_node(std::forward(v)) } {} + + #if TOML_WINDOWS_COMPAT + + template + table_init_pair(std::wstring&& k, V&& v) noexcept + : key{ narrow(k) }, + value{ make_node(std::forward(v)) } + {} + + template + table_init_pair(std::wstring_view k, V&& v) noexcept + : key{ narrow(k) }, + value{ make_node(std::forward(v)) } + {} + + template + table_init_pair(const wchar_t* k, V&& v) noexcept + : key{ narrow(std::wstring_view{ k }) }, + value{ make_node(std::forward(v)) } + {} + + #endif }; } @@ -3112,6 +3216,14 @@ namespace toml void is_inline(bool val) noexcept; [[nodiscard]] node_view operator[] (string_view key) noexcept; [[nodiscard]] node_view operator[] (string_view key) const noexcept; + + #if TOML_WINDOWS_COMPAT + + [[nodiscard]] node_view operator[] (std::wstring_view key) noexcept; + [[nodiscard]] node_view operator[] (std::wstring_view key) const noexcept; + + #endif // TOML_WINDOWS_COMPAT + [[nodiscard]] iterator begin() noexcept; [[nodiscard]] const_iterator begin() const noexcept; [[nodiscard]] const_iterator cbegin() const noexcept; @@ -3124,20 +3236,34 @@ namespace toml template + || impl::is_wide_string >> std::pair insert(K&& key, V&& val) noexcept { - auto ipos = values.lower_bound(key); - if (ipos == values.end() || ipos->first != key) + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); + + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return insert(impl::narrow(std::forward(key)), std::forward(val)); + else + #endif { - ipos = values.emplace_hint(ipos, std::forward(key), impl::make_node(std::forward(val))); - return { ipos, true }; + auto ipos = values.lower_bound(key); + if (ipos == values.end() || ipos->first != key) + { + ipos = values.emplace_hint(ipos, std::forward(key), impl::make_node(std::forward(val))); + return { ipos, true }; + } + return { ipos, false }; } - return { ipos, false }; } template + !std::is_convertible_v + && !impl::is_wide_string >> void insert(Iter first, Iter last) noexcept { @@ -3155,39 +3281,64 @@ namespace toml template std::pair insert_or_assign(K&& key, V&& val) noexcept { - auto ipos = values.lower_bound(key); - if (ipos == values.end() || ipos->first != key) - { - ipos = values.emplace_hint(ipos, std::forward(key), impl::make_node(std::forward(val))); - return { ipos, true }; - } + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Insertion using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); + + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return insert_or_assign(impl::narrow(std::forward(key)), std::forward(val)); else + #endif { - (*ipos).second.reset(impl::make_node(std::forward(val))); - return { ipos, false }; + auto ipos = values.lower_bound(key); + if (ipos == values.end() || ipos->first != key) + { + ipos = values.emplace_hint(ipos, std::forward(key), impl::make_node(std::forward(val))); + return { ipos, true }; + } + else + { + (*ipos).second.reset(impl::make_node(std::forward(val))); + return { ipos, false }; + } } } template std::pair emplace(K&& key, V&&... args) noexcept { - using type = impl::unwrapped; static_assert( - impl::is_value_or_node, - "Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array" + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Emplacement using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." ); - auto ipos = values.lower_bound(key); - if (ipos == values.end() || ipos->first != key) + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return emplace(impl::narrow(std::forward(key)), std::forward(args)...); + else + #endif { - ipos = values.emplace_hint( - ipos, - std::forward(key), - new impl::node_of{ std::forward(args)... } + + using type = impl::unwrapped; + static_assert( + impl::is_value_or_node, + "Emplacement type parameter must be one of the basic value types, a toml::table, or a toml::array" ); - return { ipos, true }; + + auto ipos = values.lower_bound(key); + if (ipos == values.end() || ipos->first != key) + { + ipos = values.emplace_hint( + ipos, + std::forward(key), + new impl::node_of{ std::forward(args)... } + ); + return { ipos, true }; + } + return { ipos, false }; } - return { ipos, false }; } iterator erase(iterator pos) noexcept; @@ -3195,20 +3346,33 @@ namespace toml iterator erase(const_iterator first, const_iterator last) noexcept; bool erase(string_view key) noexcept; + #if TOML_WINDOWS_COMPAT + + bool erase(std::wstring_view key) noexcept; + + #endif + private: template [[nodiscard]] static auto do_get(Map& vals, const Key& key) noexcept + -> std::conditional_t, const node*, node*> { - using return_type = std::conditional_t< - std::is_const_v, - const node*, - node* - >; + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Retrieval using wide-character keys is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); - if (auto it = vals.find(key); it != vals.end()) - return return_type{ it->second.get() }; - return return_type{}; + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return do_get(vals, impl::narrow(key)); + else + #endif + { + if (auto it = vals.find(key); it != vals.end()) + return { it->second.get() }; + return {}; + } } template @@ -3222,11 +3386,7 @@ namespace toml [[nodiscard]] TOML_ALWAYS_INLINE static bool do_contains(Map& vals, const Key& key) noexcept { - #if TOML_CPP >= 20 - return vals.contains(key); - #else - return do_get(vals, key) != nullptr; - #endif + return do_get(vals, key) != nullptr; } public: @@ -3235,6 +3395,17 @@ namespace toml [[nodiscard]] const node* get(string_view key) const noexcept; [[nodiscard]] iterator find(string_view key) noexcept; [[nodiscard]] const_iterator find(string_view key) const noexcept; + [[nodiscard]] bool contains(string_view key) const noexcept; + + #if TOML_WINDOWS_COMPAT + + [[nodiscard]] node* get(std::wstring_view key) noexcept; + [[nodiscard]] const node* get(std::wstring_view key) const noexcept; + [[nodiscard]] iterator find(std::wstring_view key) noexcept; + [[nodiscard]] const_iterator find(std::wstring_view key) const noexcept; + [[nodiscard]] bool contains(std::wstring_view key) const noexcept; + + #endif // TOML_WINDOWS_COMPAT template [[nodiscard]] impl::node_of* get_as(string_view key) noexcept @@ -3248,7 +3419,22 @@ namespace toml return do_get_as(values, key); } - [[nodiscard]] bool contains(string_view key) const noexcept; + #if TOML_WINDOWS_COMPAT + + template + [[nodiscard]] impl::node_of* get_as(std::wstring_view key) noexcept + { + return get_as(impl::narrow(key)); + } + + template + [[nodiscard]] const impl::node_of* get_as(std::wstring_view key) const noexcept + { + return get_as(impl::narrow(key)); + } + + #endif // TOML_WINDOWS_COMPAT + friend bool operator == (const table& lhs, const table& rhs) noexcept; friend bool operator != (const table& lhs, const table& rhs) noexcept; @@ -3299,7 +3485,10 @@ namespace toml TOML_NODISCARD_CTOR node_view() noexcept = default; [[nodiscard]] explicit operator bool() const noexcept { return node_ != nullptr; } - [[nodiscard]] viewed_type* get() const noexcept { return node_; } + [[nodiscard]] viewed_type* node() const noexcept { return node_; } + + [[nodiscard, deprecated("use node_view::node() instead (the name is better)")]] + viewed_type* get() const noexcept { return node_; } [[nodiscard]] node_type type() const noexcept { return node_ ? node_->type() : node_type::none; } [[nodiscard]] bool is_table() const noexcept { return node_ && node_->is_table(); } @@ -3422,13 +3611,25 @@ namespace toml template >> [[nodiscard]] friend bool operator == (const node_view& lhs, const U& rhs) noexcept { - const auto val = lhs.as>(); - return val && *val == rhs; + static_assert( + !impl::is_wide_string || TOML_WINDOWS_COMPAT, + "Comparison with wide-character strings is only supported on Windows with TOML_WINDOWS_COMPAT enabled." + ); + + #if TOML_WINDOWS_COMPAT + if constexpr (impl::is_wide_string) + return lhs == impl::narrow(rhs); + else + #endif + { + const auto val = lhs.as>(); + return val && *val == rhs; + } } TOML_ASYMMETRICAL_EQUALITY_OPS( - const node_view&, - const U&, - template >> + const node_view&, + const U&, + template >> ) template @@ -3454,6 +3655,17 @@ namespace toml return { nullptr }; } + #if TOML_WINDOWS_COMPAT + + [[nodiscard]] node_view operator[] (std::wstring_view key) const noexcept + { + if (auto tbl = this->as_table()) + return { tbl->get(key) }; + return { nullptr }; + } + + #endif // TOML_WINDOWS_COMPAT + [[nodiscard]] node_view operator[] (size_t index) const noexcept { if (auto arr = this->as_array()) @@ -4469,7 +4681,12 @@ namespace toml enum class format_flags : uint8_t { none, - quote_dates_and_times = 1 + + quote_dates_and_times = 1, + + allow_literal_strings = 2, + + allow_multi_line_strings = 4, }; [[nodiscard]] @@ -4504,7 +4721,6 @@ namespace toml::impl protected: [[nodiscard]] const toml::node& source() const noexcept { return *source_; } - [[nodiscard]] format_flags flags() const noexcept { return flags_; } [[nodiscard]] std::basic_ostream& stream() const noexcept { return *stream_; } static constexpr size_t indent_columns = 4; @@ -4513,9 +4729,29 @@ namespace toml::impl void indent(int8_t level) noexcept { indent_ = level; } void increase_indent() noexcept { indent_++; } void decrease_indent() noexcept { indent_--; } + [[nodiscard]] + bool quote_dates_and_times() const noexcept + { + return (flags_ & format_flags::quote_dates_and_times) != format_flags::none; + } + + [[nodiscard]] + bool literal_strings_allowed() const noexcept + { + return (flags_ & format_flags::allow_literal_strings) != format_flags::none; + } + + [[nodiscard]] + bool multi_line_strings_allowed() const noexcept + { + return (flags_ & format_flags::allow_multi_line_strings) != format_flags::none; + } + + void clear_naked_newline() noexcept + { + naked_newline_ = false; + } - TOML_ALWAYS_INLINE - void clear_naked_newline() noexcept { naked_newline_ = false; } void attach(std::basic_ostream& stream) noexcept { indent_ = {}; @@ -4546,17 +4782,62 @@ namespace toml::impl } } - void print_quoted_string(toml::string_view str) + void print_quoted_string(toml::string_view str, bool allow_multi_line = true) { + auto literals = literal_strings_allowed(); if (str.empty()) - print_to_stream("\"\""sv, *stream_); + { + print_to_stream(literals ? "''"sv : "\"\""sv, *stream_); + clear_naked_newline(); + return; + } + + auto multi_line = allow_multi_line && multi_line_strings_allowed(); + if (multi_line || literals) + { + utf8_decoder decoder; + bool has_line_breaks = false; + bool has_control_chars = false; + bool has_single_quotes = false; + for (size_t i = 0; i < str.length() && !(has_line_breaks && has_control_chars && has_single_quotes); i++) + { + decoder(static_cast(str[i])); + if (decoder.error()) + { + has_line_breaks = false; + has_control_chars = true; //force "" + has_single_quotes = true; + break; + } + else if (decoder.has_code_point()) + { + if (is_line_break(decoder.codepoint)) + has_line_breaks = true; + else if (is_nontab_control_character(decoder.codepoint)) + has_control_chars = true; + else if (decoder.codepoint == U'\'') + has_single_quotes = true; + } + } + multi_line = multi_line && has_line_breaks; + literals = literals && !has_control_chars && !(!multi_line && has_single_quotes); + } + + if (literals) + { + const auto quot = multi_line ? "'''"sv : "'"sv; + print_to_stream(quot, *stream_); + print_to_stream(str, *stream_); + print_to_stream(quot, *stream_); + } else { - print_to_stream('"', *stream_); + const auto quot = multi_line ? R"(""")"sv : R"(")"sv; + print_to_stream(quot, *stream_); print_to_stream_with_escapes(str, *stream_); - print_to_stream('"', *stream_); + print_to_stream(quot, *stream_); } - naked_newline_ = false; + clear_naked_newline(); } template @@ -4575,17 +4856,18 @@ namespace toml::impl if constexpr (is_dt) { - if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none) - print_to_stream('"', *stream_); - } - - *stream_ << val; - - if constexpr (is_dt) - { - if ((flags_ & format_flags::quote_dates_and_times) != format_flags::none) - print_to_stream('"', *stream_); + if (quote_dates_and_times()) + { + const auto quot = literal_strings_allowed() ? '\'' : '"'; + print_to_stream(quot, *stream_); + print_to_stream(*val, *stream_); + print_to_stream(quot, *stream_); + } + else + print_to_stream(*val, *stream_); } + else + print_to_stream(*val, *stream_); naked_newline_ = false; } @@ -4676,7 +4958,11 @@ namespace toml } if (requiresQuotes) - base::print_quoted_string(str); + { + impl::print_to_stream('"', base::stream()); + impl::print_to_stream_with_escapes(str, base::stream()); + impl::print_to_stream('"', base::stream()); + } else impl::print_to_stream(str, base::stream()); } @@ -4907,8 +5193,11 @@ namespace toml public: + static constexpr format_flags default_flags + = format_flags::allow_literal_strings | format_flags::allow_multi_line_strings; + TOML_NODISCARD_CTOR - explicit default_formatter(const toml::node& source, format_flags flags = {}) noexcept + explicit default_formatter(const toml::node& source, format_flags flags = default_flags) noexcept : base{ source, flags } {} @@ -4958,11 +5247,25 @@ namespace toml return lhs << default_formatter{ rhs }; } + template + TOML_EXTERNAL_LINKAGE + std::basic_ostream& operator << (std::basic_ostream& lhs, const value& rhs) + { + return lhs << default_formatter{ rhs }; + } + #if !TOML_ALL_INLINE extern template TOML_API std::ostream& operator << (std::ostream&, default_formatter&); extern template TOML_API std::ostream& operator << (std::ostream&, default_formatter&&); extern template TOML_API std::ostream& operator << (std::ostream&, const table&); extern template TOML_API std::ostream& operator << (std::ostream&, const array&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); + extern template TOML_API std::ostream& operator << (std::ostream&, const value&); #endif } @@ -5039,9 +5342,11 @@ namespace toml public: + static constexpr format_flags default_flags = format_flags::quote_dates_and_times; + TOML_NODISCARD_CTOR - explicit json_formatter(const toml::node& source, format_flags flags = {}) noexcept - : base{ source, flags | format_flags::quote_dates_and_times } + explicit json_formatter(const toml::node& source, format_flags flags = default_flags) noexcept + : base{ source, flags } {} template @@ -5104,11 +5409,7 @@ TOML_DISABLE_VTABLE_WARNINGS namespace toml { - #if TOML_LARGE_FILES - TOML_ABI_NAMESPACE_START(lf) - #else - TOML_ABI_NAMESPACE_START(sf) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_LARGE_FILES, lf, sf) #if TOML_DOXYGEN || !TOML_EXCEPTIONS @@ -5346,11 +5647,7 @@ namespace toml::impl } }; - #if TOML_LARGE_FILES - TOML_ABI_NAMESPACE_START(impl_lf) - #else - TOML_ABI_NAMESPACE_START(impl_sf) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_LARGE_FILES, impl_lf, impl_sf) struct utf8_codepoint final { @@ -5579,13 +5876,10 @@ namespace toml::impl template utf8_reader(std::basic_string_view, std::string_view) -> utf8_reader>; - - template - utf8_reader(std::basic_istream&, std::string_view) -> utf8_reader>; - template utf8_reader(std::basic_string_view, std::string&&) -> utf8_reader>; - + template + utf8_reader(std::basic_istream&, std::string_view) -> utf8_reader>; template utf8_reader(std::basic_istream&, std::string&&) -> utf8_reader>; @@ -5839,6 +6133,20 @@ namespace toml return is_err ? node_view{} : get()[key]; } + #if TOML_WINDOWS_COMPAT + + [[nodiscard]] node_view operator[] (std::wstring_view key) noexcept + { + return is_err ? node_view{} : get()[key]; + } + + [[nodiscard]] node_view operator[] (std::wstring_view key) const noexcept + { + return is_err ? node_view{} : get()[key]; + } + + #endif // TOML_WINDOWS_COMPAT + [[nodiscard]] table_iterator begin() noexcept { return is_err ? table_iterator{} : get().begin(); @@ -5881,11 +6189,7 @@ namespace toml namespace toml::impl { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(impl_ex) - #else - TOML_ABI_NAMESPACE_START(impl_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex) [[nodiscard]] TOML_API parse_result do_parse(utf8_reader_interface&&) TOML_MAY_THROW; @@ -5893,13 +6197,21 @@ namespace toml::impl TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS } +#if TOML_EXCEPTIONS + #define TOML_THROW_PARSE_ERROR(msg, path) \ + throw parse_error{ \ + msg, source_position{}, std::make_shared(std::move(path)) \ + } +#else + #define TOML_THROW_PARSE_ERROR(msg, path) \ + return parse_result{ parse_error{ \ + msg, source_position{}, std::make_shared(std::move(path)) \ + }} +#endif + namespace toml { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(parse_ex) - #else - TOML_ABI_NAMESPACE_START(parse_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, parse_ex, parse_noex) [[nodiscard]] TOML_API @@ -5909,6 +6221,14 @@ namespace toml TOML_API parse_result parse(std::string_view doc, std::string&& source_path) TOML_MAY_THROW; + #if TOML_WINDOWS_COMPAT + + [[nodiscard]] + TOML_API + parse_result parse(std::string_view doc, std::wstring_view source_path) TOML_MAY_THROW; + + #endif // TOML_WINDOWS_COMPAT + #ifdef __cpp_lib_char8_t [[nodiscard]] @@ -5919,6 +6239,14 @@ namespace toml TOML_API parse_result parse(std::u8string_view doc, std::string&& source_path) TOML_MAY_THROW; + #if TOML_WINDOWS_COMPAT + + [[nodiscard]] + TOML_API + parse_result parse(std::u8string_view doc, std::wstring_view source_path) TOML_MAY_THROW; + + #endif // TOML_WINDOWS_COMPAT + #endif // __cpp_lib_char8_t template @@ -5947,6 +6275,18 @@ namespace toml return impl::do_parse(impl::utf8_reader{ doc, std::move(source_path) }); } + #if TOML_WINDOWS_COMPAT + + template + [[nodiscard]] + TOML_EXTERNAL_LINKAGE + parse_result parse(std::basic_istream& doc, std::wstring_view source_path) TOML_MAY_THROW + { + return parse(doc, impl::narrow(source_path)); + } + + #endif // TOML_WINDOWS_COMPAT + // Q: "why are the parse_file functions templated??" // A: I don't want to force users to drag in if they're not going to do // any parsing directly from files. Keeping them templated delays their instantiation @@ -5959,25 +6299,32 @@ namespace toml parse_result parse_file(std::basic_string_view file_path) TOML_MAY_THROW { static_assert( - sizeof(Char) == 1, - "The path's character type must be 1 byte in size." + !std::is_same_v || TOML_WINDOWS_COMPAT, + "Wide-character file paths are only supported on Windows with TOML_WINDOWS_COMPAT enabled." ); - static_assert( - sizeof(StreamChar) == 1, - "The stream's character type must be 1 byte in size." - ); - - #if TOML_EXCEPTIONS - #define TOML_PARSE_FILE_ERROR(msg, pos) \ - throw parse_error{ msg, pos, std::make_shared(std::move(file_path_str)) } + #if TOML_WINDOWS_COMPAT + static_assert( + sizeof(Char) == 1 || std::is_same_v, + "The file path's underlying character type must be wchar_t or be 1 byte in size." + ); #else - #define TOML_PARSE_FILE_ERROR(msg, pos) \ - return parse_result{ \ - parse_error{ msg, pos, std::make_shared(std::move(file_path_str)) } \ - } + static_assert( + sizeof(Char) == 1, + "The file path's underlying character type must be 1 byte in size." + ); #endif + static_assert( + std::is_same_v, + "StreamChar must be 'char' (it is as an instantiation-delaying hack and is not user-configurable)." + ); - auto file_path_str = std::string(reinterpret_cast(file_path.data()), file_path.length()); + std::string file_path_str; + #if TOML_WINDOWS_COMPAT + if constexpr (std::is_same_v) + file_path_str = impl::narrow(file_path); + else + #endif + file_path_str = std::string_view{ reinterpret_cast(file_path.data()), file_path.length() }; // open file with a custom-sized stack buffer using ifstream = std::basic_ifstream; @@ -5986,12 +6333,12 @@ namespace toml file.rdbuf()->pubsetbuf(file_buffer, sizeof(file_buffer)); file.open(file_path_str, ifstream::in | ifstream::binary | ifstream::ate); if (!file.is_open()) - TOML_PARSE_FILE_ERROR("File could not be opened for reading", source_position{}); + TOML_THROW_PARSE_ERROR("File could not be opened for reading", file_path_str); // get size const auto file_size = file.tellg(); if (file_size == -1) - TOML_PARSE_FILE_ERROR("Could not determine file size", source_position{}); + TOML_THROW_PARSE_ERROR("Could not determine file size", file_path_str); file.seekg(0, std::ios::beg); // read the whole file into memory first if the file isn't too large @@ -6007,8 +6354,6 @@ namespace toml // otherwise parse it using the streams else return parse(file, std::move(file_path_str)); - - #undef TOML_PARSE_FILE_ERROR } #if !TOML_ALL_INLINE @@ -6018,6 +6363,9 @@ namespace toml #ifdef __cpp_lib_char8_t extern template TOML_API parse_result parse_file(std::u8string_view) TOML_MAY_THROW; #endif + #if TOML_WINDOWS_COMPAT + extern template TOML_API parse_result parse_file(std::wstring_view) TOML_MAY_THROW; + #endif #endif template @@ -6038,11 +6386,7 @@ namespace toml inline namespace literals { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(lit_ex) - #else - TOML_ABI_NAMESPACE_START(lit_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, lit_ex, lit_noex) [[nodiscard]] TOML_API @@ -6058,8 +6402,11 @@ namespace toml TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS } + } +#undef TOML_THROW_PARSE_ERROR + TOML_POP_WARNINGS // TOML_DISABLE_PADDING_WARNINGS #endif @@ -6499,6 +6846,57 @@ namespace toml return do_contains(values, key); } + #if TOML_WINDOWS_COMPAT + + TOML_EXTERNAL_LINKAGE + node_view table::operator[] (std::wstring_view key) noexcept + { + return { this->get(key) }; + } + TOML_EXTERNAL_LINKAGE + node_view table::operator[] (std::wstring_view key) const noexcept + { + return { this->get(key) }; + } + + TOML_EXTERNAL_LINKAGE + bool table::erase(std::wstring_view key) noexcept + { + return erase(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + node* table::get(std::wstring_view key) noexcept + { + return get(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + const node* table::get(std::wstring_view key) const noexcept + { + return get(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + table::iterator table::find(std::wstring_view key) noexcept + { + return find(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + table::const_iterator table::find(std::wstring_view key) const noexcept + { + return find(impl::narrow(key)); + } + + TOML_EXTERNAL_LINKAGE + bool table::contains(std::wstring_view key) const noexcept + { + return contains(impl::narrow(key)); + } + + #endif // TOML_WINDOWS_COMPAT + TOML_API TOML_EXTERNAL_LINKAGE bool operator == (const table& lhs, const table& rhs) noexcept @@ -6739,6 +7137,94 @@ namespace toml TOML_POP_WARNINGS // TOML_DISABLE_SWITCH_WARNINGS, TOML_DISABLE_FLOAT_WARNINGS +// implementations of windows wide string nonsense +#if TOML_WINDOWS_COMPAT + +TOML_PUSH_WARNINGS +TOML_DISABLE_ALL_WARNINGS +#include // fuckkkk :( +TOML_POP_WARNINGS + +namespace toml::impl +{ + TOML_API + TOML_EXTERNAL_LINKAGE + std::string narrow_char(std::wstring_view str) noexcept + { + if (str.empty()) + return {}; + + std::string s; + const auto len = WideCharToMultiByte( + 65001, 0, str.data(), static_cast(str.length()), nullptr, 0, nullptr, nullptr + ); + if (len) + { + s.resize(static_cast(len)); + WideCharToMultiByte(65001, 0, str.data(), static_cast(str.length()), s.data(), len, nullptr, nullptr); + } + return s; + } + + #ifdef __cpp_lib_char8_t + + TOML_API + TOML_EXTERNAL_LINKAGE + std::u8string narrow_char8(std::wstring_view str) noexcept + { + if (str.empty()) + return {}; + + std::u8string s; + const auto len = WideCharToMultiByte( + 65001, 0, str.data(), static_cast(str.length()), nullptr, 0, nullptr, nullptr + ); + if (len) + { + s.resize(static_cast(len)); + WideCharToMultiByte( + 65001, 0, str.data(), static_cast(str.length()), reinterpret_cast(s.data()), len, nullptr, nullptr + ); + } + return s; + } + + #endif // __cpp_lib_char8_t + + TOML_API + TOML_EXTERNAL_LINKAGE + std::wstring widen(std::string_view str) noexcept + { + if (str.empty()) + return {}; + + std::wstring s; + const auto len = MultiByteToWideChar(65001, 0, str.data(), static_cast(str.length()), nullptr, 0); + if (len) + { + s.resize(static_cast(len)); + MultiByteToWideChar(65001, 0, str.data(), static_cast(str.length()), s.data(), len); + } + return s; + } + + #ifdef __cpp_lib_char8_t + + TOML_API + TOML_EXTERNAL_LINKAGE + std::wstring widen(std::u8string_view str) noexcept + { + if (str.empty()) + return {}; + + return widen(std::string_view{ reinterpret_cast(str.data()), str.length() }); + } + + #endif // __cpp_lib_char8_t +} + +#endif // TOML_WINDOWS_COMPAT + #endif //---------- ↑ toml_default_formatter.hpp ---------------------------------------------------------------------------- @@ -6769,7 +7255,7 @@ namespace toml base::print_newline(true); base::print_indent(); - base::print_quoted_string(k); + base::print_quoted_string(k, false); impl::print_to_stream(" : "sv, base::stream()); const auto type = v.type(); @@ -7043,12 +7529,6 @@ namespace TOML_INTERNAL_NAMESPACE namespace toml::impl { - #if defined(NDEBUG) || !defined(_DEBUG) - #define assert_or_assume(cond) TOML_ASSUME(cond) - #else - #define assert_or_assume(cond) TOML_ASSERT(cond) - #endif - // Q: "what the fuck is this? MACROS????" // A: The parser needs to work in exceptionless mode (returning error objects directly) // and exception mode (reporting parse failures by throwing). Two totally different control flows. @@ -7057,6 +7537,12 @@ namespace toml::impl // They're all #undef'd at the bottom of the parser's implementation so they should be harmless outside // of toml++. + #if defined(NDEBUG) || !defined(_DEBUG) + #define assert_or_assume(cond) TOML_ASSUME(cond) + #else + #define assert_or_assume(cond) TOML_ASSERT(cond) + #endif + #define is_eof() !cp #define assert_not_eof() assert_or_assume(cp) #define return_if_eof(...) do { if (is_eof()) return __VA_ARGS__; } while(false) @@ -7088,11 +7574,7 @@ namespace toml::impl set_error_and_return_if_eof(__VA_ARGS__); \ } while (false) - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(impl_ex) - #else - TOML_ABI_NAMESPACE_START(impl_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, impl_ex, impl_noex) class parser final { @@ -9691,33 +10173,29 @@ namespace toml::impl TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS + #undef push_parse_scope_2 + #undef push_parse_scope_1 + #undef push_parse_scope + #undef TOML_RETURNS_BY_THROWING + #undef is_eof + #undef assert_not_eof + #undef return_if_eof + #undef is_error + #undef return_after_error + #undef assert_not_error + #undef return_if_error + #undef return_if_error_or_eof + #undef set_error_and_return + #undef set_error_and_return_default + #undef set_error_and_return_if_eof + #undef advance_and_return_if_error + #undef advance_and_return_if_error_or_eof + #undef assert_or_assume } -#undef push_parse_scope_2 -#undef push_parse_scope_1 -#undef push_parse_scope -#undef TOML_RETURNS_BY_THROWING -#undef is_eof -#undef assert_not_eof -#undef return_if_eof -#undef is_error -#undef return_after_error -#undef assert_not_error -#undef return_if_error -#undef return_if_error_or_eof -#undef set_error_and_return -#undef set_error_and_return_default -#undef set_error_and_return_if_eof -#undef advance_and_return_if_error -#undef advance_and_return_if_error_or_eof - namespace toml { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(parse_ex) - #else - TOML_ABI_NAMESPACE_START(parse_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, parse_ex, parse_noex) TOML_API TOML_EXTERNAL_LINKAGE @@ -9733,6 +10211,17 @@ namespace toml return impl::do_parse(impl::utf8_reader{ doc, std::move(source_path) }); } + #if TOML_WINDOWS_COMPAT + + TOML_API + TOML_EXTERNAL_LINKAGE + parse_result parse(std::string_view doc, std::wstring_view source_path) TOML_MAY_THROW + { + return impl::do_parse(impl::utf8_reader{ doc, impl::narrow(source_path) }); + } + + #endif // TOML_WINDOWS_COMPAT + #ifdef __cpp_lib_char8_t TOML_API @@ -9749,17 +10238,24 @@ namespace toml return impl::do_parse(impl::utf8_reader{ doc, std::move(source_path) }); } + #if TOML_WINDOWS_COMPAT + + TOML_API + TOML_EXTERNAL_LINKAGE + parse_result parse(std::u8string_view doc, std::wstring_view source_path) TOML_MAY_THROW + { + return impl::do_parse(impl::utf8_reader{ doc, impl::narrow(source_path) }); + } + + #endif // TOML_WINDOWS_COMPAT + #endif // __cpp_lib_char8_t TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS inline namespace literals { - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(lit_ex) - #else - TOML_ABI_NAMESPACE_START(lit_noex) - #endif + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, lit_ex, lit_noex) TOML_API TOML_EXTERNAL_LINKAGE @@ -9802,6 +10298,7 @@ TOML_DISABLE_ALL_WARNINGS #include TOML_POP_WARNINGS +// template instantiations namespace toml { // value<> @@ -9855,30 +10352,33 @@ namespace toml template TOML_API void print_floating_point_to_stream(float, std::ostream&, bool); template TOML_API void print_floating_point_to_stream(double, std::ostream&, bool); } - - // parser machinery - #if TOML_PARSER - - // parse error ostream - template TOML_API std::ostream& operator << (std::ostream&, const parse_error&); - - // parse() and parse_file() - #if TOML_EXCEPTIONS - TOML_ABI_NAMESPACE_START(parse_ex) - #else - TOML_ABI_NAMESPACE_START(parse_noex) - #endif - template TOML_API parse_result parse(std::istream&, std::string_view) TOML_MAY_THROW; - template TOML_API parse_result parse(std::istream&, std::string&&) TOML_MAY_THROW; - template TOML_API parse_result parse_file(std::string_view) TOML_MAY_THROW; - #ifdef __cpp_lib_char8_t - template TOML_API parse_result parse_file(std::u8string_view) TOML_MAY_THROW; - #endif - TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS - - #endif // TOML_PARSER } +// parser instantiations +#if TOML_PARSER + +namespace toml +{ + // parse error ostream + template TOML_API std::ostream& operator << (std::ostream&, const parse_error&); + + // parse() and parse_file() + TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, parse_ex, parse_noex) + + template TOML_API parse_result parse(std::istream&, std::string_view) TOML_MAY_THROW; + template TOML_API parse_result parse(std::istream&, std::string&&) TOML_MAY_THROW; + template TOML_API parse_result parse_file(std::string_view) TOML_MAY_THROW; + #ifdef __cpp_lib_char8_t + template TOML_API parse_result parse_file(std::u8string_view) TOML_MAY_THROW; + #endif + #if TOML_WINDOWS_COMPAT + template TOML_API parse_result parse_file(std::wstring_view) TOML_MAY_THROW; + #endif + + TOML_ABI_NAMESPACE_END // TOML_EXCEPTIONS +} +#endif // TOML_PARSER + #endif //--------------------------------------------------------------------------------------- ↑ toml_instantiations.hpp -- @@ -9907,7 +10407,6 @@ namespace toml #undef TOML_UNREACHABLE #undef TOML_INTERFACE #undef TOML_EMPTY_BASES - #undef TOML_CPP_VERSION #undef TOML_CPP #undef TOML_MAY_THROW #undef TOML_NO_DEFAULT_CASE @@ -9920,7 +10419,6 @@ namespace toml #undef TOML_LANG_HIGHER_THAN #undef TOML_LANG_AT_LEAST #undef TOML_LANG_UNRELEASED - #undef TOML_STRING_PREFIX_1 #undef TOML_STRING_PREFIX #undef TOML_UNDEF_MACROS #undef TOML_RELOPS_REORDERING @@ -9934,9 +10432,15 @@ namespace toml #undef TOML_TRIVIAL_ABI #undef TOML_ABI_NAMESPACES #undef TOML_ABI_NAMESPACE_START + #undef TOML_ABI_NAMESPACE_BOOL #undef TOML_ABI_NAMESPACE_END #undef TOML_PARSER_TYPENAME #undef TOML_LAUNDER + #undef TOML_CONCAT_1 + #undef TOML_CONCAT + #undef TOML_EVAL_BOOL_1 + #undef TOML_EVAL_BOOL_0 + #undef TOML_HAS_CUSTOM_OPTIONAL_TYPE #endif diff --git a/vs/test_char.vcxproj b/vs/test_char.vcxproj index 5fd7111..ac4b83d 100644 --- a/vs/test_char.vcxproj +++ b/vs/test_char.vcxproj @@ -90,6 +90,7 @@ + diff --git a/vs/test_char8.vcxproj b/vs/test_char8.vcxproj index 086c6a2..8ffd4bb 100644 --- a/vs/test_char8.vcxproj +++ b/vs/test_char8.vcxproj @@ -90,6 +90,7 @@ + diff --git a/vs/test_char8_noexcept.vcxproj b/vs/test_char8_noexcept.vcxproj index f5b7ec0..cd5a771 100644 --- a/vs/test_char8_noexcept.vcxproj +++ b/vs/test_char8_noexcept.vcxproj @@ -92,6 +92,7 @@ + diff --git a/vs/test_char8_strict.vcxproj b/vs/test_char8_strict.vcxproj index c0314b8..6b4949b 100644 --- a/vs/test_char8_strict.vcxproj +++ b/vs/test_char8_strict.vcxproj @@ -90,6 +90,7 @@ + diff --git a/vs/test_char8_strict_noexcept.vcxproj b/vs/test_char8_strict_noexcept.vcxproj index c6d1621..7369e0f 100644 --- a/vs/test_char8_strict_noexcept.vcxproj +++ b/vs/test_char8_strict_noexcept.vcxproj @@ -92,6 +92,7 @@ + diff --git a/vs/test_char_noexcept.vcxproj b/vs/test_char_noexcept.vcxproj index 5384b39..be0627f 100644 --- a/vs/test_char_noexcept.vcxproj +++ b/vs/test_char_noexcept.vcxproj @@ -92,6 +92,7 @@ + diff --git a/vs/test_char_strict.vcxproj b/vs/test_char_strict.vcxproj index 723568c..85a937b 100644 --- a/vs/test_char_strict.vcxproj +++ b/vs/test_char_strict.vcxproj @@ -90,6 +90,7 @@ + diff --git a/vs/test_char_strict_noexcept.vcxproj b/vs/test_char_strict_noexcept.vcxproj index 8cba66d..a335830 100644 --- a/vs/test_char_strict_noexcept.vcxproj +++ b/vs/test_char_strict_noexcept.vcxproj @@ -92,6 +92,7 @@ + diff --git a/vs/test_x86_char.vcxproj b/vs/test_x86_char.vcxproj index 8f55779..95d2b94 100644 --- a/vs/test_x86_char.vcxproj +++ b/vs/test_x86_char.vcxproj @@ -90,6 +90,7 @@ + diff --git a/vs/test_x86_char8.vcxproj b/vs/test_x86_char8.vcxproj index 528506c..9556563 100644 --- a/vs/test_x86_char8.vcxproj +++ b/vs/test_x86_char8.vcxproj @@ -90,6 +90,7 @@ + diff --git a/vs/test_x86_char8_noexcept.vcxproj b/vs/test_x86_char8_noexcept.vcxproj index c86abf6..87e6513 100644 --- a/vs/test_x86_char8_noexcept.vcxproj +++ b/vs/test_x86_char8_noexcept.vcxproj @@ -92,6 +92,7 @@ + diff --git a/vs/test_x86_char8_strict.vcxproj b/vs/test_x86_char8_strict.vcxproj index 2eb906e..a366d91 100644 --- a/vs/test_x86_char8_strict.vcxproj +++ b/vs/test_x86_char8_strict.vcxproj @@ -90,6 +90,7 @@ + diff --git a/vs/test_x86_char8_strict_noexcept.vcxproj b/vs/test_x86_char8_strict_noexcept.vcxproj index fc2db06..5e9d77d 100644 --- a/vs/test_x86_char8_strict_noexcept.vcxproj +++ b/vs/test_x86_char8_strict_noexcept.vcxproj @@ -92,6 +92,7 @@ + diff --git a/vs/test_x86_char_noexcept.vcxproj b/vs/test_x86_char_noexcept.vcxproj index 5e730ec..8de3adc 100644 --- a/vs/test_x86_char_noexcept.vcxproj +++ b/vs/test_x86_char_noexcept.vcxproj @@ -92,6 +92,7 @@ + diff --git a/vs/test_x86_char_strict.vcxproj b/vs/test_x86_char_strict.vcxproj index d02ac25..ea868fd 100644 --- a/vs/test_x86_char_strict.vcxproj +++ b/vs/test_x86_char_strict.vcxproj @@ -90,6 +90,7 @@ + diff --git a/vs/test_x86_char_strict_noexcept.vcxproj b/vs/test_x86_char_strict_noexcept.vcxproj index 407d7d5..099308b 100644 --- a/vs/test_x86_char_strict_noexcept.vcxproj +++ b/vs/test_x86_char_strict_noexcept.vcxproj @@ -92,6 +92,7 @@ +