diff options
Diffstat (limited to 'vendor/toml++/impl/table.hpp')
| -rw-r--r-- | vendor/toml++/impl/table.hpp | 1996 |
1 files changed, 1996 insertions, 0 deletions
diff --git a/vendor/toml++/impl/table.hpp b/vendor/toml++/impl/table.hpp new file mode 100644 index 0000000..edb7316 --- /dev/null +++ b/vendor/toml++/impl/table.hpp @@ -0,0 +1,1996 @@ +//# This file is a part of toml++ and is subject to the the terms of the MIT license. +//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT +#pragma once + +#include "forward_declarations.hpp" +#include "std_map.hpp" +#include "std_initializer_list.hpp" +#include "array.hpp" +#include "make_node.hpp" +#include "node_view.hpp" +#include "key.hpp" +#include "header_start.hpp" + +/// \cond +TOML_IMPL_NAMESPACE_START +{ + template <bool IsConst> + struct table_proxy_pair + { + using value_type = std::conditional_t<IsConst, const node, node>; + + const toml::key& first; + value_type& second; + }; + + template <bool IsConst> + class table_iterator + { + private: + template <bool> + friend class table_iterator; + + using proxy_type = table_proxy_pair<IsConst>; + using mutable_map_iterator = std::map<toml::key, node_ptr, std::less<>>::iterator; + using const_map_iterator = std::map<toml::key, node_ptr, std::less<>>::const_iterator; + using map_iterator = std::conditional_t<IsConst, const_map_iterator, mutable_map_iterator>; + + mutable map_iterator iter_; + alignas(proxy_type) mutable unsigned char proxy_[sizeof(proxy_type)]; + mutable bool proxy_instantiated_ = false; + + TOML_NODISCARD + proxy_type* get_proxy() const noexcept + { + if (!proxy_instantiated_) + { + auto p = ::new (static_cast<void*>(proxy_)) proxy_type{ iter_->first, *iter_->second.get() }; + proxy_instantiated_ = true; + return p; + } + else + return TOML_LAUNDER(reinterpret_cast<proxy_type*>(proxy_)); + } + + public: + TOML_NODISCARD_CTOR + table_iterator() noexcept = default; + + TOML_NODISCARD_CTOR + explicit table_iterator(mutable_map_iterator iter) noexcept // + : iter_{ iter } + {} + + TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst) + TOML_NODISCARD_CTOR + explicit table_iterator(const_map_iterator iter) noexcept // + : iter_{ iter } + {} + + TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst) + TOML_NODISCARD_CTOR + table_iterator(const table_iterator<false>& other) noexcept // + : iter_{ other.iter_ } + {} + + TOML_NODISCARD_CTOR + table_iterator(const table_iterator& other) noexcept // + : iter_{ other.iter_ } + {} + + table_iterator& operator=(const table_iterator& rhs) noexcept + { + iter_ = rhs.iter_; + proxy_instantiated_ = false; + return *this; + } + + using value_type = table_proxy_pair<IsConst>; + using reference = value_type&; + using pointer = value_type*; + using difference_type = typename std::iterator_traits<map_iterator>::difference_type; + using iterator_category = typename std::iterator_traits<map_iterator>::iterator_category; + + table_iterator& operator++() noexcept // ++pre + { + ++iter_; + proxy_instantiated_ = false; + return *this; + } + + table_iterator operator++(int) noexcept // post++ + { + table_iterator out{ iter_ }; + ++iter_; + proxy_instantiated_ = false; + return out; + } + + table_iterator& operator--() noexcept // --pre + { + --iter_; + proxy_instantiated_ = false; + return *this; + } + + table_iterator operator--(int) noexcept // post-- + { + table_iterator out{ iter_ }; + --iter_; + proxy_instantiated_ = false; + return out; + } + + TOML_PURE_INLINE_GETTER + reference operator*() const noexcept + { + return *get_proxy(); + } + + TOML_PURE_INLINE_GETTER + pointer operator->() const noexcept + { + return get_proxy(); + } + + TOML_PURE_INLINE_GETTER + explicit operator const map_iterator&() const noexcept + { + return iter_; + } + + TOML_CONSTRAINED_TEMPLATE(!C, bool C = IsConst) + TOML_PURE_INLINE_GETTER + explicit operator const const_map_iterator() const noexcept + { + return iter_; + } + + TOML_PURE_INLINE_GETTER + friend bool operator==(const table_iterator& lhs, const table_iterator& rhs) noexcept + { + return lhs.iter_ == rhs.iter_; + } + + TOML_PURE_INLINE_GETTER + friend bool operator!=(const table_iterator& lhs, const table_iterator& rhs) noexcept + { + return lhs.iter_ != rhs.iter_; + } + }; + + struct table_init_pair + { + mutable toml::key key; + mutable node_ptr value; + + template <typename K, typename V> + TOML_NODISCARD_CTOR + table_init_pair(K&& k, V&& v, value_flags flags = preserve_source_value_flags) // + : key{ static_cast<K&&>(k) }, + value{ make_node(static_cast<V&&>(v), flags) } + {} + }; +} +TOML_IMPL_NAMESPACE_END; +/// \endcond + +TOML_NAMESPACE_START +{ + /// \brief A BidirectionalIterator for iterating over key-value pairs in a toml::table. + using table_iterator = POXY_IMPLEMENTATION_DETAIL(impl::table_iterator<false>); + + /// \brief A BidirectionalIterator for iterating over const key-value pairs in a toml::table. + using const_table_iterator = POXY_IMPLEMENTATION_DETAIL(impl::table_iterator<true>); + + /// \brief A TOML table. + /// + /// \detail The interface of this type is modeled after std::map, with some + /// additional considerations made for the heterogeneous nature of a + /// TOML table. + /// + /// \cpp + /// toml::table tbl = toml::parse(R"( + /// + /// [animals] + /// cats = [ "tiger", "lion", "puma" ] + /// birds = [ "macaw", "pigeon", "canary" ] + /// fish = [ "salmon", "trout", "carp" ] + /// + /// )"sv); + /// + /// // operator[] retrieves node-views + /// std::cout << "cats: " << tbl["animals"]["cats"] << "\n"; + /// std::cout << "fish[1]: " << tbl["animals"]["fish"][1] << "\n"; + /// + /// // at_path() does fully-qualified "toml path" lookups + /// std::cout << "cats: " << tbl.at_path("animals.cats") << "\n"; + /// std::cout << "fish[1]: " << tbl.at_path("animals.fish[1]") << "\n"; + /// \ecpp + /// + /// \out + /// cats: ['tiger', 'lion', 'puma'] + /// fish[1] : 'trout' + /// cats : ['tiger', 'lion', 'puma'] + /// fish[1] : 'trout' + /// \eout + class TOML_EXPORTED_CLASS table : public node + { + private: + /// \cond + + using map_type = std::map<toml::key, impl::node_ptr, std::less<>>; + using map_pair = std::pair<const toml::key, impl::node_ptr>; + using map_iterator = typename map_type::iterator; + using const_map_iterator = typename map_type::const_iterator; + map_type map_; + + bool inline_ = false; + + TOML_NODISCARD_CTOR + TOML_EXPORTED_MEMBER_FUNCTION + table(const impl::table_init_pair*, const impl::table_init_pair*); + + /// \endcond + + public: + /// \brief Default constructor. + TOML_NODISCARD_CTOR + TOML_EXPORTED_MEMBER_FUNCTION + table() noexcept; + + TOML_EXPORTED_MEMBER_FUNCTION + ~table() noexcept; + + /// \brief Copy constructor. + TOML_NODISCARD_CTOR + TOML_EXPORTED_MEMBER_FUNCTION + table(const table&); + + /// \brief Move constructor. + TOML_NODISCARD_CTOR + TOML_EXPORTED_MEMBER_FUNCTION + table(table&& other) noexcept; + + /// \brief Constructs a table with one or more initial key-value pairs. + /// + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "foo", 1 }, + /// { "bar", 2.0 }, + /// { "kek", "three" } + /// }; + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// { foo = 1, bar = 2.0, kek = "three" } + /// \eout + /// + /// \param kvps A list of key-value pairs used to initialize the table. + TOML_NODISCARD_CTOR + TOML_EXPORTED_MEMBER_FUNCTION + explicit table(std::initializer_list<impl::table_init_pair> kvps) // + : table(kvps.begin(), kvps.end()) + {} + + /// \brief Copy-assignment operator. + TOML_EXPORTED_MEMBER_FUNCTION + table& operator=(const table&); + + /// \brief Move-assignment operator. + TOML_EXPORTED_MEMBER_FUNCTION + table& operator=(table&& rhs) noexcept; + + /// \name Type checks + /// @{ + + /// \brief Returns #toml::node_type::table. + TOML_CONST_INLINE_GETTER + node_type type() const noexcept final + { + return node_type::table; + } + + TOML_PURE_GETTER + TOML_EXPORTED_MEMBER_FUNCTION + bool is_homogeneous(node_type ntype) const noexcept final; + + TOML_PURE_GETTER + TOML_EXPORTED_MEMBER_FUNCTION + bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final; + + TOML_PURE_GETTER + TOML_EXPORTED_MEMBER_FUNCTION + bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final; + + /// \cond + template <typename ElemType = void> + TOML_PURE_GETTER + bool is_homogeneous() const noexcept + { + using type = impl::remove_cvref<impl::unwrap_node<ElemType>>; + static_assert(std::is_void_v<type> || toml::is_value<type> || toml::is_container<type>, + "The template type argument of table::is_homogeneous() must be void or one " + "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + return is_homogeneous(impl::node_type_of<type>); + } + /// \endcond + + /// \brief Returns `true`. + TOML_CONST_INLINE_GETTER + bool is_table() const noexcept final + { + return true; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_array() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_PURE_GETTER + bool is_array_of_tables() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_value() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_string() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_integer() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_floating_point() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_number() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_boolean() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_date() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_time() const noexcept final + { + return false; + } + + /// \brief Returns `false`. + TOML_CONST_INLINE_GETTER + bool is_date_time() const noexcept final + { + return false; + } + + /// @} + + /// \name Type casts + /// @{ + + /// \brief Returns a pointer to the table. + TOML_CONST_INLINE_GETTER + table* as_table() noexcept final + { + return this; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + array* as_array() noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + toml::value<std::string>* as_string() noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + toml::value<int64_t>* as_integer() noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + toml::value<double>* as_floating_point() noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + toml::value<bool>* as_boolean() noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + toml::value<date>* as_date() noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + toml::value<time>* as_time() noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + toml::value<date_time>* as_date_time() noexcept final + { + return nullptr; + } + + /// \brief Returns a const-qualified pointer to the table. + TOML_CONST_INLINE_GETTER + const table* as_table() const noexcept final + { + return this; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + const array* as_array() const noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + const toml::value<std::string>* as_string() const noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + const toml::value<int64_t>* as_integer() const noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + const toml::value<double>* as_floating_point() const noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + const toml::value<bool>* as_boolean() const noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + const toml::value<date>* as_date() const noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + const toml::value<time>* as_time() const noexcept final + { + return nullptr; + } + + /// \brief Returns `nullptr`. + TOML_CONST_INLINE_GETTER + const toml::value<date_time>* as_date_time() const noexcept final + { + return nullptr; + } + + /// @} + + /// \name Metadata + /// @{ + + /// \brief Returns true if this table is an inline table. + /// + /// \remarks Runtime-constructed tables (i.e. those not created during + /// parsing) are not inline by default. + TOML_PURE_INLINE_GETTER + bool is_inline() const noexcept + { + return inline_; + } + + /// \brief Sets whether this table is a TOML inline table. + /// + /// \detail \godbolt{an9xdj} + /// + /// \cpp + /// auto tbl = toml::table{ + /// { "a", 1 }, + /// { "b", 2 }, + /// { "c", 3 }, + /// { "d", toml::table{ { "e", 4 } } } + /// }; + /// std::cout << "is inline? "sv << tbl.is_inline() << "\n"; + /// std::cout << tbl << "\n\n"; + /// + /// tbl.is_inline(!tbl.is_inline()); + /// std::cout << "is inline? "sv << tbl.is_inline() << "\n"; + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// is inline? false + /// a = 1 + /// b = 2 + /// c = 3 + /// + /// [d] + /// e = 4 + /// + /// + /// is inline? true + /// { a = 1, b = 2, c = 3, d = { e = 4 } } + /// \eout + /// + /// \remarks A table being 'inline' is only relevent during printing; + /// it has no effect on the general functionality of the table + /// object. + /// + /// \param val The new value for 'inline'. + void is_inline(bool val) noexcept + { + inline_ = val; + } + + /// @} + + /// \name Value retrieval + /// @{ + + /// \brief Gets the node at a specific key. + /// + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "a", 42, }, + /// { "b", "is the meaning of life, apparently." } + /// }; + /// std::cout << R"(node ["a"] exists: )"sv << !!arr.get("a") << "\n"; + /// std::cout << R"(node ["b"] exists: )"sv << !!arr.get("b") << "\n"; + /// std::cout << R"(node ["c"] exists: )"sv << !!arr.get("c") << "\n"; + /// if (auto val = arr.get("a")) + /// std::cout << R"(node ["a"] was an )"sv << val->type() << "\n"; + /// \ecpp + /// + /// \out + /// node ["a"] exists: true + /// node ["b"] exists: true + /// node ["c"] exists: false + /// node ["a"] was an integer + /// \eout + /// + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key, or nullptr. + TOML_PURE_GETTER + TOML_EXPORTED_MEMBER_FUNCTION + node* get(std::string_view key) noexcept; + + /// \brief Gets the node at a specific key (const overload). + /// + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key, or nullptr. + TOML_PURE_INLINE_GETTER + const node* get(std::string_view key) const noexcept + { + return const_cast<table&>(*this).get(key); + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets the node at a specific key. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key, or nullptr. + TOML_NODISCARD + node* get(std::wstring_view key) + { + if (empty()) + return nullptr; + + return get(impl::narrow(key)); + } + + /// \brief Gets the node at a specific key (const overload). + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key, or nullptr. + TOML_NODISCARD + const node* get(std::wstring_view key) const + { + return const_cast<table&>(*this).get(key); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets the node at a specific key if it is a particular type. + /// + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "a", 42, }, + /// { "b", "is the meaning of life, apparently." } + /// }; + /// if (auto val = arr.get_as<int64_t>("a")) + /// std::cout << R"(node ["a"] was an integer with value )"sv << **val << "\n"; + /// \ecpp + /// + /// \out + /// node ["a"] was an integer with value 42 + /// \eout + /// + /// \tparam T One of the TOML node or value types. + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. + template <typename T> + TOML_PURE_GETTER + impl::wrap_node<T>* get_as(std::string_view key) noexcept + { + const auto n = this->get(key); + return n ? n->template as<T>() : nullptr; + } + + /// \brief Gets the node at a specific key if it is a particular type (const overload). + /// + /// \tparam T One of the TOML node or value types. + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. + template <typename T> + TOML_PURE_GETTER + const impl::wrap_node<T>* get_as(std::string_view key) const noexcept + { + return const_cast<table&>(*this).template get_as<T>(key); + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets the node at a specific key if it is a particular type. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \tparam T One of the TOML node or value types. + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. + template <typename T> + TOML_NODISCARD + impl::wrap_node<T>* get_as(std::wstring_view key) + { + if (empty()) + return nullptr; + + return get_as<T>(impl::narrow(key)); + } + + /// \brief Gets the node at a specific key if it is a particular type (const overload). + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \tparam T One of the TOML node or value types. + /// \param key The node's key. + /// + /// \returns A pointer to the node at the specified key if it was of the given type, or nullptr. + template <typename T> + TOML_NODISCARD + const impl::wrap_node<T>* get_as(std::wstring_view key) const + { + return const_cast<table&>(*this).template get_as<T>(key); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets a reference to the element at a specific key, throwing `std::out_of_range` if none existed. + TOML_NODISCARD + TOML_EXPORTED_MEMBER_FUNCTION + node& at(std::string_view key); + + /// \brief Gets a reference to the element at a specific key, throwing `std::out_of_range` if none existed. + TOML_NODISCARD + const node& at(std::string_view key) const + { + return const_cast<table&>(*this).at(key); + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets a reference to the element at a specific key, throwing `std::out_of_range` if none existed. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + TOML_NODISCARD + node& at(std::wstring_view key) + { + return at(impl::narrow(key)); + } + + /// \brief Gets a reference to the element at a specific key, throwing `std::out_of_range` if none existed. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + TOML_NODISCARD + const node& at(std::wstring_view key) const + { + return const_cast<table&>(*this).at(key); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + + /// @} + + /// \name Iteration + /// @{ + + /// \brief A BidirectionalIterator for iterating over key-value pairs in a toml::table. + using iterator = toml::table_iterator; + + /// \brief A BidirectionalIterator for iterating over const key-value pairs in a toml::table. + using const_iterator = toml::const_table_iterator; + + /// \brief Returns an iterator to the first key-value pair. + TOML_PURE_INLINE_GETTER + iterator begin() noexcept + { + return iterator{ map_.begin() }; + } + + /// \brief Returns an iterator to the first key-value pair. + TOML_PURE_INLINE_GETTER + const_iterator begin() const noexcept + { + return const_iterator{ map_.cbegin() }; + } + + /// \brief Returns an iterator to the first key-value pair. + TOML_PURE_INLINE_GETTER + const_iterator cbegin() const noexcept + { + return const_iterator{ map_.cbegin() }; + } + + /// \brief Returns an iterator to one-past-the-last key-value pair. + TOML_PURE_INLINE_GETTER + iterator end() noexcept + { + return iterator{ map_.end() }; + } + + /// \brief Returns an iterator to one-past-the-last key-value pair. + TOML_PURE_INLINE_GETTER + const_iterator end() const noexcept + { + return const_iterator{ map_.cend() }; + } + + /// \brief Returns an iterator to one-past-the-last key-value pair. + TOML_PURE_INLINE_GETTER + const_iterator cend() const noexcept + { + return const_iterator{ map_.cend() }; + } + + private: + /// \cond + + template <typename T, typename Table> + using for_each_value_ref = impl::copy_cvref<impl::wrap_node<impl::remove_cvref<impl::unwrap_node<T>>>, Table>; + + template <typename Func, typename Table, typename T> + using can_for_each = std::disjunction<std::is_invocable<Func, const key&, for_each_value_ref<T, Table>>, // + std::is_invocable<Func, for_each_value_ref<T, Table>>>; + + template <typename Func, typename Table, typename T> + using can_for_each_nothrow = std::conditional_t< + // first form + std::is_invocable_v<Func, const key&, for_each_value_ref<T, Table>>, + std::is_nothrow_invocable<Func, const key&, for_each_value_ref<T, Table>>, + std::conditional_t< + // second form + std::is_invocable_v<Func, for_each_value_ref<T, Table>>, + std::is_nothrow_invocable<Func, for_each_value_ref<T, Table>>, + std::false_type>>; + + template <typename Func, typename Table> + using can_for_each_any = std::disjunction<can_for_each<Func, Table, table>, + can_for_each<Func, Table, array>, + can_for_each<Func, Table, std::string>, + can_for_each<Func, Table, int64_t>, + can_for_each<Func, Table, double>, + can_for_each<Func, Table, bool>, + can_for_each<Func, Table, date>, + can_for_each<Func, Table, time>, + can_for_each<Func, Table, date_time>>; + + template <typename Func, typename Table, typename T> + using for_each_is_nothrow_one = std::disjunction<std::negation<can_for_each<Func, Table, T>>, // + can_for_each_nothrow<Func, Table, T>>; + + template <typename Func, typename Table> + using for_each_is_nothrow = std::conjunction<for_each_is_nothrow_one<Func, Table, table>, + for_each_is_nothrow_one<Func, Table, array>, + for_each_is_nothrow_one<Func, Table, std::string>, + for_each_is_nothrow_one<Func, Table, int64_t>, + for_each_is_nothrow_one<Func, Table, double>, + for_each_is_nothrow_one<Func, Table, bool>, + for_each_is_nothrow_one<Func, Table, date>, + for_each_is_nothrow_one<Func, Table, time>, + for_each_is_nothrow_one<Func, Table, date_time>>; + + template <typename Func, typename Table> + static void do_for_each(Func&& visitor, Table&& tbl) // + noexcept(for_each_is_nothrow<Func&&, Table&&>::value) + { + static_assert(can_for_each_any<Func&&, Table&&>::value, + "TOML table for_each visitors must be invocable for at least one of the toml::node " + "specializations:" TOML_SA_NODE_TYPE_LIST); + + using kvp_type = impl::copy_cv<map_pair, std::remove_reference_t<Table>>; + + for (kvp_type& kvp : tbl.map_) + { + using node_ref = impl::copy_cvref<toml::node, Table&&>; + static_assert(std::is_reference_v<node_ref>); + +#if TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN + +#ifndef TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_ACKNOWLEDGED + static_assert(impl::always_false<Func, Table, kvp_type, node_ref>, // + TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_MESSAGE); +#endif + + static_cast<node_ref>(*kvp.second) + .visit( + [&]([[maybe_unused]] auto&& v) // + noexcept(for_each_is_nothrow_one<Func&&, Table&&, decltype(v)>::value) + { + using value_ref = for_each_value_ref<decltype(v), Table&&>; + static_assert(std::is_reference_v<value_ref>); + + // func(key, val) + if constexpr (std::is_invocable_v<Func&&, const key&, value_ref>) + { + static_cast<Func&&>(visitor)(static_cast<const key&>(kvp.first), + static_cast<value_ref>(v)); + } + + // func(val) + else if constexpr (std::is_invocable_v<Func&&, value_ref>) + { + static_cast<Func&&>(visitor)(static_cast<value_ref>(v)); + } + }); + +#else + const auto keep_going = + static_cast<node_ref>(*kvp.second) + .visit( + [&]([[maybe_unused]] auto&& v) // + noexcept(for_each_is_nothrow_one<Func&&, Table&&, decltype(v)>::value) + { + using value_ref = for_each_value_ref<decltype(v), Table&&>; + static_assert(std::is_reference_v<value_ref>); + + // func(key, val) + if constexpr (std::is_invocable_v<Func&&, const key&, value_ref>) + { + using return_type = decltype(static_cast<Func&&>( + visitor)(static_cast<const key&>(kvp.first), static_cast<value_ref>(v))); + + if constexpr (impl::is_constructible_or_convertible<bool, return_type>) + { + return static_cast<bool>(static_cast<Func&&>( + visitor)(static_cast<const key&>(kvp.first), static_cast<value_ref>(v))); + } + else + { + static_cast<Func&&>(visitor)(static_cast<const key&>(kvp.first), + static_cast<value_ref>(v)); + return true; + } + } + + // func(val) + else if constexpr (std::is_invocable_v<Func&&, value_ref>) + { + using return_type = + decltype(static_cast<Func&&>(visitor)(static_cast<value_ref>(v))); + + if constexpr (impl::is_constructible_or_convertible<bool, return_type>) + { + return static_cast<bool>( + static_cast<Func&&>(visitor)(static_cast<value_ref>(v))); + } + else + { + static_cast<Func&&>(visitor)(static_cast<value_ref>(v)); + return true; + } + } + + // visitor not compatible with this particular type + else + return true; + }); + + if (!keep_going) + return; +#endif + } + } + + /// \endcond + + public: + /// \brief Invokes a visitor on each key-value pair in the table. + /// + /// \tparam Func A callable type invocable with one of the following signatures: + /// <ul> + /// <li> `func(key, val)` + /// <li> `func(val)` + /// </ul> + /// Where: + /// <ul> + /// <li> `key` will recieve a const reference to a toml::key + /// <li> `val` will recieve the value as it's concrete type with cvref-qualifications matching the table + /// </ul> + /// Visitors returning `bool` (or something convertible to `bool`) will cause iteration to + /// stop if they return `false`. + /// + /// \param visitor The visitor object. + /// + /// \returns A reference to the table. + /// + /// \details \cpp + /// toml::table tbl{ + /// { "0", 0 }, + /// { "1", 1 }, + /// { "2", 2 }, + /// { "3", 3.0 }, + /// { "4", "four" }, + /// { "5", "five" }, + /// { "6", 6 } + /// }; + /// + /// // select only the integers using a strongly-typed visitor + /// tbl.for_each([](toml::value<int64_t>& val) + /// { + /// std::cout << val << ", "; + /// }); + /// std::cout << "\n"; + /// + /// // select all the numeric values using a generic visitor + is_number<> metafunction + /// tbl.for_each([](auto&& val) + /// { + /// if constexpr (toml::is_number<decltype(val)>) + /// std::cout << val << ", "; + /// }); + /// std::cout << "\n"; + /// + /// // select all the numeric values until we encounter something non-numeric + /// tbl.for_each([](auto&& val) + /// { + /// if constexpr (toml::is_number<decltype(val)>) + /// { + /// std::cout << val << ", "; + /// return true; // "keep going" + /// } + /// else + /// return false; // "stop!" + /// + /// }); + /// std::cout << "\n\n"; + /// + /// // visitors may also recieve the key + /// tbl.for_each([](const toml::key& key, auto&& val) + /// { + /// std::cout << key << ": " << val << "\n"; + /// }); + /// + /// \ecpp + /// \out + /// 0, 1, 2, 6, + /// 0, 1, 2, 3.0, 6, + /// 0, 1, 2, 3.0, + /// + /// 0: 0 + /// 1: 1 + /// 2: 2 + /// 3: 3.0 + /// 4: 'four' + /// 5: 'five' + /// 6: 6 + /// \eout + /// + /// \see node::visit() + template <typename Func> + table& for_each(Func&& visitor) & // + noexcept(for_each_is_nothrow<Func&&, table&>::value) + { + do_for_each(static_cast<Func&&>(visitor), *this); + return *this; + } + + /// \brief Invokes a visitor on each key-value pair in the table (rvalue overload). + template <typename Func> + table&& for_each(Func&& visitor) && // + noexcept(for_each_is_nothrow<Func&&, table&&>::value) + { + do_for_each(static_cast<Func&&>(visitor), static_cast<table&&>(*this)); + return static_cast<table&&>(*this); + } + + /// \brief Invokes a visitor on each key-value pair in the table (const lvalue overload). + template <typename Func> + const table& for_each(Func&& visitor) const& // + noexcept(for_each_is_nothrow<Func&&, const table&>::value) + { + do_for_each(static_cast<Func&&>(visitor), *this); + return *this; + } + + /// \brief Invokes a visitor on each key-value pair in the table (const rvalue overload). + template <typename Func> + const table&& for_each(Func&& visitor) const&& // + noexcept(for_each_is_nothrow<Func&&, const table&&>::value) + { + do_for_each(static_cast<Func&&>(visitor), static_cast<const table&&>(*this)); + return static_cast<const table&&>(*this); + } + + /// @} + + /// \name Size and Capacity + /// @{ + + /// \brief Returns true if the table is empty. + TOML_PURE_INLINE_GETTER + bool empty() const noexcept + { + return map_.empty(); + } + + /// \brief Returns the number of key-value pairs in the table. + TOML_PURE_INLINE_GETTER + size_t size() const noexcept + { + return map_.size(); + } + + /// @} + + /// \name Searching + /// @{ + + private: + /// \cond + + TOML_PURE_GETTER + TOML_EXPORTED_MEMBER_FUNCTION + map_iterator get_lower_bound(std::string_view) noexcept; + + /// \endcond + + public: + /// \brief Returns an iterator to the first key-value pair with key that is _not less_ than the given key. + /// + /// \returns An iterator to the first matching key-value pair, or #end(). + TOML_PURE_GETTER + iterator lower_bound(std::string_view key) noexcept + { + return iterator{ get_lower_bound(key) }; + } + + /// \brief Returns a const iterator to the first key-value pair with key that is _not less_ than the given key. + /// + /// \returns An iterator to the first matching key-value pair, or #end(). + TOML_PURE_GETTER + const_iterator lower_bound(std::string_view key) const noexcept + { + return const_iterator{ const_cast<table&>(*this).get_lower_bound(key) }; + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Returns an iterator to the first key-value pair with key that is _not less_ than the given key. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \returns An iterator to the first matching key-value pair, or #end(). + TOML_NODISCARD + iterator lower_bound(std::wstring_view key) + { + if (empty()) + return end(); + + return lower_bound(impl::narrow(key)); + } + + /// \brief Returns a const iterator to the first key-value pair with key that is _not less_ than the given key. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \returns An iterator to the first matching key-value pair, or #end(). + TOML_NODISCARD + const_iterator lower_bound(std::wstring_view key) const + { + if (empty()) + return end(); + + return lower_bound(impl::narrow(key)); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets an iterator to the node at a specific key. + /// + /// \param key The node's key. + /// + /// \returns An iterator to the node at the specified key, or end(). + TOML_PURE_GETTER + TOML_EXPORTED_MEMBER_FUNCTION + iterator find(std::string_view key) noexcept; + + /// \brief Gets an iterator to the node at a specific key (const overload) + /// + /// \param key The node's key. + /// + /// \returns A const iterator to the node at the specified key, or cend(). + TOML_PURE_GETTER + TOML_EXPORTED_MEMBER_FUNCTION + const_iterator find(std::string_view key) const noexcept; + + /// \brief Returns true if the table contains a node at the given key. + TOML_PURE_GETTER + bool contains(std::string_view key) const noexcept + { + return get(key) != nullptr; + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets an iterator to the node at a specific key. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \param key The node's key. + /// + /// \returns An iterator to the node at the specified key, or end(). + TOML_NODISCARD + iterator find(std::wstring_view key) + { + if (empty()) + return end(); + + return find(impl::narrow(key)); + } + + /// \brief Gets an iterator to the node at a specific key (const overload). + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \param key The node's key. + /// + /// \returns A const iterator to the node at the specified key, or cend(). + TOML_NODISCARD + const_iterator find(std::wstring_view key) const + { + return find(impl::narrow(key)); + } + + /// \brief Returns true if the table contains a node at the given key. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + TOML_NODISCARD + bool contains(std::wstring_view key) const + { + return contains(impl::narrow(key)); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + + /// @} + + /// \name Erasure + /// @{ + + private: + /// \cond + + TOML_EXPORTED_MEMBER_FUNCTION + map_iterator erase(const_map_iterator) noexcept; + + TOML_EXPORTED_MEMBER_FUNCTION + map_iterator erase(const_map_iterator, const_map_iterator) noexcept; + + /// \endcond + + public: + /// \brief Removes the specified key-value pair from the table. + /// + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "a", 1 }, + /// { "b", 2 }, + /// { "c", 3 } + /// }; + /// std::cout << tbl << "\n"; + /// + /// tbl.erase(tbl.begin() + 1); + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// { a = 1, b = 2, c = 3 } + /// { a = 1, c = 3 } + /// \eout + /// + /// \param pos Iterator to the key-value pair being erased. + /// + /// \returns Iterator to the first key-value pair immediately following the removed key-value pair. + iterator erase(iterator pos) noexcept + { + return iterator{ erase(const_map_iterator{ pos }) }; + } + + /// \brief Removes the specified key-value pair from the table (const iterator overload). + /// + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "a", 1 }, + /// { "b", 2 }, + /// { "c", 3 } + /// }; + /// std::cout << tbl << "\n"; + /// + /// tbl.erase(tbl.cbegin() + 1); + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// { a = 1, b = 2, c = 3 } + /// { a = 1, c = 3 } + /// \eout + /// + /// \param pos Iterator to the key-value pair being erased. + /// + /// \returns Iterator to the first key-value pair immediately following the removed key-value pair. + iterator erase(const_iterator pos) noexcept + { + return iterator{ erase(const_map_iterator{ pos }) }; + } + + /// \brief Removes the key-value pairs in the range [first, last) from the table. + /// + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "a", 1 }, + /// { "b", "bad" }, + /// { "c", "karma" }, + /// { "d", 2 } + /// }; + /// std::cout << tbl << "\n"; + /// + /// tbl.erase(tbl.cbegin() + 1, tbl.cbegin() + 3); + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// { a = 1, b = "bad", c = "karma", d = 2 } + /// { a = 1, d = 2 } + /// \eout + /// + /// \param begin Iterator to the first key-value pair being erased. + /// \param end Iterator to the one-past-the-last key-value pair being erased. + /// + /// \returns Iterator to the first key-value pair immediately following the last removed key-value pair. + iterator erase(const_iterator begin, const_iterator end) noexcept + { + return iterator{ erase(const_map_iterator{ begin }, const_map_iterator{ end }) }; + } + + /// \brief Removes the value with the given key from the table. + /// + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "a", 1 }, + /// { "b", 2 }, + /// { "c", 3 } + /// }; + /// std::cout << tbl << "\n"; + /// + /// std::cout << tbl.erase("b") << "\n"; + /// std::cout << tbl.erase("not an existing key") << "\n"; + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// { a = 1, b = 2, c = 3 } + /// true + /// false + /// { a = 1, c = 3 } + /// \eout + /// + /// \param key Key to erase. + /// + /// \returns Number of elements removed (0 or 1). + TOML_EXPORTED_MEMBER_FUNCTION + size_t erase(std::string_view key) noexcept; + +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Removes the value with the given key from the table. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \param key Key to erase. + /// + /// \returns Number of elements removed (0 or 1). + size_t erase(std::wstring_view key) + { + if (empty()) + return false; + + return erase(impl::narrow(key)); + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Removes empty child arrays and tables. + /// + /// \detail \cpp + /// + /// auto tbl = toml::table{ { "a", 1 }, { "b", toml::array{ } }, { "c", toml::array{ toml::table{}, toml::array{} } } }; + /// std::cout << arr << "\n"; + /// + /// arr.prune(); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// { a = 1, b = [], c = [ {}, [] ] } + /// { a = 1 } + /// \eout + /// + /// \param recursive Should child arrays and tables themselves be pruned? + /// + /// \returns A reference to the table. + TOML_EXPORTED_MEMBER_FUNCTION + table& 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 table. + table&& prune(bool recursive = true) && noexcept + { + return static_cast<toml::table&&>(this->prune(recursive)); + } + + /// \brief Removes all key-value pairs from the table. + TOML_EXPORTED_MEMBER_FUNCTION + void clear() noexcept; + + /// @} + + /// \name Insertion and Emplacement + /// @{ + + private: + /// \cond + + TOML_EXPORTED_MEMBER_FUNCTION + map_iterator insert_with_hint(const_iterator, key&&, impl::node_ptr&&); + + /// \endcond + + public: + /// \brief Emplaces a new value at a specific key if one did not already exist. + /// + /// \tparam ValueType toml::table, toml::array, or any native TOML value type. + /// \tparam KeyType A toml::key or any compatible string type. + /// \tparam ValueArgs Value constructor argument types. + /// \param hint Iterator to the position before which the new element will be emplaced. + /// \param key The key at which to emplace the new value. + /// \param args Arguments to forward to the value's constructor. + /// + /// \returns An iterator to the emplacement position (or the position of the value that prevented emplacement) + /// + /// \note This function has exactly the same semantics as [std::map::emplace_hint()](https://en.cppreference.com/w/cpp/container/map/emplace_hint). + TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible<KeyType&&> || impl::is_wide_string<KeyType>), + typename ValueType = void, + typename KeyType, + typename... ValueArgs) + iterator emplace_hint(const_iterator hint, KeyType&& key, ValueArgs&&... args) + { + static_assert(!impl::is_wide_string<KeyType> || TOML_ENABLE_WINDOWS_COMPAT, + "Emplacement using wide-character keys is only supported on Windows with " + "TOML_ENABLE_WINDOWS_COMPAT enabled."); + + using raw_value_type = impl::remove_cvref<ValueType>; + using value_type = std:: + conditional_t<std::is_void_v<raw_value_type>, impl::emplaced_type_of<ValueArgs&&...>, raw_value_type>; + + if constexpr (impl::is_wide_string<KeyType>) + { +#if TOML_ENABLE_WINDOWS_COMPAT + return emplace_hint<value_type>(hint, + impl::narrow(static_cast<KeyType&&>(key)), + static_cast<ValueArgs&&>(args)...); +#else + static_assert(impl::always_false<KeyType>, "Evaluated unreachable branch!"); +#endif + } + else + { + static constexpr auto moving_node_ptr = std::is_same_v<value_type, impl::node_ptr> // + && sizeof...(ValueArgs) == 1u // + && impl::first_is_same<impl::node_ptr&&, ValueArgs&&...>; + using unwrapped_type = impl::remove_cvref<impl::unwrap_node<value_type>>; + + static_assert(moving_node_ptr // + || impl::is_native<unwrapped_type> // + || impl::is_one_of<unwrapped_type, table, array>, // + "ValueType argument of table::emplace_hint() must be one " + "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + map_iterator ipos = insert_with_hint(hint, toml::key{ static_cast<KeyType&&>(key) }, nullptr); + + // if second is nullptr then we successully claimed the key and inserted the empty sentinel, + // so now we have to construct the actual value + if (!ipos->second) + { + if constexpr (moving_node_ptr) + ipos->second = std::move(static_cast<ValueArgs&&>(args)...); + else + { +#if TOML_COMPILER_HAS_EXCEPTIONS + try + { +#endif + ipos->second.reset( + new impl::wrap_node<unwrapped_type>{ static_cast<ValueArgs&&>(args)... }); +#if TOML_COMPILER_HAS_EXCEPTIONS + } + catch (...) + { + erase(const_map_iterator{ ipos }); // strong exception guarantee + throw; + } +#endif + } + } + return iterator{ ipos }; + } + } + + /// \brief Inserts a new value at a specific key if one did not already exist. + /// + /// \detail \godbolt{bMnW5r} + /// + /// \cpp + /// auto tbl = toml::table{ + /// { "a", 1 }, + /// { "b", 2 }, + /// { "c", 3 } + /// }; + /// std::cout << tbl << "\n"; + /// + /// for (auto k : { "a", "d" }) + /// { + /// auto result = tbl.insert(k, 42); + /// std::cout << "inserted with key '"sv << k << "': "sv << result.second << "\n"; + /// } + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// a = 1 + /// b = 2 + /// c = 3 + /// + /// inserted with key 'a': false + /// inserted with key 'd': true + /// a = 1 + /// b = 2 + /// c = 3 + /// d = 42 + /// \eout + /// + /// \tparam KeyType A toml::key or any compatible string type. + /// \tparam ValueType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type + /// (or a type promotable to one). + /// \param key The key at which to insert the new value. + /// \param val The new value to insert. + /// \param flags Value flags to apply to new values. + /// + /// \returns \conditional_return{Valid input} + /// <ul> + /// <li>An iterator to the insertion position (or the position of the value that prevented insertion) + /// <li>A boolean indicating if the insertion was successful. + /// </ul> + /// \conditional_return{Input is a null toml::node_view} + /// `{ end(), false }` + /// + /// \attention The return value will always be `{ end(), false }` if the input value was an + /// null toml::node_view, because no insertion can take place. This is the only circumstance + /// in which this can occur. + TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible<KeyType&&> || impl::is_wide_string<KeyType>), + typename KeyType, + typename ValueType) + std::pair<iterator, bool> insert(KeyType&& key, + ValueType&& val, + value_flags flags = preserve_source_value_flags) + { + static_assert(!impl::is_wide_string<KeyType> || TOML_ENABLE_WINDOWS_COMPAT, + "Insertion using wide-character keys is only supported on Windows with " + "TOML_ENABLE_WINDOWS_COMPAT enabled."); + + if constexpr (is_node_view<ValueType>) + { + if (!val) + return { end(), false }; + } + + if constexpr (impl::is_wide_string<KeyType>) + { +#if TOML_ENABLE_WINDOWS_COMPAT + return insert(impl::narrow(static_cast<KeyType&&>(key)), static_cast<ValueType&&>(val), flags); +#else + static_assert(impl::always_false<KeyType>, "Evaluated unreachable branch!"); +#endif + } + else + { + const auto key_view = std::string_view{ key }; + map_iterator ipos = get_lower_bound(key_view); + if (ipos == map_.end() || ipos->first != key_view) + { + ipos = insert_with_hint(const_iterator{ ipos }, + toml::key{ static_cast<KeyType&&>(key) }, + impl::make_node(static_cast<ValueType&&>(val), flags)); + return { iterator{ ipos }, true }; + } + return { iterator{ ipos }, false }; + } + } + + /// \brief Inserts a series of key-value pairs into the table. + /// + /// \detail \godbolt{bzYcce} + /// + /// \cpp + /// auto tbl = toml::table{ + /// { "a", 1 }, + /// { "b", 2 }, + /// { "c", 3 } + /// }; + /// std::cout << tbl << "\n"; + /// + /// auto kvps = std::array<std::pair<std::string, int>, 2>{{ + /// { "d", 42 }, + /// { "a", 43 } // won't be inserted, 'a' already exists + /// }}; + /// tbl.insert(kvps.begin(), kvps.end()); + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// a = 1 + /// b = 2 + /// c = 3 + /// + /// a = 1 + /// b = 2 + /// c = 3 + /// d = 42 + /// \eout + /// + /// \tparam Iter An InputIterator to a collection of key-value pairs. + /// \param begin An iterator to the first value in the input collection. + /// \param end An iterator to one-past-the-last value in the input collection. + /// \param flags Value flags to apply to new values. + /// + /// \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. + TOML_CONSTRAINED_TEMPLATE((!is_key_or_convertible<Iter> && !impl::is_wide_string<Iter>), typename Iter) + void insert(Iter begin, Iter end, value_flags flags = preserve_source_value_flags) + { + if (begin == end) + return; + for (auto it = begin; it != end; it++) + { + if constexpr (std::is_rvalue_reference_v<decltype(*it)>) + insert(std::move((*it).first), std::move((*it).second), flags); + else + insert((*it).first, (*it).second, flags); + } + } + + /// \brief Inserts or assigns a value at a specific key. + /// + /// \detail \godbolt{ddK563} + /// + /// \cpp + /// auto tbl = toml::table{ + /// { "a", 1 }, + /// { "b", 2 }, + /// { "c", 3 } + /// }; + /// std::cout << tbl << "\n"; + /// + /// for (auto k : { "a", "d" }) + /// { + /// auto result = tbl.insert_or_assign(k, 42); + /// std::cout << "value at key '"sv << k + /// << "' was "sv << (result.second ? "inserted"sv : "assigned"sv) << "\n"; + /// } + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// a = 1 + /// b = 2 + /// c = 3 + /// + /// value at key 'a' was assigned + /// value at key 'd' was inserted + /// a = 42 + /// b = 2 + /// c = 3 + /// d = 42 + /// \eout + /// + /// \tparam KeyType A toml::key or any compatible string type. + /// \tparam ValueType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type + /// (or a type promotable to one). + /// \param key The key at which to insert or assign the value. + /// \param val The value to insert/assign. + /// \param flags Value flags to apply to new values. + /// + /// \returns \conditional_return{Valid input} + /// <ul> + /// <li>An iterator to the value's position + /// <li>`true` if the value was inserted, `false` if it was assigned. + /// </ul> + /// \conditional_return{Input is a null toml::node_view} + /// `{ end(), false }` + /// + /// \attention The return value will always be `{ end(), false }` if the input value was + /// a null toml::node_view, because no insertion or assignment can take place. + /// This is the only circumstance in which this can occur. + TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible<KeyType&&> || impl::is_wide_string<KeyType>), + typename KeyType, + typename ValueType) + std::pair<iterator, bool> insert_or_assign(KeyType&& key, + ValueType&& val, + value_flags flags = preserve_source_value_flags) + { + static_assert(!impl::is_wide_string<KeyType> || TOML_ENABLE_WINDOWS_COMPAT, + "Insertion using wide-character keys is only supported on Windows with " + "TOML_ENABLE_WINDOWS_COMPAT enabled."); + + if constexpr (is_node_view<ValueType>) + { + if (!val) + return { end(), false }; + } + + if constexpr (impl::is_wide_string<KeyType>) + { +#if TOML_ENABLE_WINDOWS_COMPAT + return insert_or_assign(impl::narrow(static_cast<KeyType&&>(key)), + static_cast<ValueType&&>(val), + flags); +#else + static_assert(impl::always_false<KeyType>, "Evaluated unreachable branch!"); +#endif + } + else + { + const auto key_view = std::string_view{ key }; + map_iterator ipos = get_lower_bound(key_view); + if (ipos == map_.end() || ipos->first != key_view) + { + ipos = insert_with_hint(const_iterator{ ipos }, + toml::key{ static_cast<KeyType&&>(key) }, + impl::make_node(static_cast<ValueType&&>(val), flags)); + return { iterator{ ipos }, true }; + } + else + { + (*ipos).second = impl::make_node(static_cast<ValueType&&>(val), flags); + return { iterator{ ipos }, false }; + } + } + } + + /// \brief Emplaces a new value at a specific key if one did not already exist. + /// + /// \detail \cpp + /// auto tbl = toml::table{ + /// { "a", 1 }, + /// { "b", 2 }, + /// { "c", 3 } + /// }; + /// std::cout << tbl << "\n"; + /// + /// for (auto k : { "a", "d" }) + /// { + /// // add a string using std::string's substring constructor + /// auto result = tbl.emplace<std::string>(k, "this is not a drill"sv, 14, 5); + /// std::cout << "emplaced with key '"sv << k << "': "sv << result.second << "\n"; + /// } + /// std::cout << tbl << "\n"; + /// \ecpp + /// + /// \out + /// { a = 1, b = 2, c = 3 } + /// emplaced with key 'a': false + /// emplaced with key 'd': true + /// { a = 1, b = 2, c = 3, d = "drill" } + /// \eout + /// + /// \tparam ValueType toml::table, toml::array, or any native TOML value type. + /// \tparam KeyType A toml::key or any compatible string type. + /// \tparam ValueArgs Value constructor argument types. + /// \param key The key at which to emplace the new value. + /// \param args Arguments to forward to the value's constructor. + /// + /// \returns A std::pair containing: <br> + /// - An iterator to the emplacement position (or the position of the value that prevented emplacement) + /// - A boolean indicating if the emplacement was successful. + /// + /// \remark There is no difference between insert() and emplace() for trivial value types (floats, ints, bools). + TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible<KeyType&&> || impl::is_wide_string<KeyType>), + typename ValueType = void, + typename KeyType, + typename... ValueArgs) + std::pair<iterator, bool> emplace(KeyType&& key, ValueArgs&&... args) + { + static_assert(!impl::is_wide_string<KeyType> || TOML_ENABLE_WINDOWS_COMPAT, + "Emplacement using wide-character keys is only supported on Windows with " + "TOML_ENABLE_WINDOWS_COMPAT enabled."); + + using raw_value_type = impl::remove_cvref<ValueType>; + using value_type = std:: + conditional_t<std::is_void_v<raw_value_type>, impl::emplaced_type_of<ValueArgs&&...>, raw_value_type>; + + if constexpr (impl::is_wide_string<KeyType>) + { +#if TOML_ENABLE_WINDOWS_COMPAT + return emplace<value_type>(impl::narrow(static_cast<KeyType&&>(key)), + static_cast<ValueArgs&&>(args)...); +#else + static_assert(impl::always_false<KeyType>, "Evaluated unreachable branch!"); +#endif + } + else + { + using unwrapped_type = impl::remove_cvref<impl::unwrap_node<value_type>>; + static_assert((impl::is_native<unwrapped_type> || impl::is_one_of<unwrapped_type, table, array>), + "ValueType argument of table::emplace() must be one " + "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + const auto key_view = std::string_view{ key }; + auto ipos = get_lower_bound(key_view); + if (ipos == map_.end() || ipos->first != key_view) + { + ipos = insert_with_hint( + const_iterator{ ipos }, + toml::key{ static_cast<KeyType&&>(key) }, + impl::node_ptr{ new impl::wrap_node<unwrapped_type>{ static_cast<ValueArgs&&>(args)... } }); + return { iterator{ ipos }, true }; + } + return { iterator{ ipos }, false }; + } + } + + /// @} + + /// \name Node views + /// @{ + + /// \cond + using node::operator[]; // inherit operator[toml::path] + /// \endcond + + /// \brief Gets a node_view for the selected value. + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if one existed, or an empty node view. + /// + /// \remarks std::map::operator[]'s behaviour of default-constructing a value at a key if it + /// didn't exist is a crazy bug factory so I've deliberately chosen not to emulate it. + /// <strong>This is not an error.</strong> + /// + /// \see toml::node_view + TOML_NODISCARD + node_view<node> operator[](std::string_view key) noexcept + { + return node_view<node>{ get(key) }; + } + + /// \brief Gets a node_view for the selected value (const overload). + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if one existed, or an empty node view. + /// + /// \remarks std::map::operator[]'s behaviour of default-constructing a value at a key if it + /// didn't exist is a crazy bug factory so I've deliberately chosen not to emulate it. + /// <strong>This is not an error.</strong> + /// + /// \see toml::node_view + TOML_NODISCARD + node_view<const node> operator[](std::string_view key) const noexcept + { + return node_view<const node>{ get(key) }; + } + +#if TOML_ENABLE_WINDOWS_COMPAT + + /// \brief Gets a node_view for the selected value. + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if one existed, or an empty node view. + /// + /// \remarks std::map::operator[]'s behaviour of default-constructing a value at a key if it + /// didn't exist is a crazy bug factory so I've deliberately chosen not to emulate it. + /// <strong>This is not an error.</strong> + /// + /// \see toml::node_view + TOML_NODISCARD + node_view<node> operator[](std::wstring_view key) + { + return node_view<node>{ get(key) }; + } + + /// \brief Gets a node_view for the selected value (const overload). + /// + /// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled. + /// + /// \param key The key used for the lookup. + /// + /// \returns A view of the value at the given key if one existed, or an empty node view. + /// + /// \remarks std::map::operator[]'s behaviour of default-constructing a value at a key if it + /// didn't exist is a crazy bug factory so I've deliberately chosen not to emulate it. + /// <strong>This is not an error.</strong> + /// + /// \see toml::node_view + TOML_NODISCARD + node_view<const node> operator[](std::wstring_view key) const + { + return node_view<const node>{ get(key) }; + } + +#endif // TOML_ENABLE_WINDOWS_COMPAT + + /// @} + + /// \name Equality + /// @{ + + private: + /// \cond + + TOML_PURE_GETTER + TOML_EXPORTED_STATIC_FUNCTION + static bool TOML_CALLCONV equal(const table&, const table&) noexcept; + + /// \endcond + public: + /// \brief Equality operator. + /// + /// \param lhs The LHS table. + /// \param rhs The RHS table. + /// + /// \returns True if the tables contained the same keys and map. + TOML_NODISCARD + friend bool operator==(const table& lhs, const table& rhs) noexcept + { + return equal(lhs, rhs); + } + + /// \brief Inequality operator. + /// + /// \param lhs The LHS table. + /// \param rhs The RHS table. + /// + /// \returns True if the tables did not contain the same keys and map. + TOML_NODISCARD + friend bool operator!=(const table& lhs, const table& rhs) noexcept + { + return !equal(lhs, rhs); + } + + /// @} + +#if TOML_ENABLE_FORMATTERS + + /// \brief Prints the table out to a stream as formatted TOML. + /// + /// \availability This operator is only available when #TOML_ENABLE_FORMATTERS is enabled. + friend std::ostream& operator<<(std::ostream& lhs, const table& rhs) + { + impl::print_to_stream(lhs, rhs); + return lhs; + } + +#endif + }; +} +TOML_NAMESPACE_END; + +#include "header_end.hpp" |
