diff --git a/README.md b/README.md index 9ae0c49b..710f35fc 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ addition of unreleased features from the [TOML master] and some sane cherry-pick [TOML issues list] where the discussion strongly indicates inclusion in a near-future release. 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_REVISION`. +defines `TOML_LANG_MAJOR`, `TOML_LANG_MINOR` and `TOML_LANG_PATCH`. ### **🔸Unreleased TOML features:** - [#356]: Allow leading zeros in the exponent part of a float @@ -104,7 +104,6 @@ defines `TOML_LANG_MAJOR`, `TOML_LANG_MINOR` and `TOML_LANG_REVISION`. - [#562]: Allow hex floatingpoint values - [#567]: Clarify that control characters are not permitted in comments - [#571]: Allow raw tabs inside strings -- [#622]: Add short escaping alias `\s` for space (`\u0020`) - [#644]: Support `+` in key names - [#665]: Make arrays heterogeneous - [#671]: Local time of day format should support `09:30` as opposed to `09:30:00` @@ -167,7 +166,7 @@ cd ../build-clang && ninja && ninja test UTF-8 decoding is performed using a state machine based on Bjoern Hoehrmann's '[Flexible and Economical UTF-8 Decoder]', which is also subject to the terms of the MIT license - see [LICENSE-utf8-decoder]. -[API documentation]: https://marzer.github.io/tomlplusplus/namespacetoml.html +[API documentation]: https://marzer.github.io/tomlplusplus/ [unreleased TOML language features]: https://github.com/marzer/tomlplusplus#unreleased-features [numbered version]: https://github.com/toml-lang/toml/releases [char8_t]: https://en.cppreference.com/w/cpp/keyword/char8_t diff --git a/docs/tomlplusplus.css b/docs/tomlplusplus.css index 535053e5..f356340c 100644 --- a/docs/tomlplusplus.css +++ b/docs/tomlplusplus.css @@ -89,7 +89,8 @@ pre.m-code + pre { margin-top: -1.0rem; color: #bababa; /* is yououou */ - background-color: #282e36aa; + background-color: #383e46; + border-top: 2px solid #181e26; font-size: 0.8rem; } diff --git a/include/toml++/toml.h b/include/toml++/toml.h index cf112c7f..ea435f7d 100644 --- a/include/toml++/toml.h +++ b/include/toml++/toml.h @@ -52,24 +52,217 @@ #undef TOML_STRING_PREFIX #undef TOML_UNDEF_MACROS #undef TOML_DOXYGEN + #undef TOML_RELOPS_REORDERING + #undef TOML_ASYMMETRICAL_EQUALITY_OPS #endif /// \mainpage toml++ /// -/// This is the home of the API documentation for toml++, a [TOML](https://github.com/toml-lang/toml) parser for C++17 and later. -/// If you're looking for information about how to add toml++ to your project etc, see the -/// see [README](https://github.com/marzer/tomlplusplus/blob/master/README.md) on GitHub. -/// Otherwise, browse the docs using the links at the top of the page. You can search from anywhere by pressing the TAB key. +/// This is the home of toml++, a header-only [TOML](https://github.com/toml-lang/toml) parser and serializer for C++17 and later. +/// +/// \tableofcontents /// -/// Obviously this page is pretty sparse and could do with some more content. If you have concrete suggestions for what -/// should go here, please [let me know](https://github.com/marzer/tomlplusplus/issues)! +/////////////////////////////////////////////////////////////////////// /// +/// \section mainpage-features Features +/// - C++17 (plus some C++20 features where supported, e.g. char8_t strings) +/// - Proper UTF-8 handling (incl. BOM) +/// - Works with or without exceptions +/// - Doesn't require RTTI +/// - First-class support for serializing to JSON +/// - Fully [TOML v0.5.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md)-compliant +/// - Supports a number of 'unreleased' TOML features (optional; these can be disabled) +/// +/////////////////////////////////////////////////////////////////////// +/// +/// \section mainpage-adding-lib Adding toml++ to your project +/// Clone [the repository](https://github.com/marzer/tomlplusplus/) from GitHub. It's header-only so there's not much you have to do after that, +/// other than some very minor (optional) configuration. See the [README](https://github.com/marzer/tomlplusplus/blob/master/README.md) for more info. +/// +/////////////////////////////////////////////////////////////////////// +/// +/// \section mainpage-api-documentation API Documentation +/// You're looking at it! Browse the docs using the links at the top of the page. You can search from anywhere by pressing the TAB key. +/// +/// toml++ is still pretty hot off the presses so there's going to be some omissions, typos and general sparseness throughout the docs. +/// If you spot something or have a suggestion, please [let me know](https://github.com/marzer/tomlplusplus/issues)! +/// +/////////////////////////////////////////////////////////////////////// +/// +/// \section mainpage-example Basic examples +/// +/////////////////////////////////// +/// +/// \subsection mainpage-example-parsing-files Parsing TOML files +/// toml++ works whether you have exceptions enabled or not. For the most part the usage is the same, +/// the main difference being how parsing errors are reported to the caller. When exceptions are enabled +/// a toml::parse_error is thrown directly from the site of the error: /// \cpp /// #include +/// #include //required for parse_file() /// #include +/// using namespace std::string_view_literals; /// /// int main() /// { +/// toml::table tbl; +/// try +/// { +/// tbl = toml::parse_file("configuration.toml"); +/// } +/// catch (const toml::parse_error& err) +/// { +/// std::cerr +/// << "Error parsing file '"sv << *err.source().path +/// << "':\n"sv << err.description() +/// << "\n ("sv << err.source().begin << ")"sv +/// << std::endl; +/// return 1; +/// } +/// +/// do_stuff_with_your_config(tbl); +/// return 0; +/// } +/// +/// \ecpp +/// +/// When exceptions are disabled parsing methods return a toml::parse_error and it is up to the caller +/// to check if parsing has been successful by examining the return value: +/// \cpp +/// #include +/// #include //required for parse_file() +/// #include +/// using namespace std::string_view_literals; +/// +/// int main() +/// { +/// toml::parse_result tbl = toml::parse_file("configuration.toml"); +/// if (!tbl) +/// { +/// std::cerr +/// << "Error parsing file '"sv << *tbl.error().source().path +/// << "':\n"sv << tbl.error().description() +/// << "\n ("sv << tbl.error().source().begin << ")"sv +/// << std::endl; +/// return 1; +/// } +/// +/// do_stuff_with_your_config(tbl); //toml::parse_result is convertible to toml::table +/// return 0; +/// } +/// \ecpp +/// \see toml::parse_file() +/// +/////////////////////////////////// +/// +/// \subsection mainpage-example-parsing-strings Parsing TOML directly from strings +/// +/// \cpp +/// #include +/// #include +/// using namespace std::string_view_literals; +/// +/// int main() +/// { +/// // parse error handling omitted for brevity. +/// static constexpr auto source = R"( +/// [library] +/// name = "toml++" +/// version = "0.1.0" +/// authors = ["Mark Gillard "] +/// +/// [dependencies] +/// cpp = 17 +/// )"sv; +/// auto tbl = toml::parse(source); +/// std::cout << tbl << std::endl; +/// return 0; +/// } +/// \ecpp +/// +/// \out +/// [dependencies] +/// cpp = 17 +/// +/// [library] +/// authors = ["Mark Gillard "] +/// name = "toml++" +/// version = "0.1.0" +/// \eout +/// \see toml::parse() +/// +/////////////////////////////////// +/// +/// \subsection mainpage-example-manipulations Traversing and manipulating data +/// +/// \cpp +/// #include +/// #include +/// using namespace std::string_view_literals; +/// +/// int main() +/// { +/// static constexpr auto source = R"( +/// numbers = [ 1, 2, 3, "four", 5.0 ] +/// vegetables = [ "tomato", "onion", "mushroom", "lettuce" ] +/// minerals = [ "quartz", "iron", "copper", "diamond" ] +/// +/// [animals] +/// cats = [ "tiger", "lion", "puma" ] +/// birds = [ "macaw", "pigeon", "canary" ] +/// fish = [ "salmon", "trout", "carp" ] +/// +/// )"sv; +/// auto tbl = toml::parse(source); +/// +/// auto numbers = tbl["numbers"]; +/// std::cout << "table has 'numbers': "sv << !!numbers << std::endl; +/// if (numbers) +/// { +/// std::cout << "'numbers' is a: "sv << numbers.type() << std::endl; +/// std::cout << "'numbers': "sv << numbers << std::endl; +/// for (auto& node : *numbers.as_array()) +/// { +/// node.visit([=](auto&& n) noexcept +/// { +/// if constexpr (toml::is_number) +/// (*n)++; +/// else if constexpr (toml::is_string) +/// n = "five"sv; +/// }); +/// } +/// numbers.as_array()->push_back(7); +/// numbers.as_array()->emplace_back(8, 9); +/// std::cout << "'numbers': "sv << numbers << std::endl; +/// } +/// +/// std::cout << "'cats': "sv << tbl["animals"]["cats"] << std::endl; +/// std::cout << "'dinosaurs': "sv << tbl["animals"]["dinosaurs"] << std::endl; //no dinosaurs :( +/// +/// return 0; +/// } +/// \ecpp +/// +/// \out +/// table has 'numbers': true +/// 'numbers' is an: array +/// 'numbers': [1, 2, 3, "four", 5.0] +/// 'numbers': [2, 3, 4, "five", 6.0, 7, [8, 9]] +/// 'cats': ["tiger", "lion", "puma"] +/// 'dinosaurs': +/// \eout +/// +/// \see toml::node, toml::node_view, toml::array, toml::table +/// +/////////////////////////////////// +/// +/// \subsection mainpage-example-serialization Serializing as TOML and JSON +/// \cpp +/// #include +/// #include +/// +/// int main() +/// { /// auto tbl = toml::table{{ /// { "lib", "toml++" }, /// { "cpp", toml::array{ 17, 20, "and beyond" } }, @@ -82,21 +275,62 @@ /// }} /// }, /// }}; +/// +/// std::cout << "###### TOML ######"sv << std::endl; +/// std::cout << tbl << std::endl << std::endl; /// -/// std::cout << tbl << std::endl; +/// std::cout << "###### JSON ######"sv << std::endl; +/// std::cout << toml::json_formatter{ tbl } << std::endl; /// return 0; /// } /// \ecpp -/// +/// /// \out -/// cpp = [ 17, 20, "and beyond" ] +/// ###### TOML ###### +/// cpp = [17, 20, "and beyond"] /// lib = "toml++" /// repo = "https://github.com/marzer/tomlplusplus/" -/// toml = [ "0.5.0", "and beyond" ] +/// toml = ["0.5.0", "and beyond"] /// /// [author] /// github = "https://github.com/marzer" /// name = "Mark Gillard" /// twitter = "https://twitter.com/marzer8789" +/// +/// ###### JSON ###### +/// { +/// "author" : { +/// "github" : "https://github.com/marzer", +/// "name" : "Mark Gillard", +/// "twitter" : "https://twitter.com/marzer8789" +/// }, +/// "cpp" : [ +/// 17, +/// 20, +/// "and beyond" +/// ], +/// "lib" : "toml++", +/// "repo" : "https://github.com/marzer/tomlplusplus/", +/// "toml" : [ +/// "0.5.0", +/// "and beyond" +/// ] +/// } /// \eout +/// \see toml::default_formatter, toml::json_formatter +/// +/////////////////////////////////////////////////////////////////////// +/// +/// \section mainpage-contributing Contributing +/// See the [Contributing](https://github.com/marzer/tomlplusplus/blob/master/README.md#contributing) section of the repository README. +/// +/////////////////////////////////////////////////////////////////////// +/// +/// \section mainpage-license License +/// +/// toml++ is licensed under the terms of the MIT license - see [LICENSE](https://github.com/marzer/tomlplusplus/blob/master/LICENSE). +/// +/// UTF-8 decoding is performed using a state machine based on Bjoern Hoehrmann's 'Flexible and Economical UTF - 8 Decoder', which is also subject +/// to the terms of the MIT license - see [LICENSE-utf8-decoder](https://github.com/marzer/tomlplusplus/blob/master/LICENSE-utf8-decoder). +/// /// diff --git a/include/toml++/toml_array.h b/include/toml++/toml_array.h index e597ff16..963a3141 100644 --- a/include/toml++/toml_array.h +++ b/include/toml++/toml_array.h @@ -178,15 +178,12 @@ namespace toml::impl namespace toml { [[nodiscard]] bool operator == (const table& lhs, const table& rhs) noexcept; - [[nodiscard]] bool operator != (const table& lhs, const table& rhs) noexcept; /// \brief A TOML array. /// - /// \remarks The interface of this type is modeled after std::vector, with some + /// \detail The interface of this type is modeled after std::vector, with some /// additional considerations made for the heterogeneous nature of a - /// TOML array. - /// - /// \detail \cpp + /// TOML array. \cpp /// /// auto tbl = toml::parse("arr = [1, 2, 3, 4, 'five']"sv); /// auto& arr = *tbl.get_as("arr"); @@ -196,9 +193,9 @@ namespace toml /// { /// arr[i].visit([=](auto&& el) noexcept /// { - /// if constexpr (toml::is_integer) + /// if constexpr (toml::is_number) /// (*el)++; - /// else + /// else if constexpr (toml::is_string) /// el = "six"sv; /// }); /// } @@ -303,6 +300,9 @@ namespace toml return *this; } + array(const array&) = delete; + array& operator= (const array&) = delete; + /// \brief Always returns node_type::array for array nodes. [[nodiscard]] node_type type() const noexcept override { return node_type::array; } /// \brief Always returns `false` for array nodes. @@ -807,6 +807,31 @@ namespace toml private: + template + [[nodiscard]] static bool container_equality(const array& lhs, const T& rhs) noexcept + { + using elem_t = std::remove_const_t; + static_assert( + impl::is_value_or_promotable, + "Container element type must be (or be promotable to) one of the TOML value types" + ); + + if (lhs.size() != rhs.size()) + return false; + if (rhs.size() == 0_sz) + return true; + + size_t i{}; + for (auto& list_elem : rhs) + { + const auto elem = lhs.get_as>(i++); + if (!elem || *elem != list_elem) + return false; + } + + return true; + } + [[nodiscard]] size_t total_leaf_count() const noexcept { size_t leaves{}; @@ -836,6 +861,22 @@ namespace toml public: + /// \brief Initializer list equality operator. + template + [[nodiscard]] friend bool operator == (const array& lhs, const std::initializer_list& rhs) noexcept + { + return container_equality(lhs, rhs); + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::initializer_list&, template ) + + /// \brief Vector equality operator. + template + [[nodiscard]] friend bool operator == (const array& lhs, const std::vector& rhs) noexcept + { + return container_equality(lhs, rhs); + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::vector&, template ) + /// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself. /// /// \detail \cpp diff --git a/include/toml++/toml_common.h b/include/toml++/toml_common.h index d9b5d07f..c81acbd7 100644 --- a/include/toml++/toml_common.h +++ b/include/toml++/toml_common.h @@ -90,6 +90,10 @@ #define TOML_INTERFACE __declspec(novtable) #define TOML_EMPTY_BASES __declspec(empty_bases) + #if !defined(TOML_RELOPS_REORDERING) && defined(__cpp_impl_three_way_comparison) + #define TOML_RELOPS_REORDERING 1 + #endif + #elif defined(__GNUC__) #ifndef __cpp_exceptions @@ -114,6 +118,10 @@ #define TOML_USE_STREAMS_FOR_FLOATS 1 #endif + #if !defined(TOML_RELOPS_REORDERING) && defined(__cpp_impl_three_way_comparison) + #define TOML_RELOPS_REORDERING 1 + #endif + #endif #ifndef TOML_CPP_VERSION @@ -209,6 +217,17 @@ #ifndef TOML_NODISCARD_CTOR #define TOML_NODISCARD_CTOR #endif +#ifndef TOML_RELOPS_REORDERING + #define TOML_RELOPS_REORDERING 0 +#endif +#if TOML_RELOPS_REORDERING + #define TOML_ASYMMETRICAL_EQUALITY_OPS(...) +#else + #define TOML_ASYMMETRICAL_EQUALITY_OPS(LHS, RHS, ...) \ + __VA_ARGS__ [[nodiscard]] friend bool operator == (RHS rhs, LHS lhs) noexcept { return lhs == rhs; } \ + __VA_ARGS__ [[nodiscard]] friend bool operator != (LHS lhs, RHS rhs) noexcept { return !(lhs == rhs); } \ + __VA_ARGS__ [[nodiscard]] friend bool operator != (RHS rhs, LHS lhs) noexcept { return !(lhs == rhs); } +#endif #include "toml_version.h" @@ -217,10 +236,10 @@ #if TOML_UNRELEASED_FEATURES #define TOML_LANG_EFFECTIVE_VERSION \ - TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_REVISION+1) + TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH+1) #else #define TOML_LANG_EFFECTIVE_VERSION \ - TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_REVISION) + TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH) #endif #define TOML_LANG_HIGHER_THAN(maj, min, rev) \ @@ -232,6 +251,7 @@ #define TOML_LANG_EXACTLY(maj, min, rev) \ (TOML_LANG_EFFECTIVE_VERSION == TOML_MAKE_VERSION(maj, min, rev)) + ////////// INCLUDES TOML_PUSH_WARNINGS @@ -321,6 +341,7 @@ namespace toml /// \brief TOML node type identifiers. enum class node_type : uint8_t { + none, ///< Not-a-node. table, ///< The node is a toml::table. array, ///< The node is a toml::array. string, ///< The node is a toml::value. @@ -745,7 +766,7 @@ namespace toml::impl template <> struct node_type_of_ { static constexpr auto value = node_type::date_time; }; template - inline constexpr auto node_type_of = node_type_of_::type>>::value; + inline constexpr auto node_type_of = node_type_of_>::type>>::value; inline constexpr toml::string_view low_character_escape_table[] = { @@ -785,6 +806,7 @@ namespace toml::impl inline constexpr std::string_view node_type_friendly_names[] = { + "none"sv, "table"sv, "array"sv, "string"sv, @@ -836,6 +858,9 @@ namespace toml /// \brief Metafunction for determining if a type is a double or toml::value. template inline constexpr bool is_floating_point = std::is_same_v>, value>; + /// \brief Metafunction for determining if a type satisfies `toml::is_integer || toml::is_floating_point`. + template + inline constexpr bool is_number = is_integer || is_floating_point; /// \brief Metafunction for determining if a type is a bool toml::value. template inline constexpr bool is_boolean = std::is_same_v>, value>; diff --git a/include/toml++/toml_date_time.h b/include/toml++/toml_date_time.h index 878fe977..6bf6bd5c 100644 --- a/include/toml++/toml_date_time.h +++ b/include/toml++/toml_date_time.h @@ -13,7 +13,6 @@ namespace toml /// \brief The day component, from 1 - 31. uint8_t day; - /// \brief Equality operator. /// /// \param lhs The LHS date. @@ -44,6 +43,13 @@ namespace toml /// \brief Prints a date out to a stream as `YYYY-MM-DD` (per RFC 3339). + /// \detail \cpp + /// std::cout << toml::date{ 1987, 3, 16 } << std::endl; + /// \ecpp + /// + /// \out + /// 1987-03-16 + /// \eout template friend inline std::basic_ostream& operator << (std::basic_ostream& lhs, const date& rhs) TOML_MAY_THROW @@ -93,6 +99,15 @@ namespace toml } /// \brief Prints a time out to a stream as `HH:MM:SS.FFFFFF` (per RFC 3339). + /// \detail \cpp + /// std::cout << toml::time{ 10, 20, 34 } << std::endl; + /// std::cout << toml::time{ 10, 20, 34, 500000000 } << std::endl; + /// \ecpp + /// + /// \out + /// 10:20:34 + /// 10:20:34.5 + /// \eout template friend inline std::basic_ostream& operator << (std::basic_ostream& lhs, const time& rhs) TOML_MAY_THROW @@ -108,13 +123,19 @@ namespace toml /// \brief Offset from UTC+0, in minutes. int16_t minutes; - /// \brief Creates a timezone offset from separate hour and minute totals. + /// \brief Default-constructs a zero time-offset. + TOML_NODISCARD_CTOR + constexpr time_offset() noexcept + : minutes{} + {} + + /// \brief Constructs a timezone offset from separate hour and minute totals. /// /// \detail \cpp - /// std::cout << time_offset::from_hh_mm(2, 30) << std::endl; - /// std::cout << time_offset::from_hh_mm(-2, 30) << std::endl; - /// std::cout << time_offset::from_hh_mm(-2, -30) << std::endl; - /// std::cout << time_offset::from_hh_mm(0,0) << std::endl; + /// std::cout << toml::time_offset{ 2, 30 } << std::endl; + /// std::cout << toml::time_offset{ -2, 30 } << std::endl; + /// std::cout << toml::time_offset{ -2, -30 } << std::endl; + /// std::cout << toml::time_offset{ 0, 0 } << std::endl; /// /// \ecpp /// @@ -129,11 +150,10 @@ namespace toml /// \param minutes The total minutes. /// /// \returns A time_offset. - [[nodiscard]] - static constexpr time_offset from_hh_mm(int8_t hours, int8_t minutes) noexcept - { - return time_offset{ static_cast(hours * 60 + minutes) }; - } + TOML_NODISCARD_CTOR + constexpr time_offset(int8_t hours, int8_t minutes) noexcept + : minutes{ static_cast(hours * 60 + minutes) } + {} /// \brief Equality operator. /// @@ -160,6 +180,21 @@ namespace toml } /// \brief Prints a time_offset out to a stream as `+-HH:MM or Z` (per RFC 3339). + /// \detail \cpp + /// std::cout << toml::time_offset{ 2, 30 } << std::endl; + /// std::cout << toml::time_offset{ 2, -30 } << std::endl; + /// std::cout << toml::time_offset{} << std::endl; + /// std::cout << toml::time_offset{ -2, 30 } << std::endl; + /// std::cout << toml::time_offset{ -2, -30 } << std::endl; + /// \ecpp + /// + /// \out + /// +02:30 + /// +01:30 + /// Z + /// -01:30 + /// -02:30 + /// \eout template friend inline std::basic_ostream& operator << (std::basic_ostream& lhs, const time_offset& rhs) TOML_MAY_THROW @@ -181,6 +216,35 @@ namespace toml /// \remarks The date_time is said to be 'local' if the time_offset is empty. std::optional time_offset; + /// \brief Default-constructs a zero date-time. + TOML_NODISCARD_CTOR + constexpr date_time() noexcept + : date{}, + time{} + {} + + /// \brief Constructs a local date-time. + /// + /// \param d The date component. + /// \param t The time component. + TOML_NODISCARD_CTOR + constexpr date_time(toml::date d, toml::time t) noexcept + : date{ d }, + time{ t } + {} + + /// \brief Constructs an offset date-time. + /// + /// \param d The date component. + /// \param t The time component. + /// \param offset The timezone offset. + TOML_NODISCARD_CTOR + constexpr date_time(toml::date d, toml::time t, toml::time_offset offset) noexcept + : date{ d }, + time{ t }, + time_offset{ offset } + {} + /// \brief Returns true if this date_time does not contain timezone offset information. [[nodiscard]] constexpr bool is_local() const noexcept @@ -217,6 +281,17 @@ namespace toml } /// \brief Prints a date_time out to a stream in RFC 3339 format. + /// \detail \cpp + /// std::cout << toml::date_time{ { 1987, 3, 16 }, { 10, 20, 34 } } << std::endl; + /// std::cout << toml::date_time{ { 1987, 3, 16 }, { 10, 20, 34 }, { -2, -30 } } << std::endl; + /// std::cout << toml::date_time{ { 1987, 3, 16 }, { 10, 20, 34 }, {} } << std::endl; + /// \ecpp + /// + /// \out + /// 1987-03-16T10:20:34 + /// 1987-03-16T10:20:34-02:30 + /// 1987-03-16T10:20:34Z + /// \eout template friend inline std::basic_ostream& operator << (std::basic_ostream& lhs, const date_time& rhs) TOML_MAY_THROW diff --git a/include/toml++/toml_node.h b/include/toml++/toml_node.h index 5e84b982..57224bb0 100644 --- a/include/toml++/toml_node.h +++ b/include/toml++/toml_node.h @@ -82,9 +82,9 @@ namespace toml /// \brief Checks if a node is a specific type. /// - /// \tparam T The + /// \tparam T A TOML node or value type. /// - /// \returns Returns true if this node is an instance + /// \returns Returns true if this node is an instance of the specified type. template [[nodiscard]] TOML_ALWAYS_INLINE bool is() const noexcept diff --git a/include/toml++/toml_node_view.h b/include/toml++/toml_node_view.h index 2d14aa79..d17d615c 100644 --- a/include/toml++/toml_node_view.h +++ b/include/toml++/toml_node_view.h @@ -3,191 +3,173 @@ #include "toml_array.h" #include "toml_value.h" -namespace toml::impl -{ - template - struct node_view_traits; - - template <> - struct node_view_traits - { - using haystack_type = const table*; - using key_type = string_view; - - [[nodiscard]] static const node* get(const table* tbl, key_type key) noexcept - { - return tbl->get(key); - } - - template - [[nodiscard]] static const node_of* as(const table* tbl, key_type key) noexcept - { - return tbl->get_as(key); - } - }; - - template <> - struct node_view_traits - { - using haystack_type = table*; - using key_type = string_view; - - [[nodiscard]] static node* get(table* tbl, key_type key) noexcept - { - return tbl->get(key); - } - - [[nodiscard]] static const node* get(const table* tbl, key_type key) noexcept - { - return tbl->get(key); - } - - template - [[nodiscard]] static node_of* as(table* tbl, key_type key) noexcept - { - return tbl->get_as(key); - } - - template - [[nodiscard]] static const node_of* as(const table* tbl, key_type key) noexcept - { - return tbl->get_as(key); - } - }; - - template - struct sub_view final { }; - - template - struct node_view_traits> - { - using haystack_type = T; - using key_type = string_view; - - [[nodiscard]] static auto get(haystack_type& view, string_view key) noexcept - { - auto parent = view.as_table(); - return parent ? parent->get(key) : nullptr; - } - - [[nodiscard]] static const node* get(const haystack_type& view, string_view key) noexcept - { - auto parent = view.as_table(); - return parent ? parent->get(key) : nullptr; - } - - template - [[nodiscard]] static auto as(haystack_type& view, string_view key) noexcept - { - auto parent = view.as_table(); - return parent ? parent->template get_as(key) : nullptr; - } - - template - [[nodiscard]] static const node_of* as(const haystack_type& view, string_view key) noexcept - { - auto parent = view.as_table(); - return parent ? parent->template get_as(key) : nullptr; - } - }; - - template - struct node_view_traits> - { - using haystack_type = T; - using key_type = size_t; - - [[nodiscard]] static auto get(haystack_type& view, size_t index) noexcept - { - auto parent = view.as_array(); - return parent ? parent->get(index) : nullptr; - } - - [[nodiscard]] static const node* get(const haystack_type& view, size_t index) noexcept - { - auto parent = view.as_array(); - return parent ? parent->get(index) : nullptr; - } - - template - [[nodiscard]] static auto as(haystack_type& view, size_t index) noexcept - { - auto parent = view.as_array(); - return parent ? parent->template get_as(index) : nullptr; - } - - template - [[nodiscard]] static const node_of* as(const haystack_type& view, size_t index) noexcept - { - auto parent = view.as_array(); - return parent ? parent->template get_as(index) : nullptr; - } - }; -} - namespace toml { - /// \brief A read-only view into a node. + /// \brief A view of a node. + /// + /// \detail A node_view is like a std::optional with lots of toml-specific stuff built-in. + /// It _may_ represent a node, and allows you to do many of the same operations that you'd do + /// on nodes directly, as well as easily traversing the node tree by creating + /// subviews (via node_view::operator[]). \cpp + /// + /// auto tbl = toml::parse(R"( + /// + /// title = "my hardware store" + /// + /// [[products]] + /// name = "Hammer" + /// sku = 738594937 + /// keywords = [ "hammer", "construction", "build" ] /// - /// \warning This type is experimental. Functionality may change radically between versions - /// until I decide that I'm happy with it. Use it at your own risk. + /// [[products]] + /// name = "Nail" + /// sku = 284758393 + /// color = "gray" + /// + /// )"sv); + /// + /// std::cout << tbl["title"] << std::endl; + /// std::cout << tbl["products"][0]["name"] << std::endl; + /// std::cout << tbl["products"][0]["keywords"] << std::endl; + /// std::cout << tbl["products"][0]["keywords"][2] << std::endl; + /// + /// tbl["products"][0]["keywords"].as_array()->push_back("heavy"); + /// std::cout << tbl["products"][0]["keywords"] << std::endl; + /// std::cout << "has third product: "sv << !!tbl["products"][2] << std::endl; + /// std::cout << tbl["products"][2] << std::endl; // no-op + /// + /// \ecpp + /// + /// \out + /// "my hardware store" + /// "Hammer" + /// [ "hammer", "construction", "build" ] + /// [ "hammer", "construction", "build", "heavy" ] + /// has third product: false + /// \eout template class node_view final { public: - using traits = impl::node_view_traits; - using key_type = typename traits::key_type; + using viewed_type = T; private: - using haystack_type = typename traits::haystack_type; - haystack_type haystack_; - key_type key_; + friend class toml::table; - public: + viewed_type* node_; TOML_NODISCARD_CTOR - node_view(haystack_type obj, key_type key) noexcept - : haystack_{ obj }, - key_{ key } + node_view(viewed_type* node) noexcept + : node_{ node } {} - [[nodiscard]] auto get() noexcept { return traits::get(haystack_, key_); } - [[nodiscard]] const node* get() const noexcept { return traits::get(haystack_, key_); } + public: - [[nodiscard]] explicit operator bool() const noexcept { return !!get(); } + /// \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() noexcept { return node_; } + /// \brief Returns the node that's being referenced by the view (const overload). + [[nodiscard]] const 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; } + + /// \brief Returns true if the viewed node is a toml::table. + [[nodiscard]] bool is_table() const noexcept { return type() == node_type::table; } + /// \brief Returns true if the viewed node is a toml::array. + [[nodiscard]] bool is_array() const noexcept { return type() == node_type::array; } + /// \brief Returns true if the viewed node is a toml::value<>. + [[nodiscard]] bool is_value() const noexcept { return type() > node_type::array; } + /// \brief Returns true if the viewed node is a toml::value. + [[nodiscard]] bool is_string() const noexcept { return type() == node_type::string; } + /// \brief Returns true if the viewed node is a toml::value. + [[nodiscard]] bool is_integer() const noexcept { return type() == node_type::integer; } + /// \brief Returns true if the viewed node is a toml::value. + [[nodiscard]] bool is_floating_point() const noexcept { return type() == node_type::floating_point; } + /// \brief Returns true if the viewed node is a toml::value. + [[nodiscard]] bool is_boolean() const noexcept { return type() == node_type::boolean; } + /// \brief Returns true if the viewed node is a toml::value. + [[nodiscard]] bool is_date() const noexcept { return type() == node_type::date; } + /// \brief Returns true if the viewed node is a toml::value
(); } + /// \brief Returns a pointer to the viewed node as a toml::array, if it is one. + [[nodiscard]] auto as_array() noexcept { return as(); } + /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. [[nodiscard]] auto as_string() noexcept { return as(); } + /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. [[nodiscard]] auto as_integer() noexcept { return as(); } + /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. [[nodiscard]] auto as_floating_point() noexcept { return as(); } + /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. [[nodiscard]] auto as_boolean() noexcept { return as(); } + /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. [[nodiscard]] auto as_date() noexcept { return as(); } + /// \brief Returns a pointer to the viewed node as a toml::value
(); } + [[nodiscard]] const table* as_table() const noexcept { return as
(); } + [[nodiscard]] const array* as_array() const noexcept { return as(); } [[nodiscard]] const value* as_string() const noexcept { return as(); } [[nodiscard]] const value* as_integer() const noexcept { return as(); } [[nodiscard]] const value* as_floating_point() const noexcept { return as(); } @@ -195,97 +177,174 @@ namespace toml [[nodiscard]] const value* as_date() const noexcept { return as(); } [[nodiscard]] const value
(); } private: - template - [[nodiscard]] static bool value_equality(const node_view& lhs, const U& rhs) noexcept + template + static decltype(auto) do_visit(N* node, FUNC&& visitor) + TOML_MAY_THROW_UNLESS(noexcept(std::declval()->visit(std::declval()))) { - const auto val = lhs.as>(); - return val && val->get() == rhs; + using return_type = decltype(node->visit(std::forward(visitor))); + if (node) + return node->visit(std::forward(visitor)); + if constexpr (!std::is_void_v) + return return_type{}; } - template - [[nodiscard]] static bool container_equality(const node_view& lhs, const U& rhs) noexcept - { - using elem_t = std::remove_const_t; - static_assert( - impl::is_value_or_promotable, - "Container element type must be (or be promotable to) one of the TOML value types" - ); + template + static constexpr bool visit_is_nothrow = + noexcept(do_visit(std::declval(), std::declval())); - const array* arr = lhs.as(); - if (!arr || arr->size() != rhs.size()) - return false; - if (rhs.size() == 0_sz) - return true; + public: - size_t i{}; - for (auto& list_elem : rhs) - { - const auto elem = arr->get_as>(i++); - if (!elem || elem->get() != list_elem) - return false; - } + /// \brief Invokes a visitor on the viewed node based on its concrete type. + /// + /// \remarks Has no effect if the view does not reference a node. + /// + /// \see node::visit() + template + decltype(auto) visit(FUNC&& visitor) + TOML_MAY_THROW_UNLESS(visit_is_nothrow) + { + return do_visit(node_, std::forward(visitor)); + } - return true; + /// \brief Invokes a visitor on the viewed node based on its concrete type (const overload). + /// + /// \remarks Has no effect if the view does not reference a node. + /// + /// \see node::visit() + template + decltype(auto) visit(FUNC&& visitor) const + TOML_MAY_THROW_UNLESS(visit_is_nothrow) + { + return do_visit(node_, std::forward(visitor)); } - public: + /// \brief Returns true if the viewed node is a table with the same contents as RHS. + [[nodiscard]] friend bool operator == (const node_view& lhs, const table& rhs) noexcept + { + if (lhs.node_ == &rhs) + return true; + const auto tbl = lhs.as
(); + return tbl && *tbl == rhs; + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const table&, ) - [[nodiscard]] bool operator == (string_view rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (int64_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (int32_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (int16_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (int8_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (uint32_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (uint16_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (uint8_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (double rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (float rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (bool rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (const date& rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (const time& rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (const date_time& rhs) const noexcept { return value_equality(*this, rhs); } + /// \brief Returns true if the viewed node is an array with the same contents as RHS. + [[nodiscard]] friend bool operator == (const node_view& lhs, const array& rhs) noexcept + { + if (lhs.node_ == &rhs) + return true; + const auto arr = lhs.as(); + return arr && *arr == rhs; + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const array&, ) + /// \brief Returns true if the viewed node is a value with the same value as RHS. template - [[nodiscard]] bool operator == (const std::initializer_list& rhs) const noexcept + [[nodiscard]] friend bool operator == (const node_view& lhs, const value& rhs) noexcept { - return container_equality(*this, rhs); + if (lhs.node_ == &rhs) + return true; + const auto val = lhs.as(); + return val && *val == rhs; } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const value&, template ) + /// \brief Returns true if the viewed node is a value with the same value as RHS. + template >> + [[nodiscard]] friend bool operator == (const node_view& lhs, const U& rhs) noexcept + { + const auto val = lhs.as>(); + return val && *val == rhs; + } + TOML_ASYMMETRICAL_EQUALITY_OPS( + 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. template - [[nodiscard]] bool operator == (const std::vector& rhs) const noexcept + [[nodiscard]] friend bool operator == (const node_view& lhs, const std::initializer_list& rhs) noexcept { - return container_equality(*this, rhs); + const auto arr = lhs.as(); + return arr && *arr == rhs; } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::initializer_list&, template ) + /// \brief Returns true if the viewed node is an array with the same contents as the RHS vector. template - [[nodiscard]] friend bool operator == (const U& lhs, const node_view& rhs) noexcept + [[nodiscard]] friend bool operator == (const node_view& lhs, const std::vector& rhs) noexcept + { + const auto arr = lhs.as(); + return arr && *arr == rhs; + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::vector&, template ) + + /// \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. + [[nodiscard]] node_view operator[] (string_view key) noexcept { - return rhs == lhs; + if (auto tbl = this->as_table()) + return { tbl->get(key) }; + return { nullptr }; } - [[nodiscard]] node_view, string_view>> operator[] (string_view key) noexcept + /// \brief Returns a view of the selected subnode. + /// + /// \param index The index of the node to retrieve + /// + /// \returns A view of the selected node if this node represented an array and it contained a + /// value at the given index, or an empty view. + [[nodiscard]] node_view operator[] (size_t index) noexcept { - return { *this, key }; + if (auto tbl = this->as_array()) + return { tbl->get(index) }; + return { nullptr }; } - [[nodiscard]] node_view, size_t>> operator[] (size_t index) noexcept + /// \brief Returns a view of the selected subnode (const overload). + [[nodiscard]] node_view operator[] (string_view key) const noexcept { - return { *this, index }; + if (auto tbl = this->as_table()) + return { tbl->get(key) }; + return { nullptr }; + } + + /// \brief Returns a view of the selected subnode (const overload). + [[nodiscard]] node_view operator[] (size_t index) const noexcept + { + if (auto tbl = this->as_array()) + return { tbl->get(index) }; + return { nullptr }; + } + + + /// \brief Prints the viewed node out to a stream. + template + friend std::basic_ostream& operator << (std::basic_ostream& os, const node_view& nv) TOML_MAY_THROW + { + nv.visit([&](const auto& node) TOML_MAY_THROW + { + os << node; + }); + return os; } }; - inline node_view
table::operator[] (string_view key) noexcept + inline node_view table::operator[] (string_view key) noexcept { - return { this, key }; + return { this->get(key) }; } - inline node_view table::operator[] (string_view key) const noexcept + inline node_view table::operator[] (string_view key) const noexcept { - return { this, key }; + return { this->get(key) }; } } diff --git a/include/toml++/toml_parser.h b/include/toml++/toml_parser.h index f1148301..664f5371 100644 --- a/include/toml++/toml_parser.h +++ b/include/toml++/toml_parser.h @@ -597,6 +597,7 @@ namespace toml::impl case U'n': str += TOML_STRING_PREFIX('\n'); break; case U'r': str += TOML_STRING_PREFIX('\r'); break; + #if 0 case U's': { if constexpr (!TOML_LANG_HIGHER_THAN(0, 5, 0)) // toml/issues/622 @@ -612,6 +613,7 @@ namespace toml::impl break; } } + #endif case U't': str += TOML_STRING_PREFIX('\t'); break; case U'"': str += TOML_STRING_PREFIX('"'); break; @@ -1925,7 +1927,8 @@ namespace toml::impl " offset; expected minute between 0 and 59 (inclusive), saw "sv, hour ); - offset.emplace(time_offset{ static_cast((hour * 60 + minute) * sign) }); + offset.emplace(); + offset->minutes = static_cast((hour * 60 + minute) * sign); } } @@ -1937,11 +1940,10 @@ namespace toml::impl ); TOML_ERROR_CHECK({}); - return { - date, - time, - offset - }; + if (offset) + return { date, time, *offset }; + else + return { date, time }; } // TOML_DISABLE_SWITCH_WARNINGS diff --git a/include/toml++/toml_table.h b/include/toml++/toml_table.h index 616b8feb..9d785ece 100644 --- a/include/toml++/toml_table.h +++ b/include/toml++/toml_table.h @@ -123,7 +123,6 @@ namespace toml { private: friend class impl::parser; - friend class node_view
; impl::string_map> values; bool inline_ = false; @@ -198,6 +197,9 @@ namespace toml return *this; } + table(const table&) = delete; + table& operator= (const table&) = delete; + /// \brief Always returns `node_type::table` for table nodes. [[nodiscard]] node_type type() const noexcept override { return node_type::table; } /// \brief Always returns `true` for table nodes. @@ -256,17 +258,17 @@ namespace toml /// /// \param key The key used for the lookup. /// - /// \returns A node_view. + /// \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]] inline node_view
operator[] (string_view key) noexcept; + [[nodiscard]] inline node_view operator[] (string_view key) noexcept; /// \brief Gets a node_view for the selected key-value pair (const overload). - [[nodiscard]] inline node_view operator[] (string_view key) const noexcept; + [[nodiscard]] inline node_view operator[] (string_view key) const noexcept; /// \brief Returns an iterator to the first key-value pair. [[nodiscard]] iterator begin() noexcept { return { values.begin() }; } @@ -302,7 +304,7 @@ namespace toml /// for (auto k : { "a", "d" }) /// { /// auto result = tbl.insert(k, 42); - /// std::cout << "inserted a value with key '"sv << k << "': "sv << result.second << std::endl; + /// std::cout << "inserted with key '"sv << k << "': "sv << result.second << std::endl; /// } /// std::cout << tbl << std::endl; /// @@ -310,8 +312,8 @@ namespace toml /// /// \out /// { a = 1, b = 2, c = 3 } - /// inserted a value with key 'a': false - /// inserted a value with key 'd': true + /// inserted with key 'a': false + /// inserted with key 'd': true /// { a = 1, b = 2, c = 3, d = 42 } /// \eout /// @@ -365,7 +367,7 @@ namespace toml /// \param first An iterator to the first value in the input collection. /// \param last An iterator to one-past-the-last value in the input collection. /// - /// \remarks This function is morally equivalent to calling insert(key, value) for each + /// \remarks This function is morally equivalent to calling `insert(key, value)` for each /// key-value pair covered by the iterator range, so any values with keys already found in the /// table will not be replaced. template (k, "this is not a drill"sv, 14, 5); - /// std::cout << "emplaced a value with key '"sv << k << "': "sv << result.second << std::endl; + /// std::cout << "emplaced with key '"sv << k << "': "sv << result.second << std::endl; /// } /// std::cout << tbl << std::endl; /// @@ -457,8 +459,8 @@ namespace toml /// /// \out /// { a = 1, b = 2, c = 3 } - /// emplaced a value with key 'a': false - /// emplaced a value with key 'd': true + /// emplaced with key 'a': false + /// emplaced with key 'd': true /// { a = 1, b = 2, c = 3, d = "drill" } /// \eout /// diff --git a/include/toml++/toml_value.h b/include/toml++/toml_value.h index 8a74fd89..f9e771cc 100644 --- a/include/toml++/toml_value.h +++ b/include/toml++/toml_value.h @@ -6,8 +6,6 @@ namespace toml { /// \brief A TOML value. /// - /// \extends ::toml::node - /// /// \tparam T The value's data type. Can be one of: /// - toml::string /// - int64_t @@ -69,6 +67,9 @@ namespace toml return *this; } + value(const value&) = delete; + value& operator= (const value&) = delete; + /// \brief Returns the value's node type identifier. /// /// \returns One of: @@ -195,12 +196,7 @@ namespace toml /// \brief Value equality operator. [[nodiscard]] friend bool operator == (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ == rhs; } - /// \brief Value equality operator. - [[nodiscard]] friend bool operator == (value_arg_t lhs, const value& rhs) noexcept { return lhs == rhs.val_; } - /// \brief Value inequality operator. - [[nodiscard]] friend bool operator != (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ != rhs; } - /// \brief Value inequality operator. - [[nodiscard]] friend bool operator != (value_arg_t lhs, const value& rhs) noexcept { return lhs != rhs.val_; } + TOML_ASYMMETRICAL_EQUALITY_OPS(const value&, value_arg_t, ) /// \brief Value less-than operator. [[nodiscard]] friend bool operator < (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ < rhs; } @@ -244,10 +240,7 @@ namespace toml template [[nodiscard]] friend bool operator != (const value& lhs, const value& rhs) noexcept { - if constexpr (std::is_same_v) - return lhs.val_ != rhs.val_; - else - return true; + return !(lhs == rhs); } /// \brief Less-than operator. diff --git a/include/toml++/toml_version.h b/include/toml++/toml_version.h index abbf363b..77cad4dd 100644 --- a/include/toml++/toml_version.h +++ b/include/toml++/toml_version.h @@ -2,8 +2,8 @@ #define TOML_LIB_MAJOR 0 #define TOML_LIB_MINOR 1 -#define TOML_LIB_REVISION 0 +#define TOML_LIB_PATCH 0 #define TOML_LANG_MAJOR 0 #define TOML_LANG_MINOR 5 -#define TOML_LANG_REVISION 0 +#define TOML_LANG_PATCH 0 diff --git a/meson.build b/meson.build index 0fe3fca7..8fac1b10 100644 --- a/meson.build +++ b/meson.build @@ -13,7 +13,7 @@ project( compiler = meson.get_compiler('cpp') if compiler.get_id() == 'gcc' - add_project_arguments(['-fmax-errors=5', '-Wno-attributes'], language : 'cpp') + add_project_arguments(['-fmax-errors=5', '-Wno-attributes', '-Wno-init-list-lifetime' ], language : 'cpp') endif if compiler.get_id() == 'clang' diff --git a/python/generate_documentation.py b/python/generate_documentation.py index 7020a307..34436084 100644 --- a/python/generate_documentation.py +++ b/python/generate_documentation.py @@ -28,6 +28,7 @@ 'date', 'time', 'date_time', + 'time_offset', 'string', 'string_view', 'string_char', @@ -844,11 +845,14 @@ def preprocess_xml(xml_dir): toml_value_text = read_all_text_from_file(toml_value_path) toml_value_search_string = 'toml::value' toml_value_insert_pos = toml_value_text.find(toml_value_search_string) + changed = False if (toml_value_insert_pos != -1): toml_value_insert_pos += len(toml_value_search_string) toml_value_text = toml_value_text[:toml_value_insert_pos] \ + '\n toml::node' \ + toml_value_text[toml_value_insert_pos:] + changed = True + if changed: with open(toml_value_path,'w', encoding='utf-8', newline='\n') as output_file: print(toml_value_text, file=output_file) diff --git a/python/generate_single_header.py b/python/generate_single_header.py index 697d8ad8..351b3c6e 100644 --- a/python/generate_single_header.py +++ b/python/generate_single_header.py @@ -114,7 +114,7 @@ def main(): match = re.search(r'^\s*#\s*define\s+TOML_LIB_MINOR\s+([0-9]+)\s*$', source_text, re.I | re.M) if match is not None: library_version[1] = match.group(1) - match = re.search(r'^\s*#\s*define\s+TOML_LIB_REVISION\s+([0-9]+)\s*$', source_text, re.I | re.M) + match = re.search(r'^\s*#\s*define\s+TOML_LIB_(?:REVISION|PATCH)\s+([0-9]+)\s*$', source_text, re.I | re.M) if match is not None: library_version[2] = match.group(1) diff --git a/tests/manipulating_arrays.cpp b/tests/manipulating_arrays.cpp index a35eedea..2c40b306 100644 --- a/tests/manipulating_arrays.cpp +++ b/tests/manipulating_arrays.cpp @@ -96,6 +96,23 @@ TEST_CASE("arrays - equality") { array arr1{ 1, 2, 3 }; CHECK(arr1 == arr1); + { + auto ilist = { 1, 2, 3 }; + CHECK(arr1 == ilist); + CHECK(ilist == arr1); + + ilist = { 2, 3, 4 }; + CHECK(arr1 != ilist); + CHECK(ilist != arr1); + + auto ivec = std::vector{ 1, 2, 3 }; + CHECK(arr1 == ivec); + CHECK(ivec == arr1); + + ivec = std::vector{ 2, 3, 4 }; + CHECK(arr1 != ivec); + CHECK(ivec != arr1); + } array arr2{ 1, 2, 3 }; CHECK(arr1 == arr2); diff --git a/tests/parsing_dates_and_times.cpp b/tests/parsing_dates_and_times.cpp index 9e6eedc6..189a6afd 100644 --- a/tests/parsing_dates_and_times.cpp +++ b/tests/parsing_dates_and_times.cpp @@ -18,13 +18,13 @@ lt2 = 00:32:00.999999 )"sv), [](table&& tbl) noexcept { - static constexpr auto odt1 = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset{} }; + static constexpr auto odt1 = date_time{ { 1979, 5, 27 }, { 7, 32 }, {} }; CHECK(tbl[S("odt1")] == odt1); - static constexpr auto odt2 = date_time{ { 1979, 5, 27 }, { 0, 32 }, time_offset::from_hh_mm(-7, 0) }; + static constexpr auto odt2 = date_time{ { 1979, 5, 27 }, { 0, 32 }, { -7, 0 } }; CHECK(tbl[S("odt2")] == odt2); - static constexpr auto odt3 = date_time{ { 1979, 5, 27 }, { 0, 32, 0, 999999000u }, time_offset::from_hh_mm(-7, 0) }; + static constexpr auto odt3 = date_time{ { 1979, 5, 27 }, { 0, 32, 0, 999999000u }, { -7, 0 } }; CHECK(tbl[S("odt3")] == odt3); - static constexpr auto odt4 = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset{} }; + static constexpr auto odt4 = date_time{ { 1979, 5, 27 }, { 7, 32 }, {} }; CHECK(tbl[S("odt4")] == odt4); static constexpr auto ldt1 = date_time{ { 1979, 5, 27 }, { 7, 32 } }; CHECK(tbl[S("ldt1")] == ldt1); @@ -49,12 +49,12 @@ lt2 = 00:32:00.999999 parse_expected_value("1987-03-16 10:20:30"sv, val); } { - const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, time_offset::from_hh_mm( -9, -30 ) }; + const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, { -9, -30 } }; parse_expected_value("1987-03-16T10:20:30-09:30"sv, val); parse_expected_value("1987-03-16 10:20:30-09:30"sv, val); } { - const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, time_offset::from_hh_mm( 9, 30 ) }; + const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, { 9, 30 } }; parse_expected_value("1987-03-16T10:20:30+09:30"sv, val); parse_expected_value("1987-03-16 10:20:30+09:30"sv, val); } @@ -64,22 +64,22 @@ lt2 = 00:32:00.999999 parse_expected_value("1987-03-16 10:20:30.04"sv, val); } { - const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, time_offset::from_hh_mm( -9, -30 ) }; + const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, { -9, -30 } }; parse_expected_value("1987-03-16T10:20:30.04-09:30"sv, val); parse_expected_value("1987-03-16 10:20:30.04-09:30"sv, val); } { - const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, time_offset::from_hh_mm( 9, 30 ) }; + const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, { 9, 30 } }; parse_expected_value("1987-03-16T10:20:30.04+09:30"sv, val); parse_expected_value("1987-03-16 10:20:30.04+09:30"sv, val); } { - const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, time_offset{} }; + const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30 }, {} }; parse_expected_value("1987-03-16T10:20:30Z"sv, val); parse_expected_value("1987-03-16 10:20:30Z"sv, val); } { - const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, time_offset{} }; + const auto val = date_time{ { 1987, 3, 16 }, { 10, 20, 30, 40000000 }, {} }; parse_expected_value("1987-03-16T10:20:30.04Z"sv, val); parse_expected_value("1987-03-16 10:20:30.04Z"sv, val); } @@ -94,17 +94,17 @@ lt2 = 00:32:00.999999 parse_expected_value("1987-03-16 10:20"sv, val ); } { - const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, time_offset::from_hh_mm( -9, -30 ) }; + const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, { -9, -30 } }; parse_expected_value("1987-03-16T10:20-09:30"sv, val); parse_expected_value("1987-03-16 10:20-09:30"sv, val); } { - const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, time_offset::from_hh_mm( 9, 30 ) }; + const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, { 9, 30 } }; parse_expected_value("1987-03-16T10:20+09:30"sv, val); parse_expected_value("1987-03-16 10:20+09:30"sv, val); } { - const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, time_offset{} }; + const auto val = date_time{ { 1987, 3, 16 }, { 10, 20 }, {} }; parse_expected_value("1987-03-16T10:20Z"sv, val); parse_expected_value("1987-03-16 10:20Z"sv, val); } diff --git a/tests/parsing_spec_example.cpp b/tests/parsing_spec_example.cpp index 983ce0cf..158081f3 100644 --- a/tests/parsing_spec_example.cpp +++ b/tests/parsing_spec_example.cpp @@ -49,7 +49,7 @@ hosts = [ CHECK(tbl[S("owner")]); CHECK(tbl[S("owner")].as
()); CHECK(tbl[S("owner")][S("name")] == S("Tom Preston-Werner"sv)); - const auto dob = date_time{ { 1979, 5, 27 }, { 7, 32 }, time_offset::from_hh_mm(-8, 0) }; + const auto dob = date_time{ { 1979, 5, 27 }, { 7, 32 }, { -8, 0 } }; CHECK(tbl[S("owner")][S("dob")] == dob); CHECK(tbl[S("database")].as
()); diff --git a/tests/parsing_strings.cpp b/tests/parsing_strings.cpp index fc85e02a..5e3568e4 100644 --- a/tests/parsing_strings.cpp +++ b/tests/parsing_strings.cpp @@ -131,7 +131,7 @@ str = ''''That's still pointless', she said.''' S("\"\u03B1\u03B2\u03B3\""sv)); // toml/issues/622 - escaping alias for spaces - #if TOML_LANG_HIGHER_THAN(0, 5, 0) + #if 0 && TOML_LANG_HIGHER_THAN(0, 5, 0) parse_expected_value( R"("The\squick\sbrown\sfox\sjumps\sover\sthe\slazy\sdog")"sv, S("The quick brown fox jumps over the lazy dog"sv)); diff --git a/tests/tests.h b/tests/tests.h index 4dafd9be..dc3febcb 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -201,10 +201,28 @@ void parse_expected_value(std::string_view value_str, const T& expected) noexcep parsing_should_succeed(std::string_view{ value }, [&](table&& tbl) noexcept { CHECK(tbl.size() == 1); - REQUIRE(tbl[S("val"sv)].as>()); - REQUIRE(tbl[S("val"sv)].get()->type() == impl::node_type_of); - CHECK(tbl[S("val"sv)].as>()->get() == expected); - CHECK(tbl[S("val"sv)].get()->source().begin == begin); - CHECK(tbl[S("val"sv)].get()->source().end == end); + const auto nv = tbl[S("val"sv)]; + REQUIRE(nv); + REQUIRE(nv.as>()); + REQUIRE(nv.get()->type() == impl::node_type_of); + + //check the raw value + CHECK(nv.as>()->get() == expected); + + //check the value relops + CHECK(*nv.as>() == expected); + CHECK(expected == *nv.as>()); + CHECK(!(*nv.as>() != expected)); + CHECK(!(expected != *nv.as>())); + + //check the node_view relops + CHECK(nv == expected); + CHECK(expected == nv); + CHECK(!(nv != expected)); + CHECK(!(expected != nv)); + + //make sure source info is correct + CHECK(nv.get()->source().begin == begin); + CHECK(nv.get()->source().end == end); }); } diff --git a/toml.hpp b/toml.hpp index 48a8a756..7902879e 100644 --- a/toml.hpp +++ b/toml.hpp @@ -154,6 +154,10 @@ #define TOML_INTERFACE __declspec(novtable) #define TOML_EMPTY_BASES __declspec(empty_bases) + #if !defined(TOML_RELOPS_REORDERING) && defined(__cpp_impl_three_way_comparison) + #define TOML_RELOPS_REORDERING 1 + #endif + #elif defined(__GNUC__) #ifndef __cpp_exceptions @@ -178,6 +182,10 @@ #define TOML_USE_STREAMS_FOR_FLOATS 1 #endif + #if !defined(TOML_RELOPS_REORDERING) && defined(__cpp_impl_three_way_comparison) + #define TOML_RELOPS_REORDERING 1 + #endif + #endif #ifndef TOML_CPP_VERSION @@ -273,24 +281,35 @@ #ifndef TOML_NODISCARD_CTOR #define TOML_NODISCARD_CTOR #endif +#ifndef TOML_RELOPS_REORDERING + #define TOML_RELOPS_REORDERING 0 +#endif +#if TOML_RELOPS_REORDERING + #define TOML_ASYMMETRICAL_EQUALITY_OPS(...) +#else + #define TOML_ASYMMETRICAL_EQUALITY_OPS(LHS, RHS, ...) \ + __VA_ARGS__ [[nodiscard]] friend bool operator == (RHS rhs, LHS lhs) noexcept { return lhs == rhs; } \ + __VA_ARGS__ [[nodiscard]] friend bool operator != (LHS lhs, RHS rhs) noexcept { return !(lhs == rhs); } \ + __VA_ARGS__ [[nodiscard]] friend bool operator != (RHS rhs, LHS lhs) noexcept { return !(lhs == rhs); } +#endif #define TOML_LIB_MAJOR 0 #define TOML_LIB_MINOR 1 -#define TOML_LIB_REVISION 0 +#define TOML_LIB_PATCH 0 #define TOML_LANG_MAJOR 0 #define TOML_LANG_MINOR 5 -#define TOML_LANG_REVISION 0 +#define TOML_LANG_PATCH 0 #define TOML_MAKE_VERSION(maj, min, rev) \ ((maj) * 1000 + (min) * 25 + (rev)) #if TOML_UNRELEASED_FEATURES #define TOML_LANG_EFFECTIVE_VERSION \ - TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_REVISION+1) + TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH+1) #else #define TOML_LANG_EFFECTIVE_VERSION \ - TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_REVISION) + TOML_MAKE_VERSION(TOML_LANG_MAJOR, TOML_LANG_MINOR, TOML_LANG_PATCH) #endif #define TOML_LANG_HIGHER_THAN(maj, min, rev) \ @@ -375,6 +394,7 @@ namespace toml enum class node_type : uint8_t { + none, table, array, string, @@ -682,7 +702,7 @@ namespace toml::impl template <> struct node_type_of_ { static constexpr auto value = node_type::date_time; }; template - inline constexpr auto node_type_of = node_type_of_::type>>::value; + inline constexpr auto node_type_of = node_type_of_>::type>>::value; inline constexpr toml::string_view low_character_escape_table[] = { @@ -722,6 +742,7 @@ namespace toml::impl inline constexpr std::string_view node_type_friendly_names[] = { + "none"sv, "table"sv, "array"sv, "string"sv, @@ -768,6 +789,8 @@ namespace toml template inline constexpr bool is_floating_point = std::is_same_v>, value>; template + inline constexpr bool is_number = is_integer || is_floating_point; + template inline constexpr bool is_boolean = std::is_same_v>, value>; template inline constexpr bool is_date = std::is_same_v>, value>; @@ -867,11 +890,15 @@ namespace toml { int16_t minutes; - [[nodiscard]] - static constexpr time_offset from_hh_mm(int8_t hours, int8_t minutes) noexcept - { - return time_offset{ static_cast(hours * 60 + minutes) }; - } + TOML_NODISCARD_CTOR + constexpr time_offset() noexcept + : minutes{} + {} + + TOML_NODISCARD_CTOR + constexpr time_offset(int8_t hours, int8_t minutes) noexcept + : minutes{ static_cast(hours * 60 + minutes) } + {} [[nodiscard]] friend constexpr bool operator == (time_offset lhs, time_offset rhs) noexcept @@ -900,6 +927,25 @@ namespace toml toml::time time; std::optional time_offset; + TOML_NODISCARD_CTOR + constexpr date_time() noexcept + : date{}, + time{} + {} + + TOML_NODISCARD_CTOR + constexpr date_time(toml::date d, toml::time t) noexcept + : date{ d }, + time{ t } + {} + + TOML_NODISCARD_CTOR + constexpr date_time(toml::date d, toml::time t, toml::time_offset offset) noexcept + : date{ d }, + time{ t }, + time_offset{ offset } + {} + [[nodiscard]] constexpr bool is_local() const noexcept { @@ -1628,6 +1674,9 @@ namespace toml return *this; } + value(const value&) = delete; + value& operator= (const value&) = delete; + [[nodiscard]] node_type type() const noexcept override { return impl::node_type_of; } [[nodiscard]] bool is_table() const noexcept override { return false; } [[nodiscard]] bool is_array() const noexcept override { return false; } @@ -1704,9 +1753,7 @@ namespace toml } [[nodiscard]] friend bool operator == (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ == rhs; } - [[nodiscard]] friend bool operator == (value_arg_t lhs, const value& rhs) noexcept { return lhs == rhs.val_; } - [[nodiscard]] friend bool operator != (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ != rhs; } - [[nodiscard]] friend bool operator != (value_arg_t lhs, const value& rhs) noexcept { return lhs != rhs.val_; } + TOML_ASYMMETRICAL_EQUALITY_OPS(const value&, value_arg_t, ) [[nodiscard]] friend bool operator < (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ < rhs; } [[nodiscard]] friend bool operator < (value_arg_t lhs, const value& rhs) noexcept { return lhs < rhs.val_; } [[nodiscard]] friend bool operator <= (const value& lhs, value_arg_t rhs) noexcept { return lhs.val_ <= rhs; } @@ -1728,10 +1775,7 @@ namespace toml template [[nodiscard]] friend bool operator != (const value& lhs, const value& rhs) noexcept { - if constexpr (std::is_same_v) - return lhs.val_ != rhs.val_; - else - return true; + return !(lhs == rhs); } template @@ -1980,7 +2024,6 @@ namespace toml::impl namespace toml { [[nodiscard]] bool operator == (const table& lhs, const table& rhs) noexcept; - [[nodiscard]] bool operator != (const table& lhs, const table& rhs) noexcept; class array final : public node @@ -2042,6 +2085,9 @@ namespace toml return *this; } + array(const array&) = delete; + array& operator= (const array&) = delete; + [[nodiscard]] node_type type() const noexcept override { return node_type::array; } [[nodiscard]] bool is_table() const noexcept override { return false; } [[nodiscard]] bool is_array() const noexcept override { return true; } @@ -2266,6 +2312,31 @@ namespace toml private: + template + [[nodiscard]] static bool container_equality(const array& lhs, const T& rhs) noexcept + { + using elem_t = std::remove_const_t; + static_assert( + impl::is_value_or_promotable, + "Container element type must be (or be promotable to) one of the TOML value types" + ); + + if (lhs.size() != rhs.size()) + return false; + if (rhs.size() == 0_sz) + return true; + + size_t i{}; + for (auto& list_elem : rhs) + { + const auto elem = lhs.get_as>(i++); + if (!elem || *elem != list_elem) + return false; + } + + return true; + } + [[nodiscard]] size_t total_leaf_count() const noexcept { size_t leaves{}; @@ -2295,6 +2366,19 @@ namespace toml public: + template + [[nodiscard]] friend bool operator == (const array& lhs, const std::initializer_list& rhs) noexcept + { + return container_equality(lhs, rhs); + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::initializer_list&, template ) + + template + [[nodiscard]] friend bool operator == (const array& lhs, const std::vector& rhs) noexcept + { + return container_equality(lhs, rhs); + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::vector&, template ) void flatten() TOML_MAY_THROW { if (values.empty()) @@ -2468,7 +2552,6 @@ namespace toml { private: friend class impl::parser; - friend class node_view
; impl::string_map> values; bool inline_ = false; @@ -2509,6 +2592,9 @@ namespace toml return *this; } + table(const table&) = delete; + table& operator= (const table&) = delete; + [[nodiscard]] node_type type() const noexcept override { return node_type::table; } [[nodiscard]] bool is_table() const noexcept override { return true; } [[nodiscard]] bool is_array() const noexcept override { return false; } @@ -2517,8 +2603,8 @@ namespace toml [[nodiscard]] const table* as_table() const noexcept override { return this; } [[nodiscard]] bool is_inline() const noexcept { return inline_; } void is_inline(bool val) noexcept { inline_ = val; } - [[nodiscard]] inline node_view
operator[] (string_view key) noexcept; - [[nodiscard]] inline node_view operator[] (string_view key) const noexcept; + [[nodiscard]] inline node_view operator[] (string_view key) noexcept; + [[nodiscard]] inline node_view operator[] (string_view key) const noexcept; [[nodiscard]] iterator begin() noexcept { return { values.begin() }; } [[nodiscard]] const_iterator begin() const noexcept { return { values.begin() }; } [[nodiscard]] const_iterator cbegin() const noexcept { return { values.cbegin() }; } @@ -2713,176 +2799,79 @@ namespace toml //------------------------------------------------------------------------------------------ ↓ toml_node_view.h ------ #pragma region -namespace toml::impl -{ - template - struct node_view_traits; - - template <> - struct node_view_traits - { - using haystack_type = const table*; - using key_type = string_view; - - [[nodiscard]] static const node* get(const table* tbl, key_type key) noexcept - { - return tbl->get(key); - } - - template - [[nodiscard]] static const node_of* as(const table* tbl, key_type key) noexcept - { - return tbl->get_as(key); - } - }; - - template <> - struct node_view_traits
- { - using haystack_type = table*; - using key_type = string_view; - - [[nodiscard]] static node* get(table* tbl, key_type key) noexcept - { - return tbl->get(key); - } - - [[nodiscard]] static const node* get(const table* tbl, key_type key) noexcept - { - return tbl->get(key); - } - - template - [[nodiscard]] static node_of* as(table* tbl, key_type key) noexcept - { - return tbl->get_as(key); - } - - template - [[nodiscard]] static const node_of* as(const table* tbl, key_type key) noexcept - { - return tbl->get_as(key); - } - }; - - template - struct sub_view final { }; - - template - struct node_view_traits> - { - using haystack_type = T; - using key_type = string_view; - - [[nodiscard]] static auto get(haystack_type& view, string_view key) noexcept - { - auto parent = view.as_table(); - return parent ? parent->get(key) : nullptr; - } - - [[nodiscard]] static const node* get(const haystack_type& view, string_view key) noexcept - { - auto parent = view.as_table(); - return parent ? parent->get(key) : nullptr; - } - - template - [[nodiscard]] static auto as(haystack_type& view, string_view key) noexcept - { - auto parent = view.as_table(); - return parent ? parent->template get_as(key) : nullptr; - } - - template - [[nodiscard]] static const node_of* as(const haystack_type& view, string_view key) noexcept - { - auto parent = view.as_table(); - return parent ? parent->template get_as(key) : nullptr; - } - }; - - template - struct node_view_traits> - { - using haystack_type = T; - using key_type = size_t; - - [[nodiscard]] static auto get(haystack_type& view, size_t index) noexcept - { - auto parent = view.as_array(); - return parent ? parent->get(index) : nullptr; - } - - [[nodiscard]] static const node* get(const haystack_type& view, size_t index) noexcept - { - auto parent = view.as_array(); - return parent ? parent->get(index) : nullptr; - } - - template - [[nodiscard]] static auto as(haystack_type& view, size_t index) noexcept - { - auto parent = view.as_array(); - return parent ? parent->template get_as(index) : nullptr; - } - - template - [[nodiscard]] static const node_of* as(const haystack_type& view, size_t index) noexcept - { - auto parent = view.as_array(); - return parent ? parent->template get_as(index) : nullptr; - } - }; -} - namespace toml { template class node_view final { public: - using traits = impl::node_view_traits; - using key_type = typename traits::key_type; + using viewed_type = T; private: - using haystack_type = typename traits::haystack_type; - haystack_type haystack_; - key_type key_; + friend class toml::table; - public: + viewed_type* node_; TOML_NODISCARD_CTOR - node_view(haystack_type obj, key_type key) noexcept - : haystack_{ obj }, - key_{ key } + node_view(viewed_type* node) noexcept + : node_{ node } {} - [[nodiscard]] auto get() noexcept { return traits::get(haystack_, key_); } - [[nodiscard]] const node* get() const noexcept { return traits::get(haystack_, key_); } - [[nodiscard]] explicit operator bool() const noexcept { return !!get(); } + public: + + [[nodiscard]] explicit operator bool() const noexcept { return node_ != nullptr; } + [[nodiscard]] viewed_type* get() noexcept { return node_; } + [[nodiscard]] const 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 type() == node_type::table; } + [[nodiscard]] bool is_array() const noexcept { return type() == node_type::array; } + [[nodiscard]] bool is_value() const noexcept { return type() > node_type::array; } + [[nodiscard]] bool is_string() const noexcept { return type() == node_type::string; } + [[nodiscard]] bool is_integer() const noexcept { return type() == node_type::integer; } + [[nodiscard]] bool is_floating_point() const noexcept { return type() == node_type::floating_point; } + [[nodiscard]] bool is_boolean() const noexcept { return type() == node_type::boolean; } + [[nodiscard]] bool is_date() const noexcept { return type() == node_type::date; } + [[nodiscard]] bool is_time() const noexcept { return type() == node_type::time; } + [[nodiscard]] bool is_date_time() const noexcept { return type() == node_type::date_time; } + [[nodiscard]] bool is_array_of_tables() const noexcept + { + return node_ ? node_->is_array_of_tables() : false; + } + + template + [[nodiscard]] + bool is() const noexcept + { + return node_ ? node_->template is() : false; + } template - [[nodiscard]] auto as() noexcept + [[nodiscard]] + auto as() noexcept { static_assert( impl::is_value_or_node>, "Template type parameter must be one of the basic value types, a toml::table, or a toml::array" ); - return traits::template as(haystack_, key_); + return node_ ? node_->template as() : nullptr; } template - [[nodiscard]] const impl::node_of* as() const noexcept + [[nodiscard]] + const impl::node_of* as() const noexcept { static_assert( impl::is_value_or_node>, "Template type parameter must be one of the basic value types, a toml::table, or a toml::array" ); - return traits::template as(haystack_, key_); + return node_ ? node_->template as() : nullptr; } + [[nodiscard]] auto as_table() noexcept { return as
(); } + [[nodiscard]] auto as_array() noexcept { return as(); } [[nodiscard]] auto as_string() noexcept { return as(); } [[nodiscard]] auto as_integer() noexcept { return as(); } [[nodiscard]] auto as_floating_point() noexcept { return as(); } @@ -2890,8 +2879,8 @@ namespace toml [[nodiscard]] auto as_date() noexcept { return as(); } [[nodiscard]] auto as_time() noexcept { return as
(); } + [[nodiscard]] const table* as_table() const noexcept { return as
(); } + [[nodiscard]] const array* as_array() const noexcept { return as(); } [[nodiscard]] const value* as_string() const noexcept { return as(); } [[nodiscard]] const value* as_integer() const noexcept { return as(); } [[nodiscard]] const value* as_floating_point() const noexcept { return as(); } @@ -2899,98 +2888,141 @@ namespace toml [[nodiscard]] const value* as_date() const noexcept { return as(); } [[nodiscard]] const value
(); } private: - template - [[nodiscard]] static bool value_equality(const node_view& lhs, const U& rhs) noexcept + template + static decltype(auto) do_visit(N* node, FUNC&& visitor) + TOML_MAY_THROW_UNLESS(noexcept(std::declval()->visit(std::declval()))) { - const auto val = lhs.as>(); - return val && val->get() == rhs; + using return_type = decltype(node->visit(std::forward(visitor))); + if (node) + return node->visit(std::forward(visitor)); + if constexpr (!std::is_void_v) + return return_type{}; } - template - [[nodiscard]] static bool container_equality(const node_view& lhs, const U& rhs) noexcept - { - using elem_t = std::remove_const_t; - static_assert( - impl::is_value_or_promotable, - "Container element type must be (or be promotable to) one of the TOML value types" - ); - - const array* arr = lhs.as(); - if (!arr || arr->size() != rhs.size()) - return false; - if (rhs.size() == 0_sz) - return true; + template + static constexpr bool visit_is_nothrow = + noexcept(do_visit(std::declval(), std::declval())); - size_t i{}; - for (auto& list_elem : rhs) - { - const auto elem = arr->get_as>(i++); - if (!elem || elem->get() != list_elem) - return false; - } + public: - return true; + template + decltype(auto) visit(FUNC&& visitor) + TOML_MAY_THROW_UNLESS(visit_is_nothrow) + { + return do_visit(node_, std::forward(visitor)); } - public: + template + decltype(auto) visit(FUNC&& visitor) const + TOML_MAY_THROW_UNLESS(visit_is_nothrow) + { + return do_visit(node_, std::forward(visitor)); + } - [[nodiscard]] bool operator == (string_view rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (int64_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (int32_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (int16_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (int8_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (uint32_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (uint16_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (uint8_t rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (double rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (float rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (bool rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (const date& rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (const time& rhs) const noexcept { return value_equality(*this, rhs); } - [[nodiscard]] bool operator == (const date_time& rhs) const noexcept { return value_equality(*this, rhs); } + [[nodiscard]] friend bool operator == (const node_view& lhs, const table& rhs) noexcept + { + if (lhs.node_ == &rhs) + return true; + const auto tbl = lhs.as
(); + return tbl && *tbl == rhs; + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const table&, ) + [[nodiscard]] friend bool operator == (const node_view& lhs, const array& rhs) noexcept + { + if (lhs.node_ == &rhs) + return true; + const auto arr = lhs.as(); + return arr && *arr == rhs; + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const array&, ) template - [[nodiscard]] bool operator == (const std::initializer_list& rhs) const noexcept + [[nodiscard]] friend bool operator == (const node_view& lhs, const value& rhs) noexcept { - return container_equality(*this, rhs); + if (lhs.node_ == &rhs) + return true; + const auto val = lhs.as(); + return val && *val == rhs; + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const value&, template ) + + template >> + [[nodiscard]] friend bool operator == (const node_view& lhs, const U& rhs) noexcept + { + const auto val = lhs.as>(); + return val && *val == rhs; } + TOML_ASYMMETRICAL_EQUALITY_OPS( + const node_view&, + const U&, + template >> + ) template - [[nodiscard]] bool operator == (const std::vector& rhs) const noexcept + [[nodiscard]] friend bool operator == (const node_view& lhs, const std::initializer_list& rhs) noexcept { - return container_equality(*this, rhs); + const auto arr = lhs.as(); + return arr && *arr == rhs; } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::initializer_list&, template ) template - [[nodiscard]] friend bool operator == (const U& lhs, const node_view& rhs) noexcept + [[nodiscard]] friend bool operator == (const node_view& lhs, const std::vector& rhs) noexcept + { + const auto arr = lhs.as(); + return arr && *arr == rhs; + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const node_view&, const std::vector&, template ) + [[nodiscard]] node_view operator[] (string_view key) noexcept + { + if (auto tbl = this->as_table()) + return { tbl->get(key) }; + return { nullptr }; + } + + [[nodiscard]] node_view operator[] (size_t index) noexcept + { + if (auto tbl = this->as_array()) + return { tbl->get(index) }; + return { nullptr }; + } + + [[nodiscard]] node_view operator[] (string_view key) const noexcept { - return rhs == lhs; + if (auto tbl = this->as_table()) + return { tbl->get(key) }; + return { nullptr }; } - [[nodiscard]] node_view, string_view>> operator[] (string_view key) noexcept + [[nodiscard]] node_view operator[] (size_t index) const noexcept { - return { *this, key }; + if (auto tbl = this->as_array()) + return { tbl->get(index) }; + return { nullptr }; } - [[nodiscard]] node_view, size_t>> operator[] (size_t index) noexcept + template + friend std::basic_ostream& operator << (std::basic_ostream& os, const node_view& nv) TOML_MAY_THROW { - return { *this, index }; + nv.visit([&](const auto& node) TOML_MAY_THROW + { + os << node; + }); + return os; } }; - inline node_view
table::operator[] (string_view key) noexcept + inline node_view table::operator[] (string_view key) noexcept { - return { this, key }; + return { this->get(key) }; } - inline node_view table::operator[] (string_view key) const noexcept + inline node_view table::operator[] (string_view key) const noexcept { - return { this, key }; + return { this->get(key) }; } } @@ -5124,6 +5156,7 @@ namespace toml::impl case U'n': str += TOML_STRING_PREFIX('\n'); break; case U'r': str += TOML_STRING_PREFIX('\r'); break; + #if 0 case U's': { if constexpr (!TOML_LANG_HIGHER_THAN(0, 5, 0)) // toml/issues/622 @@ -5139,6 +5172,7 @@ namespace toml::impl break; } } + #endif case U't': str += TOML_STRING_PREFIX('\t'); break; case U'"': str += TOML_STRING_PREFIX('"'); break; @@ -6451,7 +6485,8 @@ namespace toml::impl " offset; expected minute between 0 and 59 (inclusive), saw "sv, hour ); - offset.emplace(time_offset{ static_cast((hour * 60 + minute) * sign) }); + offset.emplace(); + offset->minutes = static_cast((hour * 60 + minute) * sign); } } @@ -6462,11 +6497,10 @@ namespace toml::impl ); TOML_ERROR_CHECK({}); - return { - date, - time, - offset - }; + if (offset) + return { date, time, *offset }; + else + return { date, time }; } // TOML_DISABLE_SWITCH_WARNINGS @@ -8447,6 +8481,8 @@ namespace toml #undef TOML_STRING_PREFIX #undef TOML_UNDEF_MACROS #undef TOML_DOXYGEN + #undef TOML_RELOPS_REORDERING + #undef TOML_ASYMMETRICAL_EQUALITY_OPS #endif #ifdef __GNUC__