From 9066ac7d017f908076e9da5de322eb914120ff61 Mon Sep 17 00:00:00 2001 From: Mark Gillard Date: Sun, 14 Nov 2021 19:24:19 +0200 Subject: [PATCH] fixed strong exception guarantee edge-cases also: - added `value` copy+move constructor overloads with flags override `table::ref()` now supports explicit ref categories and cv-qualifiers --- CHANGELOG.md | 5 +- include/toml++/impl/array.h | 555 ++++---- include/toml++/impl/array.inl | 130 +- include/toml++/impl/date_time.h | 1 - include/toml++/impl/formatter.inl | 2 +- include/toml++/impl/forward_declarations.h | 108 +- include/toml++/impl/key.h | 9 + include/toml++/impl/make_node.h | 2 +- include/toml++/impl/node.h | 210 ++- include/toml++/impl/node_view.h | 42 +- include/toml++/impl/parse_result.h | 13 +- include/toml++/impl/parser.h | 10 - include/toml++/impl/parser.inl | 104 +- include/toml++/impl/preprocessor.h | 37 +- include/toml++/impl/table.h | 1103 ++++++++------- include/toml++/impl/table.inl | 128 +- include/toml++/impl/toml_formatter.inl | 2 +- include/toml++/impl/utf8.h | 2 +- include/toml++/impl/value.h | 67 +- include/toml++/toml.h | 2 + tests/impl_toml.cpp | 130 +- toml.hpp | 1496 +++++++++++++------- 22 files changed, 2537 insertions(+), 1621 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc8f3ee..925a281 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,8 +22,8 @@ code changes at callsites or in build systems are indicated with ⚠️. #### Fixes: - ⚠️ fixed incorrect `noexcept` specifications on many functions +- ⚠️ fixed `toml::table` init-list constructor requiring double-brackets - fixed `toml::json_formatter` not formatting inf and nan incorrectly -- fixed `toml::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 documentation issues @@ -32,6 +32,7 @@ code changes at callsites or in build systems are indicated with ⚠️. - fixed missing `#include ` - fixed missing `TOML_API` on interfaces - fixed parser not correctly round-tripping the format of binary and octal integers in some cases +- fixed strong exception guarantee edge-cases in `toml::table` and `toml::array` #### Additions: - added `operator->` to `toml::value` for class types @@ -52,6 +53,7 @@ code changes at callsites or in build systems are indicated with ⚠️. - added `toml::table::emplace_hint()` (same semantics as `std::map::emplace_hint()`) - added `toml::table::lower_bound()` (same semantics as `std::map::lower_bound()`) - added `toml::table::prune()` +- added `toml::value` copy+move constructor overloads with flags override - added `toml::yaml_formatter` - added `TOML_ENABLE_FORMATTERS` option - added clang's enum annotation attributes to all enums @@ -70,6 +72,7 @@ code changes at callsites or in build systems are indicated with ⚠️. - ⚠️ 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) +- `toml::table::ref()` now supports explicit ref categories and cv-qualifiers - applied clang-format to all the things 🎉️ - improved performance of parser - made date/time constructors accept any integral types diff --git a/include/toml++/impl/array.h b/include/toml++/impl/array.h index ad990ab..e6d0de1 100644 --- a/include/toml++/impl/array.h +++ b/include/toml++/impl/array.h @@ -21,31 +21,31 @@ TOML_IMPL_NAMESPACE_START template friend class array_iterator; - using raw_mutable_iterator = std::vector::iterator; - using raw_const_iterator = std::vector::const_iterator; - using raw_iterator = std::conditional_t; + using mutable_vector_iterator = std::vector::iterator; + using const_vector_iterator = std::vector::const_iterator; + using vector_iterator = std::conditional_t; - mutable raw_iterator iter_; + mutable vector_iterator iter_; public: using value_type = std::conditional_t; using reference = value_type&; using pointer = value_type*; using difference_type = ptrdiff_t; - using iterator_category = typename std::iterator_traits::iterator_category; + using iterator_category = typename std::iterator_traits::iterator_category; TOML_NODISCARD_CTOR array_iterator() noexcept = default; TOML_NODISCARD_CTOR - array_iterator(raw_mutable_iterator raw) noexcept // - : iter_{ raw } + array_iterator(mutable_vector_iterator iter) noexcept // + : iter_{ iter } {} TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst) TOML_NODISCARD_CTOR - array_iterator(raw_const_iterator raw) noexcept // - : iter_{ raw } + array_iterator(const_vector_iterator iter) noexcept // + : iter_{ iter } {} TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst) @@ -98,7 +98,14 @@ TOML_IMPL_NAMESPACE_START } TOML_PURE_INLINE_GETTER - operator const raw_iterator&() const noexcept + operator const vector_iterator&() const noexcept + { + return iter_; + } + + TOML_CONSTRAINED_TEMPLATE(!C, bool C = IsConst) + TOML_PURE_INLINE_GETTER + operator const const_vector_iterator() const noexcept { return iter_; } @@ -181,6 +188,17 @@ TOML_IMPL_NAMESPACE_START return *(iter_ + idx)->get(); } }; + + struct array_init_elem + { + mutable node_ptr value; + + template + TOML_NODISCARD_CTOR + array_init_elem(T&& val, value_flags flags = preserve_source_value_flags) // + : value{ make_node(static_cast(val), flags) } + {} + }; } TOML_IMPL_NAMESPACE_END; /// \endcond @@ -235,7 +253,6 @@ TOML_NAMESPACE_START /// arr.emplace_back("ten"); /// arr.emplace_back(11, 12.0); /// std::cout << arr << "\n"; - /// /// \ecpp /// /// \out @@ -249,11 +266,29 @@ TOML_NAMESPACE_START private: /// \cond - std::vector elems_; + using vector_type = std::vector; + using vector_iterator = typename vector_type::iterator; + using const_vector_iterator = typename vector_type::const_iterator; + vector_type elems_; + + TOML_NODISCARD_CTOR + TOML_API + array(const impl::array_init_elem*, const impl::array_init_elem*); + + TOML_NODISCARD_CTOR + array(std::false_type, std::initializer_list elems) // + : array{ elems.begin(), elems.end() } + {} TOML_API void preinsertion_resize(size_t idx, size_t count); + TOML_API + void insert_at_back(impl::node_ptr&&); + + TOML_API + vector_iterator insert_at(const_vector_iterator, impl::node_ptr&&); + template void emplace_back_if_not_empty_view(T&& val, value_flags flags) { @@ -262,7 +297,7 @@ TOML_NAMESPACE_START if (!val) return; } - elems_.emplace_back(impl::make_node(static_cast(val), flags)); + insert_at_back(impl::make_node(static_cast(val), flags)); } TOML_NODISCARD @@ -271,6 +306,7 @@ TOML_NAMESPACE_START TOML_API void flatten_child(array&& child, size_t& dest_index) noexcept; + /// \endcond public: @@ -316,14 +352,6 @@ TOML_NAMESPACE_START TOML_API array(array&& other) noexcept; - /// \brief Copy-assignment operator. - TOML_API - array& operator=(const array&); - - /// \brief Move-assignment operator. - TOML_API - array& operator=(array&& rhs) noexcept; - /// \brief Constructs an array with one or more initial elements. /// /// \detail \cpp @@ -361,18 +389,18 @@ TOML_NAMESPACE_START typename... ElemTypes) TOML_NODISCARD_CTOR explicit array(ElemType&& val, ElemTypes&&... vals) - { - elems_.reserve(sizeof...(ElemTypes) + 1u); - emplace_back_if_not_empty_view(static_cast(val), preserve_source_value_flags); - if constexpr (sizeof...(ElemTypes) > 0) - { - (emplace_back_if_not_empty_view(static_cast(vals), preserve_source_value_flags), ...); - } + : array{ std::false_type{}, + std::initializer_list{ static_cast(val), + static_cast(vals)... } } + {} -#if TOML_LIFETIME_HOOKS - TOML_ARRAY_CREATED; -#endif - } + /// \brief Copy-assignment operator. + TOML_API + array& operator=(const array&); + + /// \brief Move-assignment operator. + TOML_API + array& operator=(array&& rhs) noexcept; /// \name Type checks /// @{ @@ -401,13 +429,13 @@ TOML_NAMESPACE_START TOML_PURE_GETTER bool is_homogeneous() const noexcept { - using type = impl::unwrap_node; - static_assert( - std::is_void_v< - type> || ((impl::is_native || impl::is_one_of)&&!impl::is_cvref), - "The template type argument of array::is_homogeneous() must be void or one " - "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - return is_homogeneous(impl::node_type_of); + using unwrapped_type = impl::unwrap_node>; + static_assert(std::is_void_v // + || (impl::is_native || impl::is_one_of), + "The template type argument of array::is_homogeneous() must be void or one " + "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + return is_homogeneous(impl::node_type_of); } /// \endcond @@ -628,7 +656,7 @@ TOML_NAMESPACE_START /// @} - /// \name Array operations + /// \name Value retrieval /// @{ /// \brief Gets a pointer to the element at a specific index. @@ -640,7 +668,6 @@ TOML_NAMESPACE_START /// std::cout << "element [2] exists: "sv << !!arr.get(2) << "\n"; /// if (toml::node* val = arr.get(0)) /// std::cout << "element [0] is an "sv << val->type() << "\n"; - /// /// \ecpp /// /// \out @@ -676,7 +703,6 @@ TOML_NAMESPACE_START /// auto arr = toml::array{ 42, "is the meaning of life, apparently."sv }; /// if (toml::value* val = arr.get_as(0)) /// std::cout << "element [0] is an integer with value "sv << *val << "\n"; - /// /// \ecpp /// /// \out @@ -763,6 +789,11 @@ TOML_NAMESPACE_START return *elems_.back(); } + /// @} + + /// \name Iterators + /// @{ + /// \brief Returns an iterator to the first element. TOML_NODISCARD iterator begin() noexcept @@ -805,6 +836,11 @@ TOML_NAMESPACE_START return elems_.cend(); } + /// @} + + /// \name Size and Capacity + /// @{ + /// \brief Returns true if the array is empty. TOML_NODISCARD bool empty() const noexcept @@ -819,18 +855,6 @@ TOML_NAMESPACE_START return elems_.size(); } - /// \brief Reserves internal storage capacity up to a pre-determined number of elements. - void reserve(size_t new_capacity) - { - elems_.reserve(new_capacity); - } - - /// \brief Removes all elements from the array. - void clear() noexcept - { - elems_.clear(); - } - /// \brief Returns the maximum number of elements that can be stored in an array on the current platform. TOML_NODISCARD size_t max_size() const noexcept @@ -845,12 +869,205 @@ TOML_NAMESPACE_START return elems_.capacity(); } + /// \brief Reserves internal storage capacity up to a pre-determined number of elements. + TOML_API + void reserve(size_t new_capacity); + /// \brief Requests the removal of any unused internal storage capacity. - void shrink_to_fit() + TOML_API + void shrink_to_fit(); + + /// \brief Shrinks the array to the given size. + /// + /// \detail \godbolt{rxEzK5} + /// + /// \cpp + /// auto arr = toml::array{ 1, 2, 3 }; + /// std::cout << arr << "\n"; + /// + /// arr.truncate(5); // no-op + /// std::cout << arr << "\n"; + /// + /// arr.truncate(1); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2, 3 ] + /// [ 1, 2, 3 ] + /// [ 1] + /// \eout + /// + /// \remarks Does nothing if the requested size is larger than or equal to the current size. + TOML_API + void truncate(size_t new_size); + + /// \brief Resizes the array. + /// + /// \detail \godbolt{W5zqx3} + /// + /// \cpp + /// auto arr = toml::array{ 1, 2, 3 }; + /// std::cout << arr << "\n"; + /// + /// arr.resize(6, 42); + /// std::cout << arr << "\n"; + /// + /// arr.resize(2, 0); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2, 3 ] + /// [ 1, 2, 3, 42, 42, 42 ] + /// [ 1, 2 ] + /// \eout + /// + /// \tparam ElemType toml::node, toml::table, toml::array, or a native TOML value type + /// (or a type promotable to one). + /// + /// \param new_size The number of elements the array will have after resizing. + /// \param default_init_val The node or value used to initialize new elements if the array needs to grow. + /// \param default_init_flags Value flags to apply to new values created if new elements are created by growing. + template + void resize(size_t new_size, + ElemType&& default_init_val, + value_flags default_init_flags = preserve_source_value_flags) { - elems_.shrink_to_fit(); + static_assert(!is_node_view, + "The default element type argument to toml::array::resize may not be toml::node_view."); + + if (!new_size) + clear(); + else if (new_size > elems_.size()) + insert(cend(), new_size - elems_.size(), static_cast(default_init_val), default_init_flags); + else + truncate(new_size); } + /// @} + + /// \name Erasure + /// @{ + + /// \brief Removes the specified element from the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2, 3 }; + /// std::cout << arr << "\n"; + /// + /// arr.erase(arr.cbegin() + 1); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2, 3 ] + /// [ 1, 3 ] + /// \eout + /// + /// \param pos Iterator to the element being erased. + /// + /// \returns Iterator to the first element immediately following the removed element. + TOML_API + iterator erase(const_iterator pos) noexcept; + + /// \brief Removes the elements in the range [first, last) from the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, "bad", "karma" 2 }; + /// std::cout << arr << "\n"; + /// + /// arr.erase(arr.cbegin() + 1, arr.cbegin() + 3); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 'bad', 'karma', 3 ] + /// [ 1, 3 ] + /// \eout + /// + /// \param first Iterator to the first element being erased. + /// \param last Iterator to the one-past-the-last element being erased. + /// + /// \returns Iterator to the first element immediately following the last removed element. + TOML_API + iterator erase(const_iterator first, const_iterator last) noexcept; + + /// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself. + /// + /// \detail \cpp + /// + /// auto arr = toml::array{ 1, 2, toml::array{ 3, 4, toml::array{ 5 } }, 6, toml::array{} }; + /// std::cout << arr << "\n"; + /// + /// arr.flatten(); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2, [ 3, 4, [ 5 ] ], 6, [] ] + /// [ 1, 2, 3, 4, 5, 6 ] + /// \eout + /// + /// \remarks Arrays inside child tables are not flattened. + /// + /// \returns A reference to the array. + TOML_API + array& flatten() &; + + /// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself (rvalue overload). + /// + /// \returns An rvalue reference to the array. + array&& flatten() && + { + return static_cast(this->flatten()); + } + + /// \brief Removes empty child arrays and tables. + /// + /// \detail \cpp + /// + /// auto arr = toml::array{ 1, 2, toml::array{ }, toml::array{ 3, toml::array{ } }, 4 }; + /// std::cout << arr << "\n"; + /// + /// arr.prune(true); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2, [], [ 3, [] ], 4 ] + /// [ 1, 2, [ 3 ], 4 ] + /// \eout + /// + /// \param recursive Should child arrays and tables themselves be pruned? + /// + /// \returns A reference to the array. + TOML_API + array& prune(bool recursive = true) & noexcept; + + /// \brief Removes empty child arrays and tables (rvalue overload). + /// + /// \param recursive Should child arrays and tables themselves be pruned? + /// + /// \returns An rvalue reference to the array. + array&& prune(bool recursive = true) && noexcept + { + return static_cast(this->prune(recursive)); + } + + /// \brief Removes the last element from the array. + TOML_API + void pop_back() noexcept; + + /// \brief Removes all elements from the array. + TOML_API + void clear() noexcept; + + /// @} + + /// \name Insertion and Emplacement + /// @{ + /// \brief Inserts a new element at a specific position in the array. /// /// \detail \cpp @@ -858,7 +1075,6 @@ TOML_NAMESPACE_START /// arr.insert(arr.cbegin() + 1, "two"); /// arr.insert(arr.cend(), toml::array{ 4, 5 }); /// std::cout << arr << "\n"; - /// /// \ecpp /// /// \out @@ -873,10 +1089,10 @@ TOML_NAMESPACE_START /// /// \returns \conditional_return{Valid input} /// An iterator to the newly-inserted element. - /// \conditional_return{Input is an empty toml::node_view} + /// \conditional_return{Input is a null toml::node_view} /// end() /// - /// \attention The return value will always be `end()` if the input value was an empty toml::node_view, + /// \attention The return value will always be `end()` if the input value was a null toml::node_view, /// because no insertion can take place. This is the only circumstance in which this can occur. template iterator insert(const_iterator pos, ElemType&& val, value_flags flags = preserve_source_value_flags) @@ -886,7 +1102,7 @@ TOML_NAMESPACE_START if (!val) return end(); } - return elems_.emplace(pos, impl::make_node(static_cast(val), flags)); + return insert_at(pos, impl::make_node(static_cast(val), flags)); } /// \brief Repeatedly inserts a new element starting at a specific position in the array. @@ -898,7 +1114,6 @@ TOML_NAMESPACE_START /// }; /// arr.insert(arr.cbegin() + 1, 3, "honk"); /// std::cout << arr << "\n"; - /// /// \ecpp /// /// \out @@ -922,10 +1137,10 @@ TOML_NAMESPACE_START /// An iterator to the newly-inserted element. /// \conditional_return{count == 0} /// A copy of pos - /// \conditional_return{Input is an empty toml::node_view} + /// \conditional_return{Input is a null toml::node_view} /// end() /// - /// \attention The return value will always be `end()` if the input value was an empty toml::node_view, + /// \attention The return value will always be `end()` if the input value was a null toml::node_view, /// because no insertion can take place. This is the only circumstance in which this can occur. template iterator insert(const_iterator pos, @@ -969,7 +1184,7 @@ TOML_NAMESPACE_START /// An iterator to the first newly-inserted element. /// \conditional_return{first >= last} /// A copy of pos - /// \conditional_return{All objects in the range were empty toml::node_views} + /// \conditional_return{All objects in the range were null toml::node_views} /// A copy of pos template iterator insert(const_iterator pos, Iter first, Iter last, value_flags flags = preserve_source_value_flags) @@ -1020,7 +1235,7 @@ TOML_NAMESPACE_START /// An iterator to the first newly-inserted element. /// \conditional_return{Input list is empty} /// A copy of pos - /// \conditional_return{All objects in the list were empty toml::node_views} + /// \conditional_return{All objects in the list were null toml::node_views} /// A copy of pos template iterator insert(const_iterator pos, @@ -1038,7 +1253,6 @@ TOML_NAMESPACE_START /// //add a string using std::string's substring constructor /// arr.emplace(arr.cbegin() + 1, "this is not a drill"sv, 14, 5); /// std::cout << arr << "\n"; - /// /// \ecpp /// /// \out @@ -1061,7 +1275,7 @@ TOML_NAMESPACE_START static_assert((impl::is_native || impl::is_one_of)&&!impl::is_cvref, "Emplacement type parameter must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - return elems_.emplace(pos, new impl::wrap_node{ static_cast(args)... }); + return insert_at(pos, impl::node_ptr{ new impl::wrap_node{ static_cast(args)... } }); } /// \brief Replaces the element at a specific position in the array with a different value. @@ -1086,10 +1300,10 @@ TOML_NAMESPACE_START /// /// \returns \conditional_return{Valid input} /// An iterator to the replaced element. - /// \conditional_return{Input is an empty toml::node_view} + /// \conditional_return{Input is a null toml::node_view} /// end() /// - /// \attention The return value will always be `end()` if the input value was an empty toml::node_view, + /// \attention The return value will always be `end()` if the input value was a null toml::node_view, /// because no replacement can take place. This is the only circumstance in which this can occur. template iterator replace(const_iterator pos, ElemType&& val, value_flags flags = preserve_source_value_flags) @@ -1107,128 +1321,6 @@ TOML_NAMESPACE_START return it; } - /// \brief Removes the specified element from the array. - /// - /// \detail \cpp - /// auto arr = toml::array{ 1, 2, 3 }; - /// std::cout << arr << "\n"; - /// - /// arr.erase(arr.cbegin() + 1); - /// std::cout << arr << "\n"; - /// - /// \ecpp - /// - /// \out - /// [ 1, 2, 3 ] - /// [ 1, 3 ] - /// \eout - /// - /// \param pos Iterator to the element being erased. - /// - /// \returns Iterator to the first element immediately following the removed element. - iterator erase(const_iterator pos) noexcept - { - return elems_.erase(pos); - } - - /// \brief Removes the elements in the range [first, last) from the array. - /// - /// \detail \cpp - /// auto arr = toml::array{ 1, "bad", "karma" 2 }; - /// std::cout << arr << "\n"; - /// - /// arr.erase(arr.cbegin() + 1, arr.cbegin() + 3); - /// std::cout << arr << "\n"; - /// - /// \ecpp - /// - /// \out - /// [ 1, 'bad', 'karma', 3 ] - /// [ 1, 3 ] - /// \eout - /// - /// \param first Iterator to the first element being erased. - /// \param last Iterator to the one-past-the-last element being erased. - /// - /// \returns Iterator to the first element immediately following the last removed element. - iterator erase(const_iterator first, const_iterator last) noexcept - { - return elems_.erase(first, last); - } - - /// \brief Resizes the array. - /// - /// \detail \godbolt{W5zqx3} - /// - /// \cpp - /// auto arr = toml::array{ 1, 2, 3 }; - /// std::cout << arr << "\n"; - /// - /// arr.resize(6, 42); - /// std::cout << arr << "\n"; - /// - /// arr.resize(2, 0); - /// std::cout << arr << "\n"; - /// - /// \ecpp - /// - /// \out - /// [ 1, 2, 3 ] - /// [ 1, 2, 3, 42, 42, 42 ] - /// [ 1, 2 ] - /// \eout - /// - /// \tparam ElemType toml::node, toml::table, toml::array, or a native TOML value type - /// (or a type promotable to one). - /// - /// \param new_size The number of elements the array will have after resizing. - /// \param default_init_val The node or value used to initialize new elements if the array needs to grow. - /// \param default_init_flags Value flags to apply to new values created if new elements are created by growing. - template - void resize(size_t new_size, - ElemType&& default_init_val, - value_flags default_init_flags = preserve_source_value_flags) - { - static_assert(!is_node_view, - "The default element type argument to toml::array::resize may not be toml::node_view."); - - if (!new_size) - elems_.clear(); - else if (new_size < elems_.size()) - elems_.resize(new_size); - else if (new_size > elems_.size()) - insert(cend(), new_size - elems_.size(), static_cast(default_init_val), default_init_flags); - } - - /// \brief Shrinks the array to the given size. - /// - /// \detail \godbolt{rxEzK5} - /// - /// \cpp - /// auto arr = toml::array{ 1, 2, 3 }; - /// std::cout << arr << "\n"; - /// - /// arr.truncate(5); // no-op - /// std::cout << arr << "\n"; - /// - /// arr.truncate(1); - /// std::cout << arr << "\n"; - /// - /// \ecpp - /// - /// \out - /// [ 1, 2, 3 ] - /// [ 1, 2, 3 ] - /// [ 1] - /// \eout - /// - /// \remarks Does nothing if the requested size is larger than or equal to the current size. - void truncate(size_t new_size) - { - if (new_size < elems_.size()) - elems_.resize(new_size); - } - /// \brief Appends a new element to the end of the array. /// /// \detail \cpp @@ -1237,7 +1329,6 @@ TOML_NAMESPACE_START /// arr.push_back(4.0); /// arr.push_back(toml::array{ 5, "six"sv }); /// std::cout << arr << "\n"; - /// /// \ecpp /// /// \out @@ -1248,7 +1339,7 @@ TOML_NAMESPACE_START /// \param val The node or value being added. /// \param flags Value flags to apply to new values. /// - /// \attention No insertion takes place if the input value is an empty toml::node_view. + /// \attention No insertion takes place if the input value is a null toml::node_view. /// This is the only circumstance in which this can occur. template void push_back(ElemType&& val, value_flags flags = preserve_source_value_flags) @@ -1262,7 +1353,6 @@ TOML_NAMESPACE_START /// auto arr = toml::array{ 1, 2 }; /// arr.emplace_back(3, "four"sv); /// std::cout << arr << "\n"; - /// /// \ecpp /// /// \out @@ -1296,86 +1386,17 @@ TOML_NAMESPACE_START if constexpr (moving_node_ptr) { - elems_.emplace_back(static_cast(args)...); + insert_at_back(static_cast(args)...); return *elems_.back(); } else { - auto nde = new impl::wrap_node{ static_cast(args)... }; - elems_.emplace_back(nde); - return *nde; + auto ptr = new impl::wrap_node{ static_cast(args)... }; + insert_at_back(impl::node_ptr{ ptr }); + return *ptr; } } - /// \brief Removes the last element from the array. - void pop_back() noexcept - { - elems_.pop_back(); - } - - /// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself. - /// - /// \detail \cpp - /// - /// auto arr = toml::array{ 1, 2, toml::array{ 3, 4, toml::array{ 5 } }, 6, toml::array{} }; - /// std::cout << arr << "\n"; - /// - /// arr.flatten(); - /// std::cout << arr << "\n"; - /// - /// \ecpp - /// - /// \out - /// [ 1, 2, [ 3, 4, [ 5 ] ], 6, [] ] - /// [ 1, 2, 3, 4, 5, 6 ] - /// \eout - /// - /// \remarks Arrays inside child tables are not flattened. - /// - /// \returns A reference to the array. - TOML_API - array& flatten() &; - - /// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself (rvalue overload). - /// - /// \returns An rvalue reference to the array. - array&& flatten() && - { - return static_cast(this->flatten()); - } - - /// \brief Removes empty child arrays and tables. - /// - /// \detail \cpp - /// - /// auto arr = toml::array{ 1, 2, toml::array{ }, toml::array{ 3, toml::array{ } }, 4 }; - /// std::cout << arr << "\n"; - /// - /// arr.prune(); - /// std::cout << arr << "\n"; - /// \ecpp - /// - /// \out - /// [ 1, 2, [], [ 3, [] ], 4 ] - /// [ 1, 2, [ 3 ], 4 ] - /// \eout - /// - /// \param recursive Should child arrays and tables themselves be pruned? - /// - /// \returns A reference to the array. - TOML_API - array& prune(bool recursive = true) & noexcept; - - /// \brief Removes empty child arrays and tables (rvalue overload). - /// - /// \param recursive Should child arrays and tables themselves be pruned? - /// - /// \returns An rvalue reference to the array. - array&& prune(bool recursive = true) && noexcept - { - return static_cast(this->prune(recursive)); - } - /// @} private: diff --git a/include/toml++/impl/array.inl b/include/toml++/impl/array.inl index 255abfc..7ae5c6e 100644 --- a/include/toml++/impl/array.inl +++ b/include/toml++/impl/array.inl @@ -16,6 +16,37 @@ TOML_NAMESPACE_START { + TOML_EXTERNAL_LINKAGE + array::array(const impl::array_init_elem* b, const impl::array_init_elem* e) + { +#if TOML_LIFETIME_HOOKS + TOML_TABLE_CREATED; +#endif + + TOML_ASSERT_ASSUME(b); + TOML_ASSERT_ASSUME(e); + TOML_ASSERT_ASSUME(b <= e); + + if TOML_UNLIKELY(b == e) + return; + + size_t cap{}; + for (auto it = b; it != e; it++) + { + if (it->value) + cap++; + } + if TOML_UNLIKELY(!cap) + return; + + elems_.reserve(cap); + for (; b != e; b++) + { + if (b->value) + elems_.push_back(std::move(b->value)); + } + } + TOML_EXTERNAL_LINKAGE array::array(const array& other) // : node(other) @@ -32,7 +63,7 @@ TOML_NAMESPACE_START TOML_EXTERNAL_LINKAGE array::array(array && other) noexcept // : node(std::move(other)), - elems_{ std::move(other.elems_) } + elems_(std::move(other.elems_)) { #if TOML_LIFETIME_HOOKS TOML_ARRAY_CREATED; @@ -68,7 +99,7 @@ TOML_NAMESPACE_START void array::preinsertion_resize(size_t idx, size_t count) { TOML_ASSERT(idx <= elems_.size()); - TOML_ASSERT(count >= 1u); + TOML_ASSERT_ASSUME(count >= 1u); const auto old_size = elems_.size(); const auto new_size = old_size + count; const auto inserting_at_end = idx == old_size; @@ -80,6 +111,18 @@ TOML_NAMESPACE_START } } + TOML_EXTERNAL_LINKAGE + void array::insert_at_back(impl::node_ptr && elem) + { + elems_.push_back(std::move(elem)); + } + + TOML_API + array::vector_iterator array::insert_at(const_vector_iterator pos, impl::node_ptr && elem) + { + return elems_.insert(pos, std::move(elem)); + } + TOML_EXTERNAL_LINKAGE bool array::is_homogeneous(node_type ntype) const noexcept { @@ -136,34 +179,41 @@ TOML_NAMESPACE_START #else auto n = get(index); - TOML_ASSERT(n && "element index not found in array!"); + TOML_ASSERT_ASSUME(n && "element index not found in array!"); return *n; #endif } TOML_EXTERNAL_LINKAGE - bool array::equal(const array& lhs, const array& rhs) noexcept + void array::reserve(size_t new_capacity) { - if (&lhs == &rhs) - return true; - if (lhs.elems_.size() != rhs.elems_.size()) - return false; - for (size_t i = 0, e = lhs.elems_.size(); i < e; i++) - { - const auto lhs_type = lhs.elems_[i]->type(); - const node& rhs_ = *rhs.elems_[i]; - const auto rhs_type = rhs_.type(); - if (lhs_type != rhs_type) - return false; + elems_.reserve(new_capacity); + } - const bool equal = lhs.elems_[i]->visit( - [&](const auto& lhs_) noexcept - { return lhs_ == *reinterpret_cast*>(&rhs_); }); - if (!equal) - return false; - } - return true; + TOML_EXTERNAL_LINKAGE + void array::shrink_to_fit() + { + elems_.shrink_to_fit(); + } + + TOML_EXTERNAL_LINKAGE + void array::truncate(size_t new_size) + { + if (new_size < elems_.size()) + elems_.resize(new_size); + } + + TOML_EXTERNAL_LINKAGE + array::iterator array::erase(const_iterator pos) noexcept + { + return elems_.erase(pos); + } + + TOML_EXTERNAL_LINKAGE + array::iterator array::erase(const_iterator first, const_iterator last) noexcept + { + return elems_.erase(first, last); } TOML_EXTERNAL_LINKAGE @@ -270,6 +320,42 @@ TOML_NAMESPACE_START return *this; } + + TOML_EXTERNAL_LINKAGE + void array::pop_back() noexcept + { + elems_.pop_back(); + } + + TOML_EXTERNAL_LINKAGE + void array::clear() noexcept + { + elems_.clear(); + } + + TOML_EXTERNAL_LINKAGE + bool array::equal(const array& lhs, const array& rhs) noexcept + { + if (&lhs == &rhs) + return true; + if (lhs.elems_.size() != rhs.elems_.size()) + return false; + for (size_t i = 0, e = lhs.elems_.size(); i < e; i++) + { + const auto lhs_type = lhs.elems_[i]->type(); + const node& rhs_ = *rhs.elems_[i]; + const auto rhs_type = rhs_.type(); + if (lhs_type != rhs_type) + return false; + + const bool equal = lhs.elems_[i]->visit( + [&](const auto& lhs_) noexcept + { return lhs_ == *reinterpret_cast*>(&rhs_); }); + if (!equal) + return false; + } + return true; + } } TOML_NAMESPACE_END; diff --git a/include/toml++/impl/date_time.h b/include/toml++/impl/date_time.h index dd5a6e2..60ea6ce 100644 --- a/include/toml++/impl/date_time.h +++ b/include/toml++/impl/date_time.h @@ -229,7 +229,6 @@ TOML_NAMESPACE_START /// std::cout << toml::time_offset{ -2, 30 } << "\n"; /// std::cout << toml::time_offset{ -2, -30 } << "\n"; /// std::cout << toml::time_offset{ 0, 0 } << "\n"; - /// /// \ecpp /// /// \out diff --git a/include/toml++/impl/formatter.inl b/include/toml++/impl/formatter.inl index 4b67b85..a43db79 100644 --- a/include/toml++/impl/formatter.inl +++ b/include/toml++/impl/formatter.inl @@ -37,7 +37,7 @@ TOML_IMPL_NAMESPACE_START constants_{ &constants }, config_{ config } { - TOML_ASSERT(source_ != nullptr); + TOML_ASSERT_ASSUME(source_); config_.flags = (config_.flags | constants_->mandatory_flags) & ~constants_->ignored_flags; diff --git a/include/toml++/impl/forward_declarations.h b/include/toml++/impl/forward_declarations.h index 37331c1..cdceee3 100644 --- a/include/toml++/impl/forward_declarations.h +++ b/include/toml++/impl/forward_declarations.h @@ -395,6 +395,62 @@ TOML_IMPL_NAMESPACE_START && !is_wide_string; + template + struct copy_ref_; + template + using copy_ref = typename copy_ref_::type; + + template + struct copy_ref_ + { + using type = Dest; + }; + + template + struct copy_ref_ + { + using type = std::add_lvalue_reference_t; + }; + + template + struct copy_ref_ + { + using type = std::add_rvalue_reference_t; + }; + + template + struct copy_cv_; + template + using copy_cv = typename copy_cv_::type; + + template + struct copy_cv_ + { + using type = Dest; + }; + + template + struct copy_cv_ + { + using type = std::add_const_t; + }; + + template + struct copy_cv_ + { + using type = std::add_volatile_t; + }; + + template + struct copy_cv_ + { + using type = std::add_cv_t; + }; + + template + using copy_cvref = + copy_ref, std::remove_reference_t>, Dest>, Src>; + template inline constexpr bool dependent_false = false; @@ -416,21 +472,21 @@ TOML_IMPL_NAMESPACE_START static constexpr auto type = node_type::none; }; + template + struct value_traits : value_traits + {}; + template + struct value_traits : value_traits + {}; + template + struct value_traits : value_traits + {}; template struct value_traits : value_traits {}; template struct value_traits : value_traits {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; // integer value_traits specializations - standard types template @@ -747,6 +803,21 @@ TOML_IMPL_NAMESPACE_START { using type = T; }; + template + struct node_wrapper + { + using type = std::add_const_t::type>; + }; + template + struct node_wrapper + { + using type = std::add_volatile_t::type>; + }; + template + struct node_wrapper + { + using type = std::add_const_t::type>>; + }; template <> struct node_wrapper { @@ -783,7 +854,7 @@ TOML_IMPL_NAMESPACE_START using type = value; }; template - using wrap_node = typename node_wrapper::type; + using wrap_node = typename node_wrapper>::type; // nodes => native value types template @@ -797,7 +868,22 @@ TOML_IMPL_NAMESPACE_START using type = T; }; template - using unwrap_node = typename node_unwrapper::type; + struct node_unwrapper> + { + using type = std::add_const_t; + }; + template + struct node_unwrapper> + { + using type = std::add_volatile_t; + }; + template + struct node_unwrapper> + { + using type = std::add_volatile_t>; + }; + template + using unwrap_node = typename node_unwrapper>::type; template struct node_type_getter diff --git a/include/toml++/impl/key.h b/include/toml++/impl/key.h index 1c16082..e630394 100644 --- a/include/toml++/impl/key.h +++ b/include/toml++/impl/key.h @@ -36,39 +36,46 @@ TOML_NAMESPACE_START public: /// \brief Default constructor. + TOML_NODISCARD_CTOR key() noexcept = default; /// \brief Constructs a key from a string view and source region. + TOML_NODISCARD_CTOR explicit key(std::string_view k, source_region&& src = {}) // : key_{ k }, source_{ std::move(src) } {} /// \brief Constructs a key from a string view and source region. + TOML_NODISCARD_CTOR explicit key(std::string_view k, const source_region& src) // : key_{ k }, source_{ src } {} /// \brief Constructs a key from a string and source region. + TOML_NODISCARD_CTOR explicit key(std::string&& k, source_region&& src = {}) noexcept // : key_{ std::move(k) }, source_{ std::move(src) } {} /// \brief Constructs a key from a string and source region. + TOML_NODISCARD_CTOR explicit key(std::string&& k, const source_region& src) noexcept // : key_{ std::move(k) }, source_{ src } {} /// \brief Constructs a key from a c-string and source region. + TOML_NODISCARD_CTOR explicit key(const char* k, source_region&& src = {}) // : key_{ k }, source_{ std::move(src) } {} /// \brief Constructs a key from a c-string view and source region. + TOML_NODISCARD_CTOR explicit key(const char* k, const source_region& src) // : key_{ k }, source_{ src } @@ -79,6 +86,7 @@ TOML_NAMESPACE_START /// \brief Constructs a key from a wide string view and source region. /// /// \availability This constructor is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + TOML_NODISCARD_CTOR explicit key(std::wstring_view k, source_region&& src = {}) // : key_{ impl::narrow(k) }, source_{ std::move(src) } @@ -87,6 +95,7 @@ TOML_NAMESPACE_START /// \brief Constructs a key from a wide string and source region. /// /// \availability This constructor is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + TOML_NODISCARD_CTOR explicit key(std::wstring_view k, const source_region& src) // : key_{ impl::narrow(k) }, source_{ src } diff --git a/include/toml++/impl/make_node.h b/include/toml++/impl/make_node.h index 003c3d4..93c3051 100644 --- a/include/toml++/impl/make_node.h +++ b/include/toml++/impl/make_node.h @@ -22,7 +22,7 @@ TOML_IMPL_NAMESPACE_START // arrays + tables - invoke copy/move ctor if constexpr (is_one_of) { - return new unwrapped_type{ static_cast(val) }; + return new unwrapped_type(static_cast(val)); } // values diff --git a/include/toml++/impl/node.h b/include/toml++/impl/node.h index 522e6b7..d9600e4 100644 --- a/include/toml++/impl/node.h +++ b/include/toml++/impl/node.h @@ -27,20 +27,41 @@ TOML_NAMESPACE_START TOML_NODISCARD decltype(auto) get_value_exact() const noexcept(impl::value_retrieval_is_nothrow); + template + using ref_type_ = std::conditional_t< // + std::is_reference_v, // + impl::copy_ref, std::remove_reference_t>, T>, // + impl::copy_cvref, N> // + >; + + template + using ref_type = std::conditional_t< // + std::is_reference_v, // + ref_type_, // + ref_type_> // + >; + template TOML_PURE_GETTER - static decltype(auto) do_ref(N&& n) noexcept + static ref_type do_ref(N&& n) noexcept { - using type = impl::unwrap_node; - static_assert((impl::is_native || impl::is_one_of)&&!impl::is_cvref, + using unwrapped_type = impl::unwrap_node; + static_assert(toml::is_value || toml::is_container, "The template type argument of node::ref() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - TOML_ASSERT( - n.template is() - && "template type argument T provided to toml::node::ref() didn't match the node's actual type"); - if constexpr (impl::is_native) - return static_cast(n).template ref_cast().get(); + + TOML_ASSERT_ASSUME( + n.template is() + && "template type argument provided to toml::node::ref() didn't match the node's actual type"); + + using node_ref = std::remove_volatile_t>&; + using val_type = std::remove_volatile_t; + using out_ref = ref_type; + static_assert(std::is_reference_v); + + if constexpr (toml::is_value) + return static_cast(const_cast(n).template ref_cast().get()); else - return static_cast(n).template ref_cast(); + return static_cast(const_cast(n).template ref_cast()); } protected: @@ -58,29 +79,55 @@ TOML_NAMESPACE_START TOML_API node& operator=(node&&) noexcept; + template + using ref_cast_type_ = std::conditional_t< // + std::is_reference_v, // + impl::copy_ref, std::remove_reference_t>, T>, // + impl::copy_cvref, N> // + >; + + template + using ref_cast_type = std::conditional_t< // + std::is_reference_v, // + ref_cast_type_, // + ref_cast_type_> // + >; + template TOML_PURE_INLINE_GETTER - impl::wrap_node& ref_cast() & noexcept + ref_cast_type ref_cast() & noexcept { - return *reinterpret_cast*>(this); + using out_ref = ref_cast_type; + using out_type = std::remove_reference_t; + return static_cast(*reinterpret_cast(this)); } template TOML_PURE_INLINE_GETTER - impl::wrap_node&& ref_cast() && noexcept + ref_cast_type ref_cast() && noexcept { - return std::move(*reinterpret_cast*>(this)); + using out_ref = ref_cast_type; + using out_type = std::remove_reference_t; + return static_cast(*reinterpret_cast(this)); } template TOML_PURE_INLINE_GETTER - const impl::wrap_node& ref_cast() const& noexcept + ref_cast_type ref_cast() const& noexcept { - return *reinterpret_cast*>(this); + using out_ref = ref_cast_type; + using out_type = std::remove_reference_t; + return static_cast(*reinterpret_cast(this)); } - template - using ref_cast_type = decltype(std::declval().template ref_cast()); + template + TOML_PURE_INLINE_GETTER + ref_cast_type ref_cast() const&& noexcept + { + using out_ref = ref_cast_type; + using out_type = std::remove_reference_t; + return static_cast(*reinterpret_cast(this)); + } /// \endcond @@ -137,7 +184,6 @@ TOML_NAMESPACE_START /// std::cout << "all floats: "sv << arr.is_homogeneous(toml::node_type::floating_point) << "\n"; /// std::cout << "all arrays: "sv << arr.is_homogeneous(toml::node_type::array) << "\n"; /// std::cout << "all ints: "sv << arr.is_homogeneous(toml::node_type::integer) << "\n"; - /// /// \ecpp /// /// \out @@ -167,7 +213,6 @@ TOML_NAMESPACE_START /// std::cout << "all doubles: "sv << arr.is_homogeneous() << "\n"; /// std::cout << "all arrays: "sv << arr.is_homogeneous() << "\n"; /// std::cout << "all integers: "sv << arr.is_homogeneous() << "\n"; - /// /// \ecpp /// /// \out @@ -190,13 +235,13 @@ TOML_NAMESPACE_START TOML_PURE_GETTER bool is_homogeneous() const noexcept { - using type = impl::unwrap_node; - static_assert( - std::is_void_v< - type> || ((impl::is_native || impl::is_one_of)&&!impl::is_cvref), - "The template type argument of node::is_homogeneous() must be void or one " - "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - return is_homogeneous(impl::node_type_of); + using unwrapped_type = impl::unwrap_node>; + static_assert(std::is_void_v // + || (toml::is_value || toml::is_container), + "The template type argument of node::is_homogeneous() must be void or one " + "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + return is_homogeneous(impl::node_type_of); } /// \brief Returns the node's type identifier. @@ -260,27 +305,27 @@ TOML_NAMESPACE_START TOML_PURE_INLINE_GETTER bool is() const noexcept { - using type = impl::unwrap_node; - static_assert((impl::is_native || impl::is_one_of)&&!impl::is_cvref, + using unwrapped_type = impl::unwrap_node>; + static_assert(toml::is_value || toml::is_container, "The template type argument of node::is() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) return is_table(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_array(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_string(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_integer(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_floating_point(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_boolean(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_date(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_time(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_date_time(); } @@ -376,7 +421,6 @@ TOML_NAMESPACE_START /// toml::value* int_value2 = node->as>(); /// if (int_value2) /// std::cout << "Node is a value\n"; - /// /// \ecpp /// /// \tparam T The node type or TOML value type to cast to. @@ -386,27 +430,27 @@ TOML_NAMESPACE_START TOML_PURE_INLINE_GETTER impl::wrap_node* as() noexcept { - using type = impl::unwrap_node; - static_assert((impl::is_native || impl::is_one_of)&&!impl::is_cvref, + using unwrapped_type = impl::unwrap_node>; + static_assert(toml::is_value || toml::is_container, "The template type argument of node::as() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) return as_table(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_array(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_string(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_integer(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_floating_point(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_boolean(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_date(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_time(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_date_time(); } @@ -415,27 +459,27 @@ TOML_NAMESPACE_START TOML_PURE_INLINE_GETTER const impl::wrap_node* as() const noexcept { - using type = impl::unwrap_node; - static_assert((impl::is_native || impl::is_one_of)&&!impl::is_cvref, + using unwrapped_type = impl::unwrap_node>; + static_assert(toml::is_value || toml::is_container, "The template type argument of node::as() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) return as_table(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_array(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_string(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_integer(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_floating_point(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_boolean(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_date(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_time(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_date_time(); } @@ -621,6 +665,23 @@ TOML_NAMESPACE_START /// \brief Gets a raw reference to a value node's underlying data. /// + /// \note Providing explicit ref qualifiers acts as an explicit ref-category cast. Providing + /// explicit cv-ref qualifiers 'merges' them with whatever the cv qualification of the node is. Examples: + /// | node | T | return type | + /// |-------------|------------------------|------------------------------| + /// | node& | std::string | std::string& | + /// | node& | std::string& | std::string& | + /// | node& | std::string&& | std::string&& | + /// | node&& | std::string | std::string&& | + /// | node&& | std::string& | std::string& | + /// | node&& | std::string&& | std::string&& | + /// | const node& | std::string | const std::string& | + /// | const node& | std::string& | const std::string& | + /// | const node& | std::string&& | const std::string&& | + /// | const node& | volatile std::string | const volatile std::string& | + /// | const node& | volatile std::string& | const volatile std::string& | + /// | const node& | volatile std::string&& | const volatile std::string&& | + /// /// \warning This function is dangerous if used carelessly and **WILL** break your code if the /// chosen value type doesn't match the node's actual type. In debug builds an assertion /// will fire when invalid accesses are attempted: \cpp @@ -632,7 +693,6 @@ TOML_NAMESPACE_START /// /// int64_t& min_ref = tbl.get("min")->ref(); // matching type /// double& max_ref = tbl.get("max")->ref(); // mismatched type, hits assert() - /// /// \ecpp /// /// \tparam T One of the TOML value types. @@ -640,7 +700,7 @@ TOML_NAMESPACE_START /// \returns A reference to the underlying data. template TOML_PURE_GETTER - impl::unwrap_node& ref() & noexcept + decltype(auto) ref() & noexcept { return do_ref(*this); } @@ -648,7 +708,7 @@ TOML_NAMESPACE_START /// \brief Gets a raw reference to a value node's underlying data (rvalue overload). template TOML_PURE_GETTER - impl::unwrap_node&& ref() && noexcept + decltype(auto) ref() && noexcept { return do_ref(std::move(*this)); } @@ -656,11 +716,19 @@ TOML_NAMESPACE_START /// \brief Gets a raw reference to a value node's underlying data (const lvalue overload). template TOML_PURE_GETTER - const impl::unwrap_node& ref() const& noexcept + decltype(auto) ref() const& noexcept { return do_ref(*this); } + /// \brief Gets a raw reference to a value node's underlying data (const rvalue overload). + template + TOML_PURE_GETTER + decltype(auto) ref() const&& noexcept + { + return do_ref(std::move(*this)); + } + /// @} /// \name Metadata @@ -681,7 +749,7 @@ TOML_NAMESPACE_START // clang-format off template - static constexpr bool can_visit = std::is_invocable_v>; + static constexpr bool can_visit = std::is_invocable_v>; template static constexpr bool can_visit_any = @@ -710,7 +778,7 @@ TOML_NAMESPACE_START template static constexpr bool visit_is_nothrow_one = !can_visit - || std::is_nothrow_invocable_v>; + || std::is_nothrow_invocable_v>; template static constexpr bool visit_is_nothrow = @@ -729,7 +797,7 @@ TOML_NAMESPACE_START template > struct visit_return_type_ { - using type = decltype(std::declval()(std::declval>())); + using type = decltype(std::declval()(std::declval>())); }; template struct visit_return_type_ @@ -856,7 +924,6 @@ TOML_NAMESPACE_START /// else /// throw std::exception{ "Expected string or integer" }; /// }); - /// /// \ecpp /// /// \tparam Func A callable type invocable with one or more of the @@ -888,6 +955,13 @@ TOML_NAMESPACE_START return do_visit(*this, static_cast(visitor)); } + /// \brief Invokes a visitor on the node based on the node's concrete type (const rvalue overload). + template + decltype(auto) visit(Func&& visitor) const&& noexcept(visit_is_nothrow) + { + return do_visit(static_cast(*this), static_cast(visitor)); + } + /// @} /// \name Node views diff --git a/include/toml++/impl/node_view.h b/include/toml++/impl/node_view.h index 60d33c0..650c7f1 100644 --- a/include/toml++/impl/node_view.h +++ b/include/toml++/impl/node_view.h @@ -225,7 +225,7 @@ TOML_NAMESPACE_START TOML_PURE_GETTER bool is() const noexcept { - return node_ ? node_->template is() : false; + return node_ ? node_->template is>>() : false; } /// \brief Checks if the viewed node contains values/elements of only one type. @@ -276,7 +276,6 @@ TOML_NAMESPACE_START /// std::cout << "all floats: "sv << cfg["arr"].is_homogeneous(toml::node_type::floating_point) << "\n"; /// std::cout << "all arrays: "sv << cfg["arr"].is_homogeneous(toml::node_type::array) << "\n"; /// std::cout << "all ints: "sv << cfg["arr"].is_homogeneous(toml::node_type::integer) << "\n"; - /// /// \ecpp /// /// \out @@ -308,7 +307,6 @@ TOML_NAMESPACE_START /// std::cout << "all doubles: "sv << cfg["arr"].is_homogeneous() << "\n"; /// std::cout << "all arrays: "sv << cfg["arr"].is_homogeneous() << "\n"; /// std::cout << "all integers: "sv << cfg["arr"].is_homogeneous() << "\n"; - /// /// \ecpp /// /// \out @@ -330,7 +328,7 @@ TOML_NAMESPACE_START TOML_PURE_GETTER bool is_homogeneous() const noexcept { - return node_ ? node_->template is_homogeneous>() : false; + return node_ ? node_->template is_homogeneous>>() : false; } /// @} @@ -347,70 +345,70 @@ TOML_NAMESPACE_START /// \see toml::node::as() template TOML_PURE_GETTER - auto as() const noexcept + auto* as() const noexcept { return node_ ? node_->template as() : nullptr; } /// \brief Returns a pointer to the viewed node as a toml::table, if it is one. TOML_PURE_GETTER - auto as_table() const noexcept + auto* as_table() const noexcept { return as(); } /// \brief Returns a pointer to the viewed node as a toml::array, if it is one. TOML_PURE_GETTER - auto as_array() const noexcept + auto* as_array() const noexcept { return as(); } /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. TOML_PURE_GETTER - auto as_string() const noexcept + auto* as_string() const noexcept { return as(); } /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. TOML_PURE_GETTER - auto as_integer() const noexcept + auto* as_integer() const noexcept { return as(); } /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. TOML_PURE_GETTER - auto as_floating_point() const noexcept + auto* as_floating_point() const noexcept { return as(); } /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. TOML_PURE_GETTER - auto as_boolean() const noexcept + auto* as_boolean() const noexcept { return as(); } /// \brief Returns a pointer to the viewed node as a toml::value, if it is one. TOML_PURE_GETTER - auto as_date() const noexcept + auto* as_date() const noexcept { return as(); } /// \brief Returns a pointer to the viewed node as a toml::value
()), table&>); - static_assert(is_same_v().ref
()), table&&>); - static_assert(is_same_v().ref
()), const table&>); - static_assert(is_same_v().ref()), array&>); - static_assert(is_same_v().ref()), array&&>); - static_assert(is_same_v().ref()), const array&>); +#define CHECK_NODE_REF_TYPE(T) \ + static_assert(is_same_v().ref()), T&>); \ + static_assert(is_same_v().ref()), const T&>); \ + static_assert(is_same_v().ref()), volatile T&>); \ + static_assert(is_same_v().ref()), const volatile T&>); \ + static_assert(is_same_v().ref()), T&>); \ + static_assert(is_same_v().ref()), const T&>); \ + static_assert(is_same_v().ref()), volatile T&>); \ + static_assert(is_same_v().ref()), const volatile T&>); \ + static_assert(is_same_v().ref()), T&&>); \ + static_assert(is_same_v().ref()), const T&&>); \ + static_assert(is_same_v().ref()), volatile T&&>); \ + static_assert(is_same_v().ref()), const volatile T&&>); \ + \ + static_assert(is_same_v().ref()), T&&>); \ + static_assert(is_same_v().ref()), const T&&>); \ + static_assert(is_same_v().ref()), volatile T&&>); \ + static_assert(is_same_v().ref()), const volatile T&&>); \ + static_assert(is_same_v().ref()), T&>); \ + static_assert(is_same_v().ref()), const T&>); \ + static_assert(is_same_v().ref()), volatile T&>); \ + static_assert(is_same_v().ref()), const volatile T&>); \ + static_assert(is_same_v().ref()), T&&>); \ + static_assert(is_same_v().ref()), const T&&>); \ + static_assert(is_same_v().ref()), volatile T&&>); \ + static_assert(is_same_v().ref()), const volatile T&&>); \ + \ + static_assert(is_same_v().ref()), const T&>); \ + static_assert(is_same_v().ref()), const T&>); \ + static_assert(is_same_v().ref()), const volatile T&>); \ + static_assert(is_same_v().ref()), const volatile T&>); \ + static_assert(is_same_v().ref()), const T&>); \ + static_assert(is_same_v().ref()), const T&>); \ + static_assert(is_same_v().ref()), const volatile T&>); \ + static_assert(is_same_v().ref()), const volatile T&>); \ + static_assert(is_same_v().ref()), const T&&>); \ + static_assert(is_same_v().ref()), const T&&>); \ + static_assert(is_same_v().ref()), const volatile T&&>); \ + static_assert(is_same_v().ref()), const volatile T&&>); \ + \ + static_assert(is_same_v().ref()), const T&&>); \ + static_assert(is_same_v().ref()), const T&&>); \ + static_assert(is_same_v().ref()), const volatile T&&>); \ + static_assert(is_same_v().ref()), const volatile T&&>); \ + static_assert(is_same_v().ref()), const T&>); \ + static_assert(is_same_v().ref()), const T&>); \ + static_assert(is_same_v().ref()), const volatile T&>); \ + static_assert(is_same_v().ref()), const volatile T&>); \ + static_assert(is_same_v().ref()), const T&&>); \ + static_assert(is_same_v().ref()), const T&&>); \ + static_assert(is_same_v().ref()), const volatile T&&>); \ + static_assert(is_same_v().ref()), const volatile T&&>) + + CHECK_NODE_REF_TYPE(table); + CHECK_NODE_REF_TYPE(array); + CHECK_NODE_REF_TYPE(std::string); + CHECK_NODE_REF_TYPE(int64_t); + CHECK_NODE_REF_TYPE(double); + CHECK_NODE_REF_TYPE(bool); + CHECK_NODE_REF_TYPE(date); + CHECK_NODE_REF_TYPE(time); + CHECK_NODE_REF_TYPE(date_time); + +#define CHECK_NODE_VIEW_REF_TYPE(T) \ + static_assert(is_same_v>().ref()), T&>); \ + static_assert(is_same_v>().ref()), const T&>); \ + static_assert(is_same_v>().ref()), volatile T&>); \ + static_assert(is_same_v>().ref()), const volatile T&>); \ + static_assert(is_same_v>().ref()), T&>); \ + static_assert(is_same_v>().ref()), const T&>); \ + static_assert(is_same_v>().ref()), volatile T&>); \ + static_assert(is_same_v>().ref()), const volatile T&>); \ + static_assert(is_same_v>().ref()), T&&>); \ + static_assert(is_same_v>().ref()), const T&&>); \ + static_assert(is_same_v>().ref()), volatile T&&>); \ + static_assert(is_same_v>().ref()), const volatile T&&>) + + CHECK_NODE_VIEW_REF_TYPE(table); + CHECK_NODE_VIEW_REF_TYPE(array); + CHECK_NODE_VIEW_REF_TYPE(std::string); + CHECK_NODE_VIEW_REF_TYPE(int64_t); + CHECK_NODE_VIEW_REF_TYPE(double); + CHECK_NODE_VIEW_REF_TYPE(bool); + CHECK_NODE_VIEW_REF_TYPE(date); + CHECK_NODE_VIEW_REF_TYPE(time); + CHECK_NODE_VIEW_REF_TYPE(date_time); + +#define CHECK_CONST_NODE_VIEW_REF_TYPE(T) \ + static_assert(is_same_v>().ref()), const T&>); \ + static_assert(is_same_v>().ref()), const T&>); \ + static_assert(is_same_v>().ref()), const volatile T&>); \ + static_assert(is_same_v>().ref()), const volatile T&>); \ + static_assert(is_same_v>().ref()), const T&>); \ + static_assert(is_same_v>().ref()), const T&>); \ + static_assert(is_same_v>().ref()), const volatile T&>); \ + static_assert(is_same_v>().ref()), const volatile T&>); \ + static_assert(is_same_v>().ref()), const T&&>); \ + static_assert(is_same_v>().ref()), const T&&>); \ + static_assert(is_same_v>().ref()), const volatile T&&>); \ + static_assert(is_same_v>().ref()), const volatile T&&>) + + CHECK_CONST_NODE_VIEW_REF_TYPE(table); + CHECK_CONST_NODE_VIEW_REF_TYPE(array); + CHECK_CONST_NODE_VIEW_REF_TYPE(std::string); + CHECK_CONST_NODE_VIEW_REF_TYPE(int64_t); + CHECK_CONST_NODE_VIEW_REF_TYPE(double); + CHECK_CONST_NODE_VIEW_REF_TYPE(bool); + CHECK_CONST_NODE_VIEW_REF_TYPE(date); + CHECK_CONST_NODE_VIEW_REF_TYPE(time); + CHECK_CONST_NODE_VIEW_REF_TYPE(date_time); - static_assert(is_same_v>().ref()), double&>); - static_assert(is_same_v>().ref()), const double&>); - static_assert(is_same_v>().ref>()), double&>); - static_assert(is_same_v>().ref>()), const double&>); - static_assert(is_same_v>().ref
()), table&>); - static_assert(is_same_v>().ref
()), const table&>); - static_assert(is_same_v>().ref()), array&>); - static_assert(is_same_v>().ref()), const array&>); } diff --git a/toml.hpp b/toml.hpp index 5352ff7..18c3a17 100644 --- a/toml.hpp +++ b/toml.hpp @@ -137,7 +137,7 @@ #define TOML_ENABLE_WARNINGS TOML_POP_WARNINGS - #define TOML_ASSUME(cond) __builtin_assume(cond) + #define TOML_ASSUME(expr) __builtin_assume(expr) #define TOML_UNREACHABLE __builtin_unreachable() #define TOML_ATTR(...) __attribute__((__VA_ARGS__)) #if defined(_MSC_VER) // msvc compat mode @@ -267,7 +267,7 @@ #define TOML_ALWAYS_INLINE __forceinline #endif #define TOML_NEVER_INLINE __declspec(noinline) - #define TOML_ASSUME(cond) __assume(cond) + #define TOML_ASSUME(expr) __assume(expr) #define TOML_UNREACHABLE __assume(0) #define TOML_ABSTRACT_BASE __declspec(novtable) #define TOML_EMPTY_BASES __declspec(empty_bases) @@ -603,11 +603,11 @@ #endif #ifndef TOML_ASSUME - #define TOML_ASSUME(cond) (void)0 + #define TOML_ASSUME(expr) static_assert(true) #endif #ifndef TOML_UNREACHABLE - #define TOML_UNREACHABLE TOML_ASSERT(false) + #define TOML_UNREACHABLE TOML_ASSUME(false) #endif #ifndef TOML_FLAGS_ENUM @@ -757,6 +757,8 @@ #define TOML_CONST_INLINE_GETTER TOML_NODISCARD TOML_ALWAYS_INLINE #endif +#define TOML_UNUSED(...) static_cast(__VA_ARGS__) + // SFINAE #if defined(__cpp_concepts) && __cpp_concepts >= 201907 #define TOML_REQUIRES(...) requires(__VA_ARGS__) @@ -866,18 +868,23 @@ #define TOML_INTERNAL_LINKAGE static #endif -TOML_DISABLE_WARNINGS; -#ifndef TOML_ASSERT - #ifdef NDEBUG - #define TOML_ASSERT(expr) static_cast(0) - #else - #ifndef assert - #include - #endif - #define TOML_ASSERT(expr) assert(expr) - #endif +#ifdef NDEBUG + #undef TOML_ASSERT + #define TOML_ASSERT(expr) static_assert(true) +#endif +#ifndef TOML_ASSERT + #ifndef assert + TOML_DISABLE_WARNINGS; + #include + TOML_ENABLE_WARNINGS; + #endif + #define TOML_ASSERT(expr) assert(expr) +#endif +#ifdef NDEBUG + #define TOML_ASSERT_ASSUME(expr) TOML_ASSUME(expr) +#else + #define TOML_ASSERT_ASSUME(expr) TOML_ASSERT(expr) #endif -TOML_ENABLE_WARNINGS; #if TOML_SIMPLE_STATIC_ASSERT_MESSAGES @@ -1311,6 +1318,62 @@ TOML_IMPL_NAMESPACE_START && !is_wide_string; + template + struct copy_ref_; + template + using copy_ref = typename copy_ref_::type; + + template + struct copy_ref_ + { + using type = Dest; + }; + + template + struct copy_ref_ + { + using type = std::add_lvalue_reference_t; + }; + + template + struct copy_ref_ + { + using type = std::add_rvalue_reference_t; + }; + + template + struct copy_cv_; + template + using copy_cv = typename copy_cv_::type; + + template + struct copy_cv_ + { + using type = Dest; + }; + + template + struct copy_cv_ + { + using type = std::add_const_t; + }; + + template + struct copy_cv_ + { + using type = std::add_volatile_t; + }; + + template + struct copy_cv_ + { + using type = std::add_cv_t; + }; + + template + using copy_cvref = + copy_ref, std::remove_reference_t>, Dest>, Src>; + template inline constexpr bool dependent_false = false; @@ -1332,21 +1395,21 @@ TOML_IMPL_NAMESPACE_START static constexpr auto type = node_type::none; }; + template + struct value_traits : value_traits + {}; + template + struct value_traits : value_traits + {}; + template + struct value_traits : value_traits + {}; template struct value_traits : value_traits {}; template struct value_traits : value_traits {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; - template - struct value_traits : value_traits - {}; // integer value_traits specializations - standard types template @@ -1663,6 +1726,21 @@ TOML_IMPL_NAMESPACE_START { using type = T; }; + template + struct node_wrapper + { + using type = std::add_const_t::type>; + }; + template + struct node_wrapper + { + using type = std::add_volatile_t::type>; + }; + template + struct node_wrapper + { + using type = std::add_const_t::type>>; + }; template <> struct node_wrapper { @@ -1699,7 +1777,7 @@ TOML_IMPL_NAMESPACE_START using type = value; }; template - using wrap_node = typename node_wrapper::type; + using wrap_node = typename node_wrapper>::type; // nodes => native value types template @@ -1713,7 +1791,22 @@ TOML_IMPL_NAMESPACE_START using type = T; }; template - using unwrap_node = typename node_unwrapper::type; + struct node_unwrapper> + { + using type = std::add_const_t; + }; + template + struct node_unwrapper> + { + using type = std::add_volatile_t; + }; + template + struct node_unwrapper> + { + using type = std::add_volatile_t>; + }; + template + using unwrap_node = typename node_unwrapper>::type; template struct node_type_getter @@ -2442,20 +2535,41 @@ TOML_NAMESPACE_START TOML_NODISCARD decltype(auto) get_value_exact() const noexcept(impl::value_retrieval_is_nothrow); + template + using ref_type_ = std::conditional_t< // + std::is_reference_v, // + impl::copy_ref, std::remove_reference_t>, T>, // + impl::copy_cvref, N> // + >; + + template + using ref_type = std::conditional_t< // + std::is_reference_v, // + ref_type_, // + ref_type_> // + >; + template TOML_PURE_GETTER - static decltype(auto) do_ref(N&& n) noexcept + static ref_type do_ref(N&& n) noexcept { - using type = impl::unwrap_node; - static_assert((impl::is_native || impl::is_one_of)&&!impl::is_cvref, + using unwrapped_type = impl::unwrap_node; + static_assert(toml::is_value || toml::is_container, "The template type argument of node::ref() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - TOML_ASSERT( - n.template is() - && "template type argument T provided to toml::node::ref() didn't match the node's actual type"); - if constexpr (impl::is_native) - return static_cast(n).template ref_cast().get(); + + TOML_ASSERT_ASSUME( + n.template is() + && "template type argument provided to toml::node::ref() didn't match the node's actual type"); + + using node_ref = std::remove_volatile_t>&; + using val_type = std::remove_volatile_t; + using out_ref = ref_type; + static_assert(std::is_reference_v); + + if constexpr (toml::is_value) + return static_cast(const_cast(n).template ref_cast().get()); else - return static_cast(n).template ref_cast(); + return static_cast(const_cast(n).template ref_cast()); } protected: @@ -2473,29 +2587,55 @@ TOML_NAMESPACE_START TOML_API node& operator=(node&&) noexcept; + template + using ref_cast_type_ = std::conditional_t< // + std::is_reference_v, // + impl::copy_ref, std::remove_reference_t>, T>, // + impl::copy_cvref, N> // + >; + + template + using ref_cast_type = std::conditional_t< // + std::is_reference_v, // + ref_cast_type_, // + ref_cast_type_> // + >; + template TOML_PURE_INLINE_GETTER - impl::wrap_node& ref_cast() & noexcept + ref_cast_type ref_cast() & noexcept { - return *reinterpret_cast*>(this); + using out_ref = ref_cast_type; + using out_type = std::remove_reference_t; + return static_cast(*reinterpret_cast(this)); } template TOML_PURE_INLINE_GETTER - impl::wrap_node&& ref_cast() && noexcept + ref_cast_type ref_cast() && noexcept { - return std::move(*reinterpret_cast*>(this)); + using out_ref = ref_cast_type; + using out_type = std::remove_reference_t; + return static_cast(*reinterpret_cast(this)); } template TOML_PURE_INLINE_GETTER - const impl::wrap_node& ref_cast() const& noexcept + ref_cast_type ref_cast() const& noexcept { - return *reinterpret_cast*>(this); + using out_ref = ref_cast_type; + using out_type = std::remove_reference_t; + return static_cast(*reinterpret_cast(this)); } - template - using ref_cast_type = decltype(std::declval().template ref_cast()); + template + TOML_PURE_INLINE_GETTER + ref_cast_type ref_cast() const&& noexcept + { + using out_ref = ref_cast_type; + using out_type = std::remove_reference_t; + return static_cast(*reinterpret_cast(this)); + } public: TOML_API @@ -2514,13 +2654,13 @@ TOML_NAMESPACE_START TOML_PURE_GETTER bool is_homogeneous() const noexcept { - using type = impl::unwrap_node; - static_assert( - std::is_void_v< - type> || ((impl::is_native || impl::is_one_of)&&!impl::is_cvref), - "The template type argument of node::is_homogeneous() must be void or one " - "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - return is_homogeneous(impl::node_type_of); + using unwrapped_type = impl::unwrap_node>; + static_assert(std::is_void_v // + || (toml::is_value || toml::is_container), + "The template type argument of node::is_homogeneous() must be void or one " + "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + return is_homogeneous(impl::node_type_of); } TOML_PURE_GETTER @@ -2566,27 +2706,27 @@ TOML_NAMESPACE_START TOML_PURE_INLINE_GETTER bool is() const noexcept { - using type = impl::unwrap_node; - static_assert((impl::is_native || impl::is_one_of)&&!impl::is_cvref, + using unwrapped_type = impl::unwrap_node>; + static_assert(toml::is_value || toml::is_container, "The template type argument of node::is() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) return is_table(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_array(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_string(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_integer(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_floating_point(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_boolean(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_date(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_time(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return is_date_time(); } @@ -2648,27 +2788,27 @@ TOML_NAMESPACE_START TOML_PURE_INLINE_GETTER impl::wrap_node* as() noexcept { - using type = impl::unwrap_node; - static_assert((impl::is_native || impl::is_one_of)&&!impl::is_cvref, + using unwrapped_type = impl::unwrap_node>; + static_assert(toml::is_value || toml::is_container, "The template type argument of node::as() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) return as_table(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_array(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_string(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_integer(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_floating_point(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_boolean(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_date(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_time(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_date_time(); } @@ -2676,27 +2816,27 @@ TOML_NAMESPACE_START TOML_PURE_INLINE_GETTER const impl::wrap_node* as() const noexcept { - using type = impl::unwrap_node; - static_assert((impl::is_native || impl::is_one_of)&&!impl::is_cvref, + using unwrapped_type = impl::unwrap_node>; + static_assert(toml::is_value || toml::is_container, "The template type argument of node::as() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) return as_table(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_array(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_string(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_integer(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_floating_point(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_boolean(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_date(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_time(); - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) return as_date_time(); } @@ -2714,25 +2854,32 @@ TOML_NAMESPACE_START template TOML_PURE_GETTER - impl::unwrap_node& ref() & noexcept + decltype(auto) ref() & noexcept { return do_ref(*this); } template TOML_PURE_GETTER - impl::unwrap_node&& ref() && noexcept + decltype(auto) ref() && noexcept { return do_ref(std::move(*this)); } template TOML_PURE_GETTER - const impl::unwrap_node& ref() const& noexcept + decltype(auto) ref() const& noexcept { return do_ref(*this); } + template + TOML_PURE_GETTER + decltype(auto) ref() const&& noexcept + { + return do_ref(std::move(*this)); + } + TOML_PURE_INLINE_GETTER const source_region& source() const noexcept { @@ -2744,7 +2891,7 @@ TOML_NAMESPACE_START // clang-format off template - static constexpr bool can_visit = std::is_invocable_v>; + static constexpr bool can_visit = std::is_invocable_v>; template static constexpr bool can_visit_any = @@ -2773,7 +2920,7 @@ TOML_NAMESPACE_START template static constexpr bool visit_is_nothrow_one = !can_visit - || std::is_nothrow_invocable_v>; + || std::is_nothrow_invocable_v>; template static constexpr bool visit_is_nothrow = @@ -2792,7 +2939,7 @@ TOML_NAMESPACE_START template > struct visit_return_type_ { - using type = decltype(std::declval()(std::declval>())); + using type = decltype(std::declval()(std::declval>())); }; template struct visit_return_type_ @@ -2913,6 +3060,12 @@ TOML_NAMESPACE_START return do_visit(*this, static_cast(visitor)); } + template + decltype(auto) visit(Func&& visitor) const&& noexcept(visit_is_nothrow) + { + return do_visit(static_cast(*this), static_cast(visitor)); + } + TOML_NODISCARD explicit operator node_view() noexcept; @@ -3085,7 +3238,7 @@ TOML_NAMESPACE_START TOML_PURE_GETTER bool is() const noexcept { - return node_ ? node_->template is() : false; + return node_ ? node_->template is>>() : false; } TOML_NODISCARD @@ -3109,66 +3262,66 @@ TOML_NAMESPACE_START TOML_PURE_GETTER bool is_homogeneous() const noexcept { - return node_ ? node_->template is_homogeneous>() : false; + return node_ ? node_->template is_homogeneous>>() : false; } template TOML_PURE_GETTER - auto as() const noexcept + auto* as() const noexcept { return node_ ? node_->template as() : nullptr; } TOML_PURE_GETTER - auto as_table() const noexcept + auto* as_table() const noexcept { return as
(); } TOML_PURE_GETTER - auto as_array() const noexcept + auto* as_array() const noexcept { return as(); } TOML_PURE_GETTER - auto as_string() const noexcept + auto* as_string() const noexcept { return as(); } TOML_PURE_GETTER - auto as_integer() const noexcept + auto* as_integer() const noexcept { return as(); } TOML_PURE_GETTER - auto as_floating_point() const noexcept + auto* as_floating_point() const noexcept { return as(); } TOML_PURE_GETTER - auto as_boolean() const noexcept + auto* as_boolean() const noexcept { return as(); } TOML_PURE_GETTER - auto as_date() const noexcept + auto* as_date() const noexcept { return as(); } TOML_PURE_GETTER - auto as_time() const noexcept + auto* as_time() const noexcept { return as