From 76e681da4d08d764889d9af5a6542dde7d08ea04 Mon Sep 17 00:00:00 2001 From: Mark Gillard Date: Sun, 7 Nov 2021 21:25:42 +0200 Subject: [PATCH] added `array::at()` and `table::at()` --- CHANGELOG.md | 55 ++-- README.md | 6 +- include/toml++/impl/array.h | 22 ++ include/toml++/impl/source_region.h | 14 +- include/toml++/impl/table.h | 379 ++++++++++++++-------------- include/toml++/impl/table.inl | 95 ++++--- tests/manipulating_arrays.cpp | 25 ++ tests/manipulating_tables.cpp | 17 +- toml.hpp | 280 +++++++++++--------- 9 files changed, 510 insertions(+), 383 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70b4b752..a316deb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,53 +17,56 @@ template: ## Unreleased This release will be a major version bump, so it's ABI breaks all around. Any API changes that might necessitate -code changes at callsites or in build systems are indicated with '⚠️'. +code changes at callsites or in build systems are indicated with ⚠️. #### Fixes: +- fixed `json_formatter` not formatting inf and nan incorrectly +- fixed `table` init-list constructor requiring double-brackets +- fixed `TOML_API` + extern templates causing linker errors in some circumstances - fixed an illegal table redefinition edge case (#112) (@python36) -- fixed incorrect source position in redefinition error messages - fixed documentation issues -- fixed some interfaces missing `TOML_API` -- fixed `toml::table` init-list constructor requiring double-brackets +- fixed incorrect `noexcept` specifications on many functions ⚠️ +- fixed incorrect source position in redefinition error messages - fixed missing `#include ` - fixed missing `#include ` -- fixed incorrect `noexcept` specifications on many functions ⚠️ -- fixed `TOML_API` + extern templates causing linker errors in some circumstances -- fixed inf and nan being formatted incorrectly by the `json_formatter` +- fixed missing `TOML_API` on interfaces - fixed parser not correctly round-tripping the format of binary and octal integers in some cases #### Additions: -- added support for Unicode 14.0 -- added formatter indentation flags (#120) (@W4RH4WK) -- added value flags to array + table insert methods (#44) (@levicki) -- added magic `value_flags` constant `preserve_source_value_flags` -- added clang's enum annotation attributes to all enums -- added `formatter_flags::quote_infinities_and_nans` -- added `TOML_ENABLE_FORMATTERS` option +- added `array::at()` and `table::at()` +- added `array::replace()` (#109) (#LebJe) - added `default_init_flags` param to `array::resize()` -- added `toml::yaml_formatter` -- added `operator->` to `toml::value` for class types +- added `formatter_flags::quote_infinities_and_nans` +- added `operator->` to `value` for class types - added `parse_benchmark` example -- added `array::replace()` (#109) (#LebJe) +- added `TOML_ENABLE_FORMATTERS` option +- added `yaml_formatter` +- added clang's enum annotation attributes to all enums +- added formatter indentation flags (#120) (@W4RH4WK) +- added magic `value_flags` constant `preserve_source_value_flags` +- added support for Unicode 14.0 +- added value flags to array + table insert methods (#44) (@levicki) #### Changes: -- moved all implementation headers to `/impl` +- `format_flags` is now backed by `uint64_t` (was previously `uint8_t`) ⚠️ +- `source_index` is now an alias for `uint32_t` unconditionally (was previously dependent on `TOML_LARGE_FILES`) ⚠️ +- `value_flags` is now backed by `uint16_t` (was previously `uint8_t`) ⚠️ +- applied clang-format to all the things 🎉️ - improved performance of parser's internal UTF-8 stream decoder +- made all overloaded operators 'hidden friends' where possible ⚠️ - made date/time constructors accept any integral types -- made all overloaded operators 'hidden friends' where possible -- renamed all implementation headers to `.h` and 'source' headers to `.inl` +- moved all implementation headers to `/impl` - renamed `default_formatter` to `toml_formatter` (`default_formatter` is now an alias) -- applied clang-format to all the things 🎉️ +- renamed all implementation headers to `.h` and 'source' headers to `.inl` - updated conformance tests - #### Removals and Deprecations: -- removed `TOML_LARGE_FILES` (it is now default - explicitly setting `TOML_PARSER` to `0` will invoke an `#error`) ⚠️ -- renamed `TOML_PARSER` option to `TOML_ENABLE_PARSER` (`TOML_PARSER` will continue to work but is deprecated) ⚠️ -- renamed `TOML_WINDOWS_COMPAT` to `TOML_ENABLE_WINDOWS_COMPAT` (`TOML_WINDOWS_COMPAT` will continue to work but is deprecated) ⚠️ -- renamed `TOML_UNRELEASED_FEATURES` to `TOML_ENABLE_UNRELEASED_FEATURES` (`TOML_UNRELEASED_FEATURES` will continue to work but is deprecated) ⚠️ +- removed `TOML_LARGE_FILES` (it is now default - explicitly setting `TOML_LARGE_FILES` to `0` will invoke an `#error`) ⚠️ - removed unnecessary template machinery (esp. where ostreams were involved) - removed unnecessary uses of `final` +- renamed `TOML_PARSER` option to `TOML_ENABLE_PARSER` (`TOML_PARSER` will continue to work but is deprecated) ⚠️ +- renamed `TOML_UNRELEASED_FEATURES` to `TOML_ENABLE_UNRELEASED_FEATURES` (`TOML_UNRELEASED_FEATURES` will continue to work but is deprecated) ⚠️ +- renamed `TOML_WINDOWS_COMPAT` to `TOML_ENABLE_WINDOWS_COMPAT` (`TOML_WINDOWS_COMPAT` will continue to work but is deprecated) ⚠️ #### Build system: - disabled 'install' path when being used as a meson subproject (#114) (@Tachi107) diff --git a/README.md b/README.md index 12caf7a1..4c323388 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ won't need to mess with these at all, but if you do, set them before including t | `TOML_CONFIG_HEADER` | string literal | Includes the given header file before the rest of the library. | undefined | | `TOML_ENABLE_FORMATTERS` | boolean | Enables the formatters. Set to `0` if you don't need them to improve compile times and binary size. | `1` | | `TOML_ENABLE_PARSER` | boolean | Enables the parser. Set to `0` if you don't need it to improve compile times and binary size. | `1` | -| `TOML_ENABLE_UNRELEASED_FEATURES` | boolean | Enables support for [unreleased TOML language features. | `0` | +| `TOML_ENABLE_UNRELEASED_FEATURES` | boolean | Enables support for [unreleased TOML language features]. | `0` | | `TOML_ENABLE_WINDOWS_COMPAT` | boolean | Enables support for transparent conversion between wide and narrow strings. | `1` on Windows | | `TOML_EXCEPTIONS` | boolean | Sets whether the library uses exceptions. | per compiler settings | | `TOML_HEADER_ONLY` | boolean | Disable this to explicitly control where toml++'s implementation is compiled (e.g. as part of a library).| `1` | @@ -167,7 +167,7 @@ support for a number of unreleased features from the [TOML master] and some sane The library advertises the most recent numbered language version it fully supports via the preprocessor defines `TOML_LANG_MAJOR`, `TOML_LANG_MINOR` and `TOML_LANG_PATCH`. -### 🔸️ **Unreleased language features:** +### **Unreleased language features:** - [#516]: Allow newlines and trailing commas in inline tables - [#562]: Allow hex floating-point values - [#644]: Support `+` in key names @@ -238,7 +238,7 @@ though you're welcome to reach out via other means. In order of likely response [API documentation]: https://marzer.github.io/tomlplusplus/ [homepage]: https://marzer.github.io/tomlplusplus/ -[unreleased TOML language features]: #-unreleased-language-features +[unreleased TOML language features]: #unreleased-language-features [most recently-released version]: https://github.com/toml-lang/toml/releases [numbered version]: https://github.com/toml-lang/toml/releases [char8_t]: https://en.cppreference.com/w/cpp/keyword/char8_t diff --git a/include/toml++/impl/array.h b/include/toml++/impl/array.h index de097dc9..07de3c60 100644 --- a/include/toml++/impl/array.h +++ b/include/toml++/impl/array.h @@ -646,6 +646,28 @@ TOML_NAMESPACE_START return *elems_[index]; } +#if TOML_COMPILER_EXCEPTIONS + + /// \brief Gets a reference to the element at a specific index, throwing `std::out_of_range` if none existed. + /// + /// \availability This function is only available if you compile with exceptions enabled. + TOML_NODISCARD + node& at(size_t index) + { + return *elems_.at(index); + } + + /// \brief Gets a reference to the element at a specific index, throwing `std::out_of_range` if none existed. + /// + /// \availability This function is only available if you compile with exceptions enabled. + TOML_NODISCARD + const node& at(size_t index) const + { + return *elems_.at(index); + } + +#endif // TOML_COMPILER_EXCEPTIONS + /// \brief Returns a reference to the first element in the array. TOML_NODISCARD node& front() noexcept diff --git a/include/toml++/impl/source_region.h b/include/toml++/impl/source_region.h index 0c25e5d5..d0ea0f63 100644 --- a/include/toml++/impl/source_region.h +++ b/include/toml++/impl/source_region.h @@ -25,7 +25,6 @@ TOML_NAMESPACE_START /// std::cout << "The node 'description' was defined at "sv /// << table.get("description")->source().begin() /// << "\n"; - /// /// \ecpp /// /// \out @@ -33,8 +32,8 @@ TOML_NAMESPACE_START /// \eout /// /// \remarks toml++'s parser is unicode-aware insofar as it knows how to handle - /// various non-conventional whitespace and newline characters, but it doesn't give - /// much thought to combining marks, grapheme clusters vs. characters, et cetera. + /// non-ASCII whitespace and newline characters, but it doesn't give much thought + /// to combining marks, grapheme clusters vs. characters, et cetera. /// If a TOML document contains lots of codepoints outside of the ASCII range /// you may find that your source_positions don't match those given by a text editor /// (typically the line numbers will be accurate but column numbers will be too high). @@ -93,7 +92,6 @@ TOML_NAMESPACE_START /// std::cout << "The value for 'bar' was found on "sv /// << tbl.get("bar")->source().begin() /// << "\n"; - /// /// \ecpp /// /// \out @@ -121,7 +119,6 @@ TOML_NAMESPACE_START /// std::cout << "end: "sv << server->source().end << "\n"; /// std::cout << "path: "sv << *server->source().path << "\n"; /// } - /// /// \ecpp /// /// \out @@ -131,8 +128,8 @@ TOML_NAMESPACE_START /// \eout /// /// \remarks toml++'s parser is unicode-aware insofar as it knows how to handle - /// various non-conventional whitespace and newline characters, but it doesn't give - /// much thought to combining marks, grapheme clusters vs. characters, et cetera. + /// non-ASCII whitespace and newline characters, but it doesn't give much thought + /// to combining marks, grapheme clusters vs. characters, et cetera. /// If a TOML document contains lots of codepoints outside of the ASCII range /// you may find that your source_positions don't match those given by a text editor /// (typically the line numbers will be accurate but column numbers will be too high). @@ -159,7 +156,7 @@ TOML_NAMESPACE_START /// /// \remarks This will return an empty optional if no path was provided to toml::parse(). TOML_NODISCARD - optional wide_path() const noexcept + optional wide_path() const { if (!path || path->empty()) return {}; @@ -176,7 +173,6 @@ TOML_NAMESPACE_START /// std::cout << "The value for 'bar' was found on "sv /// << tbl.get("bar")->source() /// << "\n"; - /// /// \ecpp /// /// \out diff --git a/include/toml++/impl/table.h b/include/toml++/impl/table.h index eaad2e4a..c00c4bd7 100644 --- a/include/toml++/impl/table.h +++ b/include/toml++/impl/table.h @@ -235,47 +235,6 @@ TOML_NAMESPACE_START TOML_API table(const impl::table_init_pair*, const impl::table_init_pair*); - template - TOML_NODISCARD - static auto do_get(Map& vals, const Key& key) noexcept(!impl::is_wide_string) - -> std::conditional_t, const node*, node*> - { - static_assert(!impl::is_wide_string || TOML_ENABLE_WINDOWS_COMPAT, - "Retrieval using wide-character keys is only supported on Windows with " - "TOML_ENABLE_WINDOWS_COMPAT enabled."); - - if constexpr (impl::is_wide_string) - { -#if TOML_ENABLE_WINDOWS_COMPAT - return do_get(vals, impl::narrow(key)); -#else - static_assert(impl::dependent_false, "Evaluated unreachable branch!"); -#endif - } - else - { - if (auto it = vals.find(key); it != vals.end()) - return { it->second.get() }; - return {}; - } - } - - template - TOML_NODISCARD - static auto do_get_as(Map& vals, const Key& key) noexcept - { - const auto node = do_get(vals, key); - return node ? node->template as() : nullptr; - } - - template - TOML_NODISCARD - TOML_ALWAYS_INLINE - static bool do_contains(Map& vals, const Key& key) noexcept - { - return do_get(vals, key) != nullptr; - } - /// \endcond public: @@ -608,7 +567,7 @@ TOML_NAMESPACE_START /// /// \remarks Runtime-constructed tables (i.e. those not created during /// parsing) are not inline by default. - TOML_NODISCARD + TOML_PURE_INLINE_GETTER bool is_inline() const noexcept { return inline_; @@ -660,84 +619,190 @@ TOML_NAMESPACE_START /// @} - /// \name Node views + /// \name Value retrieval /// @{ - /// \brief Gets a node_view for the selected key-value pair. + /// \brief Gets the node at a specific key. /// - /// \param key The key used for the lookup. + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "a", 42, }, + /// { "b", "is the meaning of life, apparently." } + /// }; + /// std::cout << R"(node ["a"] exists: )"sv << !!arr.get("a") << "\n"; + /// std::cout << R"(node ["b"] exists: )"sv << !!arr.get("b") << "\n"; + /// std::cout << R"(node ["c"] exists: )"sv << !!arr.get("c") << "\n"; + /// if (auto val = arr.get("a")) + /// std::cout << R"(node ["a"] was an )"sv << val->type() << "\n"; /// - /// \returns A view of the value at the given key if one existed, or an empty node view. + /// \ecpp /// - /// \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. + /// \out + /// node ["a"] exists: true + /// node ["b"] exists: true + /// node ["c"] exists: false + /// node ["a"] was an integer + /// \eout /// - /// \see toml::node_view - TOML_NODISCARD - node_view operator[](std::string_view key) noexcept + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key, or nullptr. + TOML_PURE_GETTER + TOML_API + node* get(std::string_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. + TOML_PURE_INLINE_GETTER + const node* get(std::string_view key) const noexcept { - return node_view{ this->get(key) }; + return const_cast(*this).get(key); } - /// \brief Gets a node_view for the selected key-value pair (const overload). +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets the node at a specific key. /// - /// \param key The key used for the lookup. + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. /// - /// \returns A view of the value at the given key if one existed, or an empty node view. + /// \param key The node's key. /// - /// \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. + /// \returns A pointer to the node at the specified key, or nullptr. + TOML_NODISCARD + TOML_API + node* get(std::wstring_view key); + + /// \brief Gets the node at a specific key (const overload). /// - /// \see toml::node_view + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key, or nullptr. TOML_NODISCARD - node_view operator[](std::string_view key) const noexcept + const node* get(std::wstring_view key) const { - return node_view{ this->get(key) }; + return const_cast(*this).get(key); } -#if TOML_ENABLE_WINDOWS_COMPAT +#endif // TOML_ENABLE_WINDOWS_COMPAT - /// \brief Gets a node_view for the selected key-value pair. + /// \brief Gets the node at a specific key if it is a particular type. /// - /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "a", 42, }, + /// { "b", "is the meaning of life, apparently." } + /// }; + /// if (auto val = arr.get_as("a")) + /// std::cout << R"(node ["a"] was an integer with value )"sv << **val << "\n"; /// - /// \param key The key used for the lookup. + /// \ecpp /// - /// \returns A view of the value at the given key if one existed, or an empty node view. + /// \out + /// node ["a"] was an integer with value 42 + /// \eout /// - /// \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. + /// \tparam ValueType One of the TOML node or value types. + /// \param key The node's key. /// - /// \see toml::node_view + /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. + template + TOML_PURE_GETTER + impl::wrap_node* get_as(std::string_view key) noexcept + { + const auto n = this->get(key); + return n ? n->template as() : nullptr; + } + + /// \brief Gets the node at a specific key if it is a particular type (const overload). + /// + /// \tparam ValueType One of the TOML node or value types. + /// \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. + template + TOML_PURE_GETTER + const impl::wrap_node* get_as(std::string_view key) const noexcept + { + return const_cast(*this).template get_as(key); + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets the node at a specific key if it is a particular type. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \tparam ValueType One of the TOML node or value types. + /// \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. + template TOML_NODISCARD - node_view operator[](std::wstring_view key) noexcept + impl::wrap_node* get_as(std::wstring_view key) { - return node_view{ this->get(key) }; + return get_as(impl::narrow(key)); } - /// \brief Gets a node_view for the selected key-value pair (const overload). + /// \brief Gets the node at a specific key if it is a particular type (const overload). /// /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. /// - /// \param key The key used for the lookup. + /// \tparam ValueType One of the TOML node or value types. + /// \param key The node's key. /// - /// \returns A view of the value at the given key if one existed, or an empty node view. + /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. + template + TOML_NODISCARD + const impl::wrap_node* get_as(std::wstring_view key) const + { + return const_cast(*this).template get_as(key); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + +#if TOML_COMPILER_EXCEPTIONS + + /// \brief Gets a reference to the element at a specific key, throwing `std::out_of_range` if none existed. /// - /// \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. + /// \availability This function is only available if you compile with exceptions enabled. + TOML_NODISCARD + TOML_API + node& at(std::string_view key); + + /// \brief Gets a reference to the element at a specific key, throwing `std::out_of_range` if none existed. /// - /// \see toml::node_view + /// \availability This function is only available if you compile with exceptions enabled. TOML_NODISCARD - node_view operator[](std::wstring_view key) const noexcept + const node& at(std::string_view key) const + { + return const_cast(*this).at(key); + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets a reference to the element at a specific key, throwing `std::out_of_range` if none existed. + /// + /// \availability This function is only available if you compile with exceptions and #TOML_ENABLE_WINDOWS_COMPAT enabled. + TOML_NODISCARD + TOML_API + node& at(std::wstring_view key); + + /// \brief Gets a reference to the element at a specific key, throwing `std::out_of_range` if none existed. + /// + /// \availability This function is only available if you compile with exceptions and #TOML_ENABLE_WINDOWS_COMPAT enabled. + TOML_NODISCARD + const node& at(std::wstring_view key) const { - return node_view{ this->get(key) }; + return const_cast(*this).at(key); } #endif // TOML_ENABLE_WINDOWS_COMPAT +#endif // TOML_COMPILER_EXCEPTIONS /// @} @@ -1284,7 +1349,7 @@ TOML_NAMESPACE_START TOML_NODISCARD bool contains(std::string_view key) const noexcept { - return do_contains(map_, key); + return get(key) != nullptr; } #if TOML_ENABLE_WINDOWS_COMPAT @@ -1328,151 +1393,81 @@ TOML_NAMESPACE_START /// @} - /// \name Value retrieval + /// \name Node views /// @{ - /// \brief Gets the node at a specific key. - /// - /// \detail \cpp - /// auto tbl = toml::table{ - /// { "a", 42, }, - /// { "b", "is the meaning of life, apparently." } - /// }; - /// std::cout << R"(node ["a"] exists: )"sv << !!arr.get("a") << "\n"; - /// std::cout << R"(node ["b"] exists: )"sv << !!arr.get("b") << "\n"; - /// std::cout << R"(node ["c"] exists: )"sv << !!arr.get("c") << "\n"; - /// if (auto val = arr.get("a")) - /// std::cout << R"(node ["a"] was an )"sv << val->type() << "\n"; - /// - /// \ecpp - /// - /// \out - /// node ["a"] exists: true - /// node ["b"] exists: true - /// node ["c"] exists: false - /// node ["a"] was an integer - /// \eout + /// \brief Gets a node_view for the selected key-value pair. /// - /// \param key The node's key. + /// \param key The key used for the lookup. /// - /// \returns A pointer to the node at the specified key, or nullptr. - TOML_NODISCARD - node* get(std::string_view key) noexcept - { - return do_get(map_, key); - } - - /// \brief Gets the node at a specific key (const overload). + /// \returns A view of the value at the given key if one existed, or an empty node view. /// - /// \param key The node's key. + /// \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. /// - /// \returns A pointer to the node at the specified key, or nullptr. + /// \see toml::node_view TOML_NODISCARD - const node* get(std::string_view key) const noexcept + node_view operator[](std::string_view key) noexcept { - return do_get(map_, key); + return node_view{ get(key) }; } -#if TOML_ENABLE_WINDOWS_COMPAT - - /// \brief Gets the node at a specific key. - /// - /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. - /// - /// \param key The node's key. + /// \brief Gets a node_view for the selected key-value pair (const overload). /// - /// \returns A pointer to the node at the specified key, or nullptr. - TOML_NODISCARD - node* get(std::wstring_view key) - { - return get(impl::narrow(key)); - } - - /// \brief Gets the node at a specific key (const overload). + /// \param key The key used for the lookup. /// - /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// \returns A view of the value at the given key if one existed, or an empty node view. /// - /// \param key The node's key. + /// \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. /// - /// \returns A pointer to the node at the specified key, or nullptr. + /// \see toml::node_view TOML_NODISCARD - const node* get(std::wstring_view key) const + node_view operator[](std::string_view key) const noexcept { - return get(impl::narrow(key)); + return node_view{ get(key) }; } -#endif // TOML_ENABLE_WINDOWS_COMPAT +#if TOML_ENABLE_WINDOWS_COMPAT - /// \brief Gets the node at a specific key if it is a particular type. - /// - /// \detail \cpp - /// auto tbl = toml::table{ - /// { "a", 42, }, - /// { "b", "is the meaning of life, apparently." } - /// }; - /// if (auto val = arr.get_as("a")) - /// std::cout << R"(node ["a"] was an integer with value )"sv << **val << "\n"; - /// - /// \ecpp + /// \brief Gets a node_view for the selected key-value pair. /// - /// \out - /// node ["a"] was an integer with value 42 - /// \eout + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. /// - /// \tparam ValueType One of the TOML node or value types. - /// \param key The node's key. + /// \param key The key used for the lookup. /// - /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. - template - TOML_NODISCARD - impl::wrap_node* get_as(std::string_view key) noexcept - { - return do_get_as(map_, key); - } - - /// \brief Gets the node at a specific key if it is a particular type (const overload). + /// \returns A view of the value at the given key if one existed, or an empty node view. /// - /// \tparam ValueType One of the TOML node or value types. - /// \param key The node's key. + /// \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. /// - /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. - template + /// \see toml::node_view TOML_NODISCARD - const impl::wrap_node* get_as(std::string_view key) const noexcept + node_view operator[](std::wstring_view key) noexcept { - return do_get_as(map_, key); + return node_view{ get(key) }; } -#if TOML_ENABLE_WINDOWS_COMPAT - - /// \brief Gets the node at a specific key if it is a particular type. + /// \brief Gets a node_view for the selected key-value pair (const overload). /// /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. /// - /// \tparam ValueType One of the TOML node or value types. - /// \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. - template - TOML_NODISCARD - impl::wrap_node* get_as(std::wstring_view key) - { - return get_as(impl::narrow(key)); - } - - /// \brief Gets the node at a specific key if it is a particular type (const overload). + /// \param key The key used for the lookup. /// - /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// \returns A view of the value at the given key if one existed, or an empty node view. /// - /// \tparam ValueType One of the TOML node or value types. - /// \param key The node's key. + /// \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. /// - /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. - template + /// \see toml::node_view TOML_NODISCARD - const impl::wrap_node* get_as(std::wstring_view key) const + node_view operator[](std::wstring_view key) const noexcept { - return get_as(impl::narrow(key)); + return node_view{ get(key) }; } #endif // TOML_ENABLE_WINDOWS_COMPAT diff --git a/include/toml++/impl/table.inl b/include/toml++/impl/table.inl index 5b3a5fcd..6efbd016 100644 --- a/include/toml++/impl/table.inl +++ b/include/toml++/impl/table.inl @@ -15,33 +15,6 @@ #include "node_view.h" #include "header_start.h" -TOML_ANON_NAMESPACE_START -{ - template - TOML_INTERNAL_LINKAGE - bool table_is_homogeneous(T & map, node_type ntype, U & first_nonmatch) noexcept - { - if (map.empty()) - { - first_nonmatch = {}; - return false; - } - if (ntype == node_type::none) - ntype = map.cbegin()->second->type(); - for (const auto& [k, v] : map) - { - (void)k; - if (v->type() != ntype) - { - first_nonmatch = v.get(); - return false; - } - } - return true; - } -} -TOML_ANON_NAMESPACE_END; - TOML_NAMESPACE_START { TOML_EXTERNAL_LINKAGE @@ -132,15 +105,79 @@ TOML_NAMESPACE_START TOML_EXTERNAL_LINKAGE bool table::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept { - return TOML_ANON_NAMESPACE::table_is_homogeneous(map_, ntype, first_nonmatch); + if (map_.empty()) + { + first_nonmatch = {}; + return false; + } + if (ntype == node_type::none) + ntype = map_.cbegin()->second->type(); + for (const auto& [k, v] : map_) + { + (void)k; + if (v->type() != ntype) + { + first_nonmatch = v.get(); + return false; + } + } + return true; } TOML_EXTERNAL_LINKAGE bool table::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept { - return TOML_ANON_NAMESPACE::table_is_homogeneous(map_, ntype, first_nonmatch); + node* fnm = nullptr; + const auto result = const_cast(*this).is_homogeneous(ntype, fnm); + first_nonmatch = fnm; + return result; + } + + TOML_EXTERNAL_LINKAGE + node* table::get(std::string_view key) noexcept + { + if (auto it = map_.find(key); it != map_.end()) + return it->second.get(); + return nullptr; + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + TOML_EXTERNAL_LINKAGE + node* table::get(std::wstring_view key) + { + return get(impl::narrow(key)); } +#endif + +#if TOML_COMPILER_EXCEPTIONS + + TOML_EXTERNAL_LINKAGE + node& table::at(std::string_view key) + { + auto n = get(key); + if (!n) + { + auto err = "key '"s; + err.append(key); + err.append("' not found in table"sv); + throw std::out_of_range{ err }; + } + return *n; + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + TOML_EXTERNAL_LINKAGE + node& table::at(std::wstring_view key) + { + return at(impl::narrow(key)); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT +#endif // TOML_COMPILER_EXCEPTIONS + TOML_EXTERNAL_LINKAGE bool table::equal(const table& lhs, const table& rhs) noexcept { diff --git a/tests/manipulating_arrays.cpp b/tests/manipulating_arrays.cpp index 72e33e4e..0ad5fa39 100644 --- a/tests/manipulating_arrays.cpp +++ b/tests/manipulating_arrays.cpp @@ -178,9 +178,28 @@ TEST_CASE("arrays - construction") CHECK(arr.cbegin() != arr.cend()); REQUIRE(arr.get_as(0u)); CHECK(*arr.get_as(0u) == 42); + CHECK(arr.get(0u) == &arr[0u]); CHECK(arr.is_homogeneous()); CHECK(arr.is_homogeneous()); CHECK(!arr.is_homogeneous()); +#if TOML_COMPILER_EXCEPTIONS + CHECK(arr.get(0u) == &arr.at(0u)); +#endif + + const array& carr = arr; + CHECK(carr.size() == 1u); + CHECK(!carr.empty()); + CHECK(carr.begin() != carr.end()); + CHECK(carr.cbegin() != carr.cend()); + REQUIRE(carr.get_as(0u)); + CHECK(*carr.get_as(0u) == 42); + CHECK(carr.get(0u) == &carr[0u]); + CHECK(carr.is_homogeneous()); + CHECK(carr.is_homogeneous()); + CHECK(!carr.is_homogeneous()); +#if TOML_COMPILER_EXCEPTIONS + CHECK(carr.get(0u) == &carr.at(0u)); +#endif } { @@ -189,14 +208,20 @@ TEST_CASE("arrays - construction") CHECK(!arr.empty()); REQUIRE(arr.get_as(0u)); CHECK(*arr.get_as(0u) == 42); + CHECK(arr.get(0u) == &arr[0u]); REQUIRE(arr.get_as(1u)); CHECK(*arr.get_as(1u) == "test"sv); + CHECK(arr.get(1u) == &arr[1u]); REQUIRE(arr.get_as(2u)); CHECK(*arr.get_as(2u) == 10.0); REQUIRE(arr.get_as(3u)); REQUIRE(arr.get_as(4u)); CHECK(*arr.get_as(4u) == 3); CHECK(!arr.is_homogeneous()); +#if TOML_COMPILER_EXCEPTIONS + CHECK(arr.get(0u) == &arr.at(0u)); + CHECK(arr.get(1u) == &arr.at(1u)); +#endif } #if TOML_ENABLE_WINDOWS_COMPAT diff --git a/tests/manipulating_tables.cpp b/tests/manipulating_tables.cpp index b2439561..6a41f84a 100644 --- a/tests/manipulating_tables.cpp +++ b/tests/manipulating_tables.cpp @@ -19,7 +19,21 @@ TEST_CASE("tables - moving") CHECK(tbl["test"].as()->size() == 1u); CHECK(tbl["test"].as
()->source().begin == source_position{ 1, 8 }); CHECK(tbl["test"].as
()->source().end == source_position{ 1, 24 }); - CHECK(tbl["test"]["val1"] == "foo"); + CHECK(tbl["test"].node() == tbl.get("test"sv)); +#if TOML_COMPILER_EXCEPTIONS + CHECK(tbl["test"].node() == &tbl.at("test"sv)); +#endif + + // sanity-check initial state of a freshly-parsed table (const) + const table& ctbl = tbl; + REQUIRE(ctbl["test"].as
()); + CHECK(ctbl["test"].as
()->size() == 1u); + CHECK(ctbl["test"].as
()->source().begin == source_position{ 1, 8 }); + CHECK(ctbl["test"].as
()->source().end == source_position{ 1, 24 }); + CHECK(ctbl["test"].node() == ctbl.get("test"sv)); +#if TOML_COMPILER_EXCEPTIONS + CHECK(ctbl["test"].node() == &ctbl.at("test"sv)); +#endif // sanity check the virtual type checks CHECK(tbl.type() == node_type::table); @@ -48,7 +62,6 @@ TEST_CASE("tables - moving") CHECK(!tbl.as_date_time()); // sanity check the virtual type casts (const) - const auto& ctbl = std::as_const(tbl); CHECK(ctbl.as_table() == &ctbl); CHECK(!ctbl.as_array()); CHECK(!ctbl.as_string()); diff --git a/toml.hpp b/toml.hpp index bb9c3996..4b179ab9 100644 --- a/toml.hpp +++ b/toml.hpp @@ -2066,7 +2066,7 @@ TOML_NAMESPACE_START #if TOML_ENABLE_WINDOWS_COMPAT TOML_NODISCARD - optional wide_path() const noexcept + optional wide_path() const { if (!path || path->empty()) return {}; @@ -5190,6 +5190,22 @@ TOML_NAMESPACE_START return *elems_[index]; } +#if TOML_COMPILER_EXCEPTIONS + + TOML_NODISCARD + node& at(size_t index) + { + return *elems_.at(index); + } + + TOML_NODISCARD + const node& at(size_t index) const + { + return *elems_.at(index); + } + +#endif // TOML_COMPILER_EXCEPTIONS + TOML_NODISCARD node& front() noexcept { @@ -5800,47 +5816,6 @@ TOML_NAMESPACE_START TOML_API table(const impl::table_init_pair*, const impl::table_init_pair*); - template - TOML_NODISCARD - static auto do_get(Map& vals, const Key& key) noexcept(!impl::is_wide_string) - -> std::conditional_t, const node*, node*> - { - static_assert(!impl::is_wide_string || TOML_ENABLE_WINDOWS_COMPAT, - "Retrieval using wide-character keys is only supported on Windows with " - "TOML_ENABLE_WINDOWS_COMPAT enabled."); - - if constexpr (impl::is_wide_string) - { -#if TOML_ENABLE_WINDOWS_COMPAT - return do_get(vals, impl::narrow(key)); -#else - static_assert(impl::dependent_false, "Evaluated unreachable branch!"); -#endif - } - else - { - if (auto it = vals.find(key); it != vals.end()) - return { it->second.get() }; - return {}; - } - } - - template - TOML_NODISCARD - static auto do_get_as(Map& vals, const Key& key) noexcept - { - const auto node = do_get(vals, key); - return node ? node->template as() : nullptr; - } - - template - TOML_NODISCARD - TOML_ALWAYS_INLINE - static bool do_contains(Map& vals, const Key& key) noexcept - { - return do_get(vals, key) != nullptr; - } - public: using iterator = table_iterator; @@ -6097,7 +6072,7 @@ TOML_NAMESPACE_START return nullptr; } - TOML_NODISCARD + TOML_PURE_INLINE_GETTER bool is_inline() const noexcept { return inline_; @@ -6108,34 +6083,90 @@ TOML_NAMESPACE_START inline_ = val; } - TOML_NODISCARD - node_view operator[](std::string_view key) noexcept + TOML_PURE_GETTER + TOML_API + node* get(std::string_view key) noexcept; + + TOML_PURE_INLINE_GETTER + const node* get(std::string_view key) const noexcept { - return node_view{ this->get(key) }; + return const_cast(*this).get(key); } +#if TOML_ENABLE_WINDOWS_COMPAT + TOML_NODISCARD - node_view operator[](std::string_view key) const noexcept + TOML_API + node* get(std::wstring_view key); + + TOML_NODISCARD + const node* get(std::wstring_view key) const { - return node_view{ this->get(key) }; + return const_cast(*this).get(key); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + + template + TOML_PURE_GETTER + impl::wrap_node* get_as(std::string_view key) noexcept + { + const auto n = this->get(key); + return n ? n->template as() : nullptr; + } + + template + TOML_PURE_GETTER + const impl::wrap_node* get_as(std::string_view key) const noexcept + { + return const_cast(*this).template get_as(key); } #if TOML_ENABLE_WINDOWS_COMPAT + template TOML_NODISCARD - node_view operator[](std::wstring_view key) noexcept + impl::wrap_node* get_as(std::wstring_view key) { - return node_view{ this->get(key) }; + return get_as(impl::narrow(key)); } + template TOML_NODISCARD - node_view operator[](std::wstring_view key) const noexcept + const impl::wrap_node* get_as(std::wstring_view key) const { - return node_view{ this->get(key) }; + return const_cast(*this).template get_as(key); } #endif // TOML_ENABLE_WINDOWS_COMPAT +#if TOML_COMPILER_EXCEPTIONS + + TOML_NODISCARD + TOML_API + node& at(std::string_view key); + + TOML_NODISCARD + const node& at(std::string_view key) const + { + return const_cast(*this).at(key); + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + TOML_NODISCARD + TOML_API + node& at(std::wstring_view key); + + TOML_NODISCARD + const node& at(std::wstring_view key) const + { + return const_cast(*this).at(key); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT +#endif // TOML_COMPILER_EXCEPTIONS + TOML_NODISCARD iterator begin() noexcept { @@ -6369,7 +6400,7 @@ TOML_NAMESPACE_START TOML_NODISCARD bool contains(std::string_view key) const noexcept { - return do_contains(map_, key); + return get(key) != nullptr; } #if TOML_ENABLE_WINDOWS_COMPAT @@ -6395,61 +6426,29 @@ TOML_NAMESPACE_START #endif // TOML_ENABLE_WINDOWS_COMPAT TOML_NODISCARD - node* get(std::string_view key) noexcept - { - return do_get(map_, key); - } - - TOML_NODISCARD - const node* get(std::string_view key) const noexcept - { - return do_get(map_, key); - } - -#if TOML_ENABLE_WINDOWS_COMPAT - - TOML_NODISCARD - node* get(std::wstring_view key) - { - return get(impl::narrow(key)); - } - - TOML_NODISCARD - const node* get(std::wstring_view key) const - { - return get(impl::narrow(key)); - } - -#endif // TOML_ENABLE_WINDOWS_COMPAT - - template - TOML_NODISCARD - impl::wrap_node* get_as(std::string_view key) noexcept + node_view operator[](std::string_view key) noexcept { - return do_get_as(map_, key); + return node_view{ get(key) }; } - template TOML_NODISCARD - const impl::wrap_node* get_as(std::string_view key) const noexcept + node_view operator[](std::string_view key) const noexcept { - return do_get_as(map_, key); + return node_view{ get(key) }; } #if TOML_ENABLE_WINDOWS_COMPAT - template TOML_NODISCARD - impl::wrap_node* get_as(std::wstring_view key) + node_view operator[](std::wstring_view key) noexcept { - return get_as(impl::narrow(key)); + return node_view{ get(key) }; } - template TOML_NODISCARD - const impl::wrap_node* get_as(std::wstring_view key) const + node_view operator[](std::wstring_view key) const noexcept { - return get_as(impl::narrow(key)); + return node_view{ get(key) }; } #endif // TOML_ENABLE_WINDOWS_COMPAT @@ -9459,33 +9458,6 @@ TOML_PUSH_WARNINGS; #undef max #endif -TOML_ANON_NAMESPACE_START -{ - template - TOML_INTERNAL_LINKAGE - bool table_is_homogeneous(T & map, node_type ntype, U & first_nonmatch) noexcept - { - if (map.empty()) - { - first_nonmatch = {}; - return false; - } - if (ntype == node_type::none) - ntype = map.cbegin()->second->type(); - for (const auto& [k, v] : map) - { - (void)k; - if (v->type() != ntype) - { - first_nonmatch = v.get(); - return false; - } - } - return true; - } -} -TOML_ANON_NAMESPACE_END; - TOML_NAMESPACE_START { TOML_EXTERNAL_LINKAGE @@ -9576,15 +9548,79 @@ TOML_NAMESPACE_START TOML_EXTERNAL_LINKAGE bool table::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept { - return TOML_ANON_NAMESPACE::table_is_homogeneous(map_, ntype, first_nonmatch); + if (map_.empty()) + { + first_nonmatch = {}; + return false; + } + if (ntype == node_type::none) + ntype = map_.cbegin()->second->type(); + for (const auto& [k, v] : map_) + { + (void)k; + if (v->type() != ntype) + { + first_nonmatch = v.get(); + return false; + } + } + return true; } TOML_EXTERNAL_LINKAGE bool table::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept { - return TOML_ANON_NAMESPACE::table_is_homogeneous(map_, ntype, first_nonmatch); + node* fnm = nullptr; + const auto result = const_cast(*this).is_homogeneous(ntype, fnm); + first_nonmatch = fnm; + return result; + } + + TOML_EXTERNAL_LINKAGE + node* table::get(std::string_view key) noexcept + { + if (auto it = map_.find(key); it != map_.end()) + return it->second.get(); + return nullptr; } +#if TOML_ENABLE_WINDOWS_COMPAT + + TOML_EXTERNAL_LINKAGE + node* table::get(std::wstring_view key) + { + return get(impl::narrow(key)); + } + +#endif + +#if TOML_COMPILER_EXCEPTIONS + + TOML_EXTERNAL_LINKAGE + node& table::at(std::string_view key) + { + auto n = get(key); + if (!n) + { + auto err = "key '"s; + err.append(key); + err.append("' not found in table"sv); + throw std::out_of_range{ err }; + } + return *n; + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + TOML_EXTERNAL_LINKAGE + node& table::at(std::wstring_view key) + { + return at(impl::narrow(key)); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT +#endif // TOML_COMPILER_EXCEPTIONS + TOML_EXTERNAL_LINKAGE bool table::equal(const table& lhs, const table& rhs) noexcept {