diff options
Diffstat (limited to 'vendor/toml++/impl/array.hpp')
| -rw-r--r-- | vendor/toml++/impl/array.hpp | 3261 |
1 files changed, 1510 insertions, 1751 deletions
diff --git a/vendor/toml++/impl/array.hpp b/vendor/toml++/impl/array.hpp index ad79379..c276637 100644 --- a/vendor/toml++/impl/array.hpp +++ b/vendor/toml++/impl/array.hpp @@ -1,15 +1,15 @@ -//# 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 +// # 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 "header_start.hpp" +#include "make_node.hpp" +#include "std_initializer_list.hpp" #include "std_utility.hpp" #include "std_vector.hpp" -#include "std_initializer_list.hpp" #include "value.hpp" -#include "make_node.hpp" -#include "header_start.hpp" #ifndef TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN #if TOML_GCC && TOML_GCC <= 7 @@ -19,1771 +19,1530 @@ #endif #endif -#if TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN && !defined(TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_ACKNOWLEDGED) -#define TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_MESSAGE \ - "If you're seeing this error it's because you're using one of toml++'s for_each() functions on a compiler with " \ - "known bugs in that area (e.g. GCC 7). On these compilers returning a bool (or bool-convertible) value from the " \ - "for_each() callable causes spurious compilation failures, while returning nothing (void) works fine. " \ - "If you believe this message is incorrect for your compiler, you can try your luck by #defining " \ - "TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN as 0 and recompiling - if it works, great! Let me know at " \ - "https://github.com/marzer/tomlplusplus/issues. Alternatively, if you don't have any need for early-exiting from " \ - "for_each(), you can suppress this error by #defining TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_ACKNOWLEDGED " \ - "and moving on with your life." +#if TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN && \ + !defined(TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_ACKNOWLEDGED) +#define TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_MESSAGE \ + "If you're seeing this error it's because you're using one of toml++'s for_each() functions on " \ + "a compiler with " \ + "known bugs in that area (e.g. GCC 7). On these compilers returning a bool (or " \ + "bool-convertible) value from the " \ + "for_each() callable causes spurious compilation failures, while returning nothing (void) " \ + "works fine. " \ + "If you believe this message is incorrect for your compiler, you can try your luck by " \ + "#defining " \ + "TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN as 0 and recompiling - if it works, great! Let me know " \ + "at " \ + "https://github.com/marzer/tomlplusplus/issues. Alternatively, if you don't have any need for " \ + "early-exiting from " \ + "for_each(), you can suppress this error by #defining " \ + "TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_ACKNOWLEDGED " \ + "and moving on with your life." #endif /// \cond -TOML_IMPL_NAMESPACE_START -{ - template <bool IsConst> - class TOML_TRIVIAL_ABI array_iterator - { - private: - template <bool> - friend class array_iterator; - - using mutable_vector_iterator = std::vector<node_ptr>::iterator; - using const_vector_iterator = std::vector<node_ptr>::const_iterator; - using vector_iterator = std::conditional_t<IsConst, const_vector_iterator, mutable_vector_iterator>; - - mutable vector_iterator iter_; - - public: - using value_type = std::conditional_t<IsConst, const node, node>; - using reference = value_type&; - using pointer = value_type*; - using difference_type = ptrdiff_t; - using iterator_category = typename std::iterator_traits<vector_iterator>::iterator_category; - - TOML_NODISCARD_CTOR - array_iterator() noexcept = default; - - TOML_NODISCARD_CTOR - explicit array_iterator(mutable_vector_iterator iter) noexcept // - : iter_{ iter } - {} - - TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst) - TOML_NODISCARD_CTOR - explicit array_iterator(const_vector_iterator iter) noexcept // - : iter_{ iter } - {} - - TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst) - TOML_NODISCARD_CTOR - array_iterator(const array_iterator<false>& other) noexcept // - : iter_{ other.iter_ } - {} - - TOML_NODISCARD_CTOR - array_iterator(const array_iterator&) noexcept = default; - - array_iterator& operator=(const array_iterator&) noexcept = default; - - array_iterator& operator++() noexcept // ++pre - { - ++iter_; - return *this; - } - - array_iterator operator++(int) noexcept // post++ - { - array_iterator out{ iter_ }; - ++iter_; - return out; - } - - array_iterator& operator--() noexcept // --pre - { - --iter_; - return *this; - } - - array_iterator operator--(int) noexcept // post-- - { - array_iterator out{ iter_ }; - --iter_; - return out; - } - - TOML_PURE_INLINE_GETTER - reference operator*() const noexcept - { - return *iter_->get(); - } - - TOML_PURE_INLINE_GETTER - pointer operator->() const noexcept - { - return iter_->get(); - } - - TOML_PURE_INLINE_GETTER - explicit operator const vector_iterator&() const noexcept - { - return iter_; - } - - TOML_CONSTRAINED_TEMPLATE(!C, bool C = IsConst) - TOML_PURE_INLINE_GETTER - explicit operator const const_vector_iterator() const noexcept - { - return iter_; - } - - array_iterator& operator+=(ptrdiff_t rhs) noexcept - { - iter_ += rhs; - return *this; - } - - array_iterator& operator-=(ptrdiff_t rhs) noexcept - { - iter_ -= rhs; - return *this; - } - - TOML_NODISCARD - friend array_iterator operator+(const array_iterator& lhs, ptrdiff_t rhs) noexcept - { - return array_iterator{ lhs.iter_ + rhs }; - } - - TOML_NODISCARD - friend array_iterator operator+(ptrdiff_t lhs, const array_iterator& rhs) noexcept - { - return array_iterator{ rhs.iter_ + lhs }; - } - - TOML_NODISCARD - friend array_iterator operator-(const array_iterator& lhs, ptrdiff_t rhs) noexcept - { - return array_iterator{ lhs.iter_ - rhs }; - } - - TOML_PURE_INLINE_GETTER - friend ptrdiff_t operator-(const array_iterator& lhs, const array_iterator& rhs) noexcept - { - return lhs.iter_ - rhs.iter_; - } - - TOML_PURE_INLINE_GETTER - friend bool operator==(const array_iterator& lhs, const array_iterator& rhs) noexcept - { - return lhs.iter_ == rhs.iter_; - } - - TOML_PURE_INLINE_GETTER - friend bool operator!=(const array_iterator& lhs, const array_iterator& rhs) noexcept - { - return lhs.iter_ != rhs.iter_; - } - - TOML_PURE_INLINE_GETTER - friend bool operator<(const array_iterator& lhs, const array_iterator& rhs) noexcept - { - return lhs.iter_ < rhs.iter_; - } - - TOML_PURE_INLINE_GETTER - friend bool operator<=(const array_iterator& lhs, const array_iterator& rhs) noexcept - { - return lhs.iter_ <= rhs.iter_; - } - - TOML_PURE_INLINE_GETTER - friend bool operator>(const array_iterator& lhs, const array_iterator& rhs) noexcept - { - return lhs.iter_ > rhs.iter_; - } - - TOML_PURE_INLINE_GETTER - friend bool operator>=(const array_iterator& lhs, const array_iterator& rhs) noexcept - { - return lhs.iter_ >= rhs.iter_; - } - - TOML_PURE_INLINE_GETTER - reference operator[](ptrdiff_t idx) const noexcept - { - return *(iter_ + idx)->get(); - } - }; - - struct array_init_elem - { - mutable node_ptr value; - - template <typename T> - TOML_NODISCARD_CTOR - array_init_elem(T&& val, value_flags flags = preserve_source_value_flags) // - : value{ make_node(static_cast<T&&>(val), flags) } - {} - }; +TOML_IMPL_NAMESPACE_START { + template <bool IsConst> + class TOML_TRIVIAL_ABI array_iterator { + private: + template <bool> + friend class array_iterator; + + using mutable_vector_iterator = std::vector<node_ptr>::iterator; + using const_vector_iterator = std::vector<node_ptr>::const_iterator; + using vector_iterator = + std::conditional_t<IsConst, const_vector_iterator, mutable_vector_iterator>; + + mutable vector_iterator iter_; + + public: + using value_type = std::conditional_t<IsConst, const node, node>; + using reference = value_type&; + using pointer = value_type*; + using difference_type = ptrdiff_t; + using iterator_category = typename std::iterator_traits<vector_iterator>::iterator_category; + + TOML_NODISCARD_CTOR + array_iterator() noexcept = default; + + TOML_NODISCARD_CTOR + explicit array_iterator(mutable_vector_iterator iter) noexcept // + : iter_{iter} {} + + TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst) + TOML_NODISCARD_CTOR + explicit array_iterator(const_vector_iterator iter) noexcept // + : iter_{iter} {} + + TOML_CONSTRAINED_TEMPLATE(C, bool C = IsConst) + TOML_NODISCARD_CTOR + array_iterator(const array_iterator<false>& other) noexcept // + : iter_{other.iter_} {} + + TOML_NODISCARD_CTOR + array_iterator(const array_iterator&) noexcept = default; + + array_iterator& operator=(const array_iterator&) noexcept = default; + + array_iterator& operator++() noexcept // ++pre + { + ++iter_; + return *this; + } + + array_iterator operator++(int) noexcept // post++ + { + array_iterator out{iter_}; + ++iter_; + return out; + } + + array_iterator& operator--() noexcept // --pre + { + --iter_; + return *this; + } + + array_iterator operator--(int) noexcept // post-- + { + array_iterator out{iter_}; + --iter_; + return out; + } + + TOML_PURE_INLINE_GETTER + reference operator*() const noexcept { return *iter_->get(); } + + TOML_PURE_INLINE_GETTER + pointer operator->() const noexcept { return iter_->get(); } + + TOML_PURE_INLINE_GETTER + explicit operator const vector_iterator&() const noexcept { return iter_; } + + TOML_CONSTRAINED_TEMPLATE(!C, bool C = IsConst) + TOML_PURE_INLINE_GETTER + explicit operator const const_vector_iterator() const noexcept { return iter_; } + + array_iterator& operator+=(ptrdiff_t rhs) noexcept { + iter_ += rhs; + return *this; + } + + array_iterator& operator-=(ptrdiff_t rhs) noexcept { + iter_ -= rhs; + return *this; + } + + TOML_NODISCARD + friend array_iterator operator+(const array_iterator& lhs, ptrdiff_t rhs) noexcept { + return array_iterator{lhs.iter_ + rhs}; + } + + TOML_NODISCARD + friend array_iterator operator+(ptrdiff_t lhs, const array_iterator& rhs) noexcept { + return array_iterator{rhs.iter_ + lhs}; + } + + TOML_NODISCARD + friend array_iterator operator-(const array_iterator& lhs, ptrdiff_t rhs) noexcept { + return array_iterator{lhs.iter_ - rhs}; + } + + TOML_PURE_INLINE_GETTER + friend ptrdiff_t operator-(const array_iterator& lhs, const array_iterator& rhs) noexcept { + return lhs.iter_ - rhs.iter_; + } + + TOML_PURE_INLINE_GETTER + friend bool operator==(const array_iterator& lhs, const array_iterator& rhs) noexcept { + return lhs.iter_ == rhs.iter_; + } + + TOML_PURE_INLINE_GETTER + friend bool operator!=(const array_iterator& lhs, const array_iterator& rhs) noexcept { + return lhs.iter_ != rhs.iter_; + } + + TOML_PURE_INLINE_GETTER + friend bool operator<(const array_iterator& lhs, const array_iterator& rhs) noexcept { + return lhs.iter_ < rhs.iter_; + } + + TOML_PURE_INLINE_GETTER + friend bool operator<=(const array_iterator& lhs, const array_iterator& rhs) noexcept { + return lhs.iter_ <= rhs.iter_; + } + + TOML_PURE_INLINE_GETTER + friend bool operator>(const array_iterator& lhs, const array_iterator& rhs) noexcept { + return lhs.iter_ > rhs.iter_; + } + + TOML_PURE_INLINE_GETTER + friend bool operator>=(const array_iterator& lhs, const array_iterator& rhs) noexcept { + return lhs.iter_ >= rhs.iter_; + } + + TOML_PURE_INLINE_GETTER + reference operator[](ptrdiff_t idx) const noexcept { return *(iter_ + idx)->get(); } + }; + + struct array_init_elem { + mutable node_ptr value; + + template <typename T> + TOML_NODISCARD_CTOR array_init_elem(T&& val, + value_flags flags = preserve_source_value_flags) // + : value{make_node(static_cast<T&&>(val), flags)} {} + }; } TOML_IMPL_NAMESPACE_END; /// \endcond -TOML_NAMESPACE_START -{ - /// \brief A RandomAccessIterator for iterating over elements in a toml::array. - using array_iterator = POXY_IMPLEMENTATION_DETAIL(impl::array_iterator<false>); - - /// \brief A RandomAccessIterator for iterating over const elements in a toml::array. - using const_array_iterator = POXY_IMPLEMENTATION_DETAIL(impl::array_iterator<true>); - - /// \brief A TOML array. - /// - /// \detail The interface of this type is modeled after std::vector, with some - /// additional considerations made for the heterogeneous nature of a - /// TOML array. - /// - /// \godbolt{sjK4da} - /// - /// \cpp - /// - /// toml::table tbl = toml::parse(R"( - /// arr = [1, 2, 3, 4, 'five'] - /// )"sv); - /// - /// // get the element as an array - /// toml::array& arr = *tbl.get_as<toml::array>("arr"); - /// std::cout << arr << "\n"; - /// - /// // increment each element with visit() - /// for (auto&& elem : arr) - /// { - /// elem.visit([](auto&& el) noexcept - /// { - /// if constexpr (toml::is_number<decltype(el)>) - /// (*el)++; - /// else if constexpr (toml::is_string<decltype(el)>) - /// el = "six"sv; - /// }); - /// } - /// std::cout << arr << "\n"; - /// - /// // add and remove elements - /// arr.push_back(7); - /// arr.push_back(8.0f); - /// arr.push_back("nine"sv); - /// arr.erase(arr.cbegin()); - /// std::cout << arr << "\n"; - /// - /// // emplace elements - /// arr.emplace_back("ten"); - /// arr.emplace_back<toml::array>(11, 12.0); - /// std::cout << arr << "\n"; - /// \ecpp - /// - /// \out - /// [ 1, 2, 3, 4, 'five' ] - /// [ 2, 3, 4, 5, 'six' ] - /// [ 3, 4, 5, 'six', 7, 8.0, 'nine' ] - /// [ 3, 4, 5, 'six', 7, 8.0, 'nine', 'ten', [ 11, 12.0 ] ] - /// \eout - class TOML_EXPORTED_CLASS array : public node - { - private: - /// \cond - - using vector_type = std::vector<impl::node_ptr>; - using vector_iterator = typename vector_type::iterator; - using const_vector_iterator = typename vector_type::const_iterator; - vector_type elems_; - - TOML_NODISCARD_CTOR - TOML_EXPORTED_MEMBER_FUNCTION - array(const impl::array_init_elem*, const impl::array_init_elem*); - - TOML_NODISCARD_CTOR - array(std::false_type, std::initializer_list<impl::array_init_elem> elems) // - : array{ elems.begin(), elems.end() } - {} - - TOML_EXPORTED_MEMBER_FUNCTION - void preinsertion_resize(size_t idx, size_t count); - - TOML_EXPORTED_MEMBER_FUNCTION - void insert_at_back(impl::node_ptr&&); - - TOML_EXPORTED_MEMBER_FUNCTION - vector_iterator insert_at(const_vector_iterator, impl::node_ptr&&); - - template <typename T> - void emplace_back_if_not_empty_view(T&& val, value_flags flags) - { - if constexpr (is_node_view<T>) - { - if (!val) - return; - } - insert_at_back(impl::make_node(static_cast<T&&>(val), flags)); - } - - TOML_NODISCARD - TOML_EXPORTED_MEMBER_FUNCTION - size_t total_leaf_count() const noexcept; - - TOML_EXPORTED_MEMBER_FUNCTION - void flatten_child(array&& child, size_t& dest_index) noexcept; - - /// \endcond - - public: - using value_type = node; - using size_type = size_t; - using difference_type = ptrdiff_t; - using reference = node&; - using const_reference = const node&; - - /// \brief Default constructor. - TOML_NODISCARD_CTOR - TOML_EXPORTED_MEMBER_FUNCTION - array() noexcept; - - TOML_EXPORTED_MEMBER_FUNCTION - ~array() noexcept; - - /// \brief Copy constructor. - TOML_NODISCARD_CTOR - TOML_EXPORTED_MEMBER_FUNCTION - array(const array&); - - /// \brief Move constructor. - TOML_NODISCARD_CTOR - TOML_EXPORTED_MEMBER_FUNCTION - array(array&& other) noexcept; - - /// \brief Constructs an array with one or more initial elements. - /// - /// \detail \cpp - /// auto arr = toml::array{ 1, 2.0, "three"sv, toml::array{ 4, 5 } }; - /// std::cout << arr << "\n"; - /// \ecpp - /// - /// \out - /// [ 1, 2.0, 'three', [ 4, 5 ] ] - /// \eout - /// - /// \remark \parblock If you need to construct an array with one child array element, the array's move constructor - /// will take precedence and perform a move-construction instead. You can use toml::inserter to - /// suppress this behaviour: \cpp - /// // desired result: [ [ 42 ] ] - /// auto bad = toml::array{ toml::array{ 42 } } - /// auto good = toml::array{ toml::inserter{ toml::array{ 42 } } } - /// std::cout << "bad: " << bad << "\n"; - /// std::cout << "good:" << good << "\n"; - /// \ecpp - /// - /// \out - /// bad: [ 42 ] - /// good: [ [ 42 ] ] - /// \eout - /// - /// \endparblock - /// - /// \tparam ElemType One of the TOML node or value types (or a type promotable to one). - /// \tparam ElemTypes One of the TOML node or value types (or a type promotable to one). - /// \param val The node or value used to initialize element 0. - /// \param vals The nodes or values used to initialize elements 1...N. - TOML_CONSTRAINED_TEMPLATE((sizeof...(ElemTypes) > 0 || !std::is_same_v<impl::remove_cvref<ElemType>, array>), - typename ElemType, - typename... ElemTypes) - TOML_NODISCARD_CTOR - explicit array(ElemType&& val, ElemTypes&&... vals) - : array{ std::false_type{}, - std::initializer_list<impl::array_init_elem>{ static_cast<ElemType&&>(val), - static_cast<ElemTypes&&>(vals)... } } - {} - - /// \brief Copy-assignment operator. - TOML_EXPORTED_MEMBER_FUNCTION - array& operator=(const array&); - - /// \brief Move-assignment operator. - TOML_EXPORTED_MEMBER_FUNCTION - array& operator=(array&& rhs) noexcept; - - /// \name Type checks - /// @{ - - /// \brief Returns #toml::node_type::array. - TOML_CONST_INLINE_GETTER - node_type type() const noexcept final - { - return node_type::array; - } - - 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 array::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 `false`. - TOML_CONST_INLINE_GETTER - bool is_table() const noexcept final - { - return false; - } - - /// \brief Returns `true`. - TOML_CONST_INLINE_GETTER - bool is_array() const noexcept final - { - return true; - } - - /// \brief Returns `true` if the array contains only tables. - TOML_PURE_GETTER - bool is_array_of_tables() const noexcept final - { - return is_homogeneous(node_type::table); - } - - /// \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 `nullptr`. - TOML_CONST_INLINE_GETTER - table* as_table() noexcept final - { - return nullptr; - } - - /// \brief Returns a pointer to the array. - TOML_CONST_INLINE_GETTER - array* as_array() noexcept final - { - return this; - } - - /// \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 `nullptr`. - TOML_CONST_INLINE_GETTER - const table* as_table() const noexcept final - { - return nullptr; - } - - /// \brief Returns a const-qualified pointer to the array. - TOML_CONST_INLINE_GETTER - const array* as_array() const noexcept final - { - return this; - } - - /// \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 Value retrieval - /// @{ - - /// \brief Gets a pointer to the element at a specific index. - /// - /// \detail \cpp - /// auto arr = toml::array{ 99, "bottles of beer on the wall" }; - /// std::cout << "element [0] exists: "sv << !!arr.get(0) << "\n"; - /// std::cout << "element [1] exists: "sv << !!arr.get(1) << "\n"; - /// 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 - /// element [0] exists: true - /// element [1] exists: true - /// element [2] exists: false - /// element [0] is an integer - /// \eout - /// - /// \param index The element's index. - /// - /// \returns A pointer to the element at the specified index if one existed, or nullptr. - TOML_PURE_INLINE_GETTER - node* get(size_t index) noexcept - { - return index < elems_.size() ? elems_[index].get() : nullptr; - } - - /// \brief Gets a pointer to the element at a specific index (const overload). - /// - /// \param index The element's index. - /// - /// \returns A pointer to the element at the specified index if one existed, or nullptr. - TOML_PURE_INLINE_GETTER - const node* get(size_t index) const noexcept - { - return const_cast<array&>(*this).get(index); - } - - /// \brief Gets a pointer to the element at a specific index if it is a particular type. - /// - /// \detail \cpp - /// auto arr = toml::array{ 42, "is the meaning of life, apparently."sv }; - /// if (toml::value<int64_t>* val = arr.get_as<int64_t>(0)) - /// std::cout << "element [0] is an integer with value "sv << *val << "\n"; - /// \ecpp - /// - /// \out - /// element [0] is an integer with value 42 - /// \eout - /// - /// \tparam ElemType toml::table, toml::array, or a native TOML value type - /// \param index The element's index. - /// - /// \returns A pointer to the selected element if it existed and was of the specified type, or nullptr. - template <typename ElemType> - TOML_NODISCARD - impl::wrap_node<ElemType>* get_as(size_t index) noexcept - { - if (auto val = get(index)) - return val->template as<ElemType>(); - return nullptr; - } - - /// \brief Gets a pointer to the element at a specific index if it is a particular type (const overload). - /// - /// \tparam ElemType toml::table, toml::array, or a native TOML value type - /// \param index The element's index. - /// - /// \returns A pointer to the selected element if it existed and was of the specified type, or nullptr. - template <typename ElemType> - TOML_NODISCARD - const impl::wrap_node<ElemType>* get_as(size_t index) const noexcept - { - return const_cast<array&>(*this).template get_as<ElemType>(index); - } - - /// \cond - using node::operator[]; // inherit operator[toml::path] - /// \endcond - - /// \brief Gets a reference to the element at a specific index. - TOML_NODISCARD - node& operator[](size_t index) noexcept - { - return *elems_[index]; - } - - /// \brief Gets a reference to the element at a specific index. - TOML_NODISCARD - const node& operator[](size_t index) const noexcept - { - return *elems_[index]; - } - - /// \brief Gets a reference to the element at a specific index, throwing `std::out_of_range` if none existed. - TOML_NODISCARD - TOML_EXPORTED_MEMBER_FUNCTION - node& at(size_t index); - - /// \brief Gets a reference to the element at a specific index, throwing `std::out_of_range` if none existed. - TOML_NODISCARD - const node& at(size_t index) const - { - return const_cast<array&>(*this).at(index); - } - - /// \brief Returns a reference to the first element in the array. - TOML_NODISCARD - node& front() noexcept - { - return *elems_.front(); - } - - /// \brief Returns a reference to the first element in the array. - TOML_NODISCARD - const node& front() const noexcept - { - return *elems_.front(); - } - - /// \brief Returns a reference to the last element in the array. - TOML_NODISCARD - node& back() noexcept - { - return *elems_.back(); - } - - /// \brief Returns a reference to the last element in the array. - TOML_NODISCARD - const node& back() const noexcept - { - return *elems_.back(); - } - - /// @} - - /// \name Iteration - /// @{ - - /// \brief A RandomAccessIterator for iterating over elements in a toml::array. - using iterator = array_iterator; - - /// \brief A RandomAccessIterator for iterating over const elements in a toml::array. - using const_iterator = const_array_iterator; - - /// \brief Returns an iterator to the first element. - TOML_NODISCARD - iterator begin() noexcept - { - return iterator{ elems_.begin() }; - } - - /// \brief Returns an iterator to the first element. - TOML_NODISCARD - const_iterator begin() const noexcept - { - return const_iterator{ elems_.cbegin() }; - } - - /// \brief Returns an iterator to the first element. - TOML_NODISCARD - const_iterator cbegin() const noexcept - { - return const_iterator{ elems_.cbegin() }; - } - - /// \brief Returns an iterator to one-past-the-last element. - TOML_NODISCARD - iterator end() noexcept - { - return iterator{ elems_.end() }; - } - - /// \brief Returns an iterator to one-past-the-last element. - TOML_NODISCARD - const_iterator end() const noexcept - { - return const_iterator{ elems_.cend() }; - } - - /// \brief Returns an iterator to one-past-the-last element. - TOML_NODISCARD - const_iterator cend() const noexcept - { - return const_iterator{ elems_.cend() }; - } - - private: - /// \cond - - template <typename T, typename Array> - using for_each_elem_ref = impl::copy_cvref<impl::wrap_node<impl::remove_cvref<impl::unwrap_node<T>>>, Array>; - - template <typename Func, typename Array, typename T> - using can_for_each = std::disjunction<std::is_invocable<Func, for_each_elem_ref<T, Array>, size_t>, - std::is_invocable<Func, size_t, for_each_elem_ref<T, Array>>, - std::is_invocable<Func, for_each_elem_ref<T, Array>>>; - - template <typename Func, typename Array, typename T> - using can_for_each_nothrow = std::conditional_t< - // first form - std::is_invocable_v<Func, for_each_elem_ref<T, Array>, size_t>, - std::is_nothrow_invocable<Func, for_each_elem_ref<T, Array>, size_t>, - std::conditional_t< - // second form - std::is_invocable_v<Func, size_t, for_each_elem_ref<T, Array>>, - std::is_nothrow_invocable<Func, size_t, for_each_elem_ref<T, Array>>, - std::conditional_t< - // third form - std::is_invocable_v<Func, for_each_elem_ref<T, Array>>, - std::is_nothrow_invocable<Func, for_each_elem_ref<T, Array>>, - std::false_type>>>; - - template <typename Func, typename Array> - using can_for_each_any = std::disjunction<can_for_each<Func, Array, table>, - can_for_each<Func, Array, array>, - can_for_each<Func, Array, std::string>, - can_for_each<Func, Array, int64_t>, - can_for_each<Func, Array, double>, - can_for_each<Func, Array, bool>, - can_for_each<Func, Array, date>, - can_for_each<Func, Array, time>, - can_for_each<Func, Array, date_time>>; - - template <typename Func, typename Array, typename T> - using for_each_is_nothrow_one = std::disjunction<std::negation<can_for_each<Func, Array, T>>, // - can_for_each_nothrow<Func, Array, T>>; - - template <typename Func, typename Array> - using for_each_is_nothrow = std::conjunction<for_each_is_nothrow_one<Func, Array, table>, - for_each_is_nothrow_one<Func, Array, array>, - for_each_is_nothrow_one<Func, Array, std::string>, - for_each_is_nothrow_one<Func, Array, int64_t>, - for_each_is_nothrow_one<Func, Array, double>, - for_each_is_nothrow_one<Func, Array, bool>, - for_each_is_nothrow_one<Func, Array, date>, - for_each_is_nothrow_one<Func, Array, time>, - for_each_is_nothrow_one<Func, Array, date_time>>; - - template <typename Func, typename Array> - static void do_for_each(Func&& visitor, Array&& arr) // - noexcept(for_each_is_nothrow<Func&&, Array&&>::value) - { - static_assert(can_for_each_any<Func&&, Array&&>::value, - "TOML array for_each visitors must be invocable for at least one of the toml::node " - "specializations:" TOML_SA_NODE_TYPE_LIST); - - for (size_t i = 0; i < arr.size(); i++) - { - using node_ref = impl::copy_cvref<toml::node, Array&&>; - static_assert(std::is_reference_v<node_ref>); +TOML_NAMESPACE_START { + /// \brief A RandomAccessIterator for iterating over elements in a toml::array. + using array_iterator = POXY_IMPLEMENTATION_DETAIL(impl::array_iterator<false>); + + /// \brief A RandomAccessIterator for iterating over const elements in a toml::array. + using const_array_iterator = POXY_IMPLEMENTATION_DETAIL(impl::array_iterator<true>); + + /// \brief A TOML array. + /// + /// \detail The interface of this type is modeled after std::vector, with some + /// additional considerations made for the heterogeneous nature of a + /// TOML array. + /// + /// \godbolt{sjK4da} + /// + /// \cpp + /// + /// toml::table tbl = toml::parse(R"( + /// arr = [1, 2, 3, 4, 'five'] + /// )"sv); + /// + /// // get the element as an array + /// toml::array& arr = *tbl.get_as<toml::array>("arr"); + /// std::cout << arr << "\n"; + /// + /// // increment each element with visit() + /// for (auto&& elem : arr) + /// { + /// elem.visit([](auto&& el) noexcept + /// { + /// if constexpr (toml::is_number<decltype(el)>) + /// (*el)++; + /// else if constexpr (toml::is_string<decltype(el)>) + /// el = "six"sv; + /// }); + /// } + /// std::cout << arr << "\n"; + /// + /// // add and remove elements + /// arr.push_back(7); + /// arr.push_back(8.0f); + /// arr.push_back("nine"sv); + /// arr.erase(arr.cbegin()); + /// std::cout << arr << "\n"; + /// + /// // emplace elements + /// arr.emplace_back("ten"); + /// arr.emplace_back<toml::array>(11, 12.0); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2, 3, 4, 'five' ] + /// [ 2, 3, 4, 5, 'six' ] + /// [ 3, 4, 5, 'six', 7, 8.0, 'nine' ] + /// [ 3, 4, 5, 'six', 7, 8.0, 'nine', 'ten', [ 11, 12.0 ] ] + /// \eout + class TOML_EXPORTED_CLASS array : public node { + private: + /// \cond + + using vector_type = std::vector<impl::node_ptr>; + using vector_iterator = typename vector_type::iterator; + using const_vector_iterator = typename vector_type::const_iterator; + vector_type elems_; + + TOML_NODISCARD_CTOR + TOML_EXPORTED_MEMBER_FUNCTION + array(const impl::array_init_elem*, const impl::array_init_elem*); + + TOML_NODISCARD_CTOR + array(std::false_type, std::initializer_list<impl::array_init_elem> elems) // + : array{elems.begin(), elems.end()} {} + + TOML_EXPORTED_MEMBER_FUNCTION + void preinsertion_resize(size_t idx, size_t count); + + TOML_EXPORTED_MEMBER_FUNCTION + void insert_at_back(impl::node_ptr&&); + + TOML_EXPORTED_MEMBER_FUNCTION + vector_iterator insert_at(const_vector_iterator, impl::node_ptr&&); + + template <typename T> + void emplace_back_if_not_empty_view(T&& val, value_flags flags) { + if constexpr (is_node_view<T>) { + if (!val) return; + } + insert_at_back(impl::make_node(static_cast<T&&>(val), flags)); + } + + TOML_NODISCARD + TOML_EXPORTED_MEMBER_FUNCTION + size_t total_leaf_count() const noexcept; + + TOML_EXPORTED_MEMBER_FUNCTION + void flatten_child(array&& child, size_t& dest_index) noexcept; + + /// \endcond + + public: + using value_type = node; + using size_type = size_t; + using difference_type = ptrdiff_t; + using reference = node&; + using const_reference = const node&; + + /// \brief Default constructor. + TOML_NODISCARD_CTOR + TOML_EXPORTED_MEMBER_FUNCTION + array() noexcept; + + TOML_EXPORTED_MEMBER_FUNCTION + ~array() noexcept; + + /// \brief Copy constructor. + TOML_NODISCARD_CTOR + TOML_EXPORTED_MEMBER_FUNCTION + array(const array&); + + /// \brief Move constructor. + TOML_NODISCARD_CTOR + TOML_EXPORTED_MEMBER_FUNCTION + array(array&& other) noexcept; + + /// \brief Constructs an array with one or more initial elements. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2.0, "three"sv, toml::array{ 4, 5 } }; + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2.0, 'three', [ 4, 5 ] ] + /// \eout + /// + /// \remark \parblock If you need to construct an array with one child array element, the + /// array's move constructor will take precedence and perform a move-construction instead. You + /// can use toml::inserter to suppress this behaviour: \cpp + /// // desired result: [ [ 42 ] ] + /// auto bad = toml::array{ toml::array{ 42 } } + /// auto good = toml::array{ toml::inserter{ toml::array{ 42 } } } + /// std::cout << "bad: " << bad << "\n"; + /// std::cout << "good:" << good << "\n"; + /// \ecpp + /// + /// \out + /// bad: [ 42 ] + /// good: [ [ 42 ] ] + /// \eout + /// + /// \endparblock + /// + /// \tparam ElemType One of the TOML node or value types (or a type promotable to one). + /// \tparam ElemTypes One of the TOML node or value types (or a type promotable to one). + /// \param val The node or value used to initialize element 0. + /// \param vals The nodes or values used to initialize elements 1...N. + TOML_CONSTRAINED_TEMPLATE((sizeof...(ElemTypes) > 0 || + !std::is_same_v<impl::remove_cvref<ElemType>, array>), + typename ElemType, typename... ElemTypes) + TOML_NODISCARD_CTOR + explicit array(ElemType&& val, ElemTypes&&... vals) + : array{std::false_type{}, + std::initializer_list<impl::array_init_elem>{static_cast<ElemType&&>(val), + static_cast<ElemTypes&&>(vals)...}} {} + + /// \brief Copy-assignment operator. + TOML_EXPORTED_MEMBER_FUNCTION + array& operator=(const array&); + + /// \brief Move-assignment operator. + TOML_EXPORTED_MEMBER_FUNCTION + array& operator=(array&& rhs) noexcept; + + /// \name Type checks + /// @{ + + /// \brief Returns #toml::node_type::array. + TOML_CONST_INLINE_GETTER + node_type type() const noexcept final { return node_type::array; } + + 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 array::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 `false`. + TOML_CONST_INLINE_GETTER + bool is_table() const noexcept final { return false; } + + /// \brief Returns `true`. + TOML_CONST_INLINE_GETTER + bool is_array() const noexcept final { return true; } + + /// \brief Returns `true` if the array contains only tables. + TOML_PURE_GETTER + bool is_array_of_tables() const noexcept final { return is_homogeneous(node_type::table); } + + /// \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 `nullptr`. + TOML_CONST_INLINE_GETTER + table* as_table() noexcept final { return nullptr; } + + /// \brief Returns a pointer to the array. + TOML_CONST_INLINE_GETTER + array* as_array() noexcept final { return this; } + + /// \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 `nullptr`. + TOML_CONST_INLINE_GETTER + const table* as_table() const noexcept final { return nullptr; } + + /// \brief Returns a const-qualified pointer to the array. + TOML_CONST_INLINE_GETTER + const array* as_array() const noexcept final { return this; } + + /// \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 Value retrieval + /// @{ + + /// \brief Gets a pointer to the element at a specific index. + /// + /// \detail \cpp + /// auto arr = toml::array{ 99, "bottles of beer on the wall" }; + /// std::cout << "element [0] exists: "sv << !!arr.get(0) << "\n"; + /// std::cout << "element [1] exists: "sv << !!arr.get(1) << "\n"; + /// 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 + /// element [0] exists: true + /// element [1] exists: true + /// element [2] exists: false + /// element [0] is an integer + /// \eout + /// + /// \param index The element's index. + /// + /// \returns A pointer to the element at the specified index if one existed, or nullptr. + TOML_PURE_INLINE_GETTER + node* get(size_t index) noexcept { + return index < elems_.size() ? elems_[index].get() : nullptr; + } + + /// \brief Gets a pointer to the element at a specific index (const overload). + /// + /// \param index The element's index. + /// + /// \returns A pointer to the element at the specified index if one existed, or nullptr. + TOML_PURE_INLINE_GETTER + const node* get(size_t index) const noexcept { return const_cast<array&>(*this).get(index); } + + /// \brief Gets a pointer to the element at a specific index if it is a particular type. + /// + /// \detail \cpp + /// auto arr = toml::array{ 42, "is the meaning of life, apparently."sv }; + /// if (toml::value<int64_t>* val = arr.get_as<int64_t>(0)) + /// std::cout << "element [0] is an integer with value "sv << *val << "\n"; + /// \ecpp + /// + /// \out + /// element [0] is an integer with value 42 + /// \eout + /// + /// \tparam ElemType toml::table, toml::array, or a native TOML value type + /// \param index The element's index. + /// + /// \returns A pointer to the selected element if it existed and was of the specified type, or + /// nullptr. + template <typename ElemType> + TOML_NODISCARD impl::wrap_node<ElemType>* get_as(size_t index) noexcept { + if (auto val = get(index)) return val->template as<ElemType>(); + return nullptr; + } + + /// \brief Gets a pointer to the element at a specific index if it is a particular type (const + /// overload). + /// + /// \tparam ElemType toml::table, toml::array, or a native TOML value type + /// \param index The element's index. + /// + /// \returns A pointer to the selected element if it existed and was of the specified type, or + /// nullptr. + template <typename ElemType> + TOML_NODISCARD const impl::wrap_node<ElemType>* get_as(size_t index) const noexcept { + return const_cast<array&>(*this).template get_as<ElemType>(index); + } + + /// \cond + using node::operator[]; // inherit operator[toml::path] + /// \endcond + + /// \brief Gets a reference to the element at a specific index. + TOML_NODISCARD + node& operator[](size_t index) noexcept { return *elems_[index]; } + + /// \brief Gets a reference to the element at a specific index. + TOML_NODISCARD + const node& operator[](size_t index) const noexcept { return *elems_[index]; } + + /// \brief Gets a reference to the element at a specific index, throwing `std::out_of_range` if + /// none existed. + TOML_NODISCARD + TOML_EXPORTED_MEMBER_FUNCTION + node& at(size_t index); + + /// \brief Gets a reference to the element at a specific index, throwing `std::out_of_range` if + /// none existed. + TOML_NODISCARD + const node& at(size_t index) const { return const_cast<array&>(*this).at(index); } + + /// \brief Returns a reference to the first element in the array. + TOML_NODISCARD + node& front() noexcept { return *elems_.front(); } + + /// \brief Returns a reference to the first element in the array. + TOML_NODISCARD + const node& front() const noexcept { return *elems_.front(); } + + /// \brief Returns a reference to the last element in the array. + TOML_NODISCARD + node& back() noexcept { return *elems_.back(); } + + /// \brief Returns a reference to the last element in the array. + TOML_NODISCARD + const node& back() const noexcept { return *elems_.back(); } + + /// @} + + /// \name Iteration + /// @{ + + /// \brief A RandomAccessIterator for iterating over elements in a toml::array. + using iterator = array_iterator; + + /// \brief A RandomAccessIterator for iterating over const elements in a toml::array. + using const_iterator = const_array_iterator; + + /// \brief Returns an iterator to the first element. + TOML_NODISCARD + iterator begin() noexcept { return iterator{elems_.begin()}; } + + /// \brief Returns an iterator to the first element. + TOML_NODISCARD + const_iterator begin() const noexcept { return const_iterator{elems_.cbegin()}; } + + /// \brief Returns an iterator to the first element. + TOML_NODISCARD + const_iterator cbegin() const noexcept { return const_iterator{elems_.cbegin()}; } + + /// \brief Returns an iterator to one-past-the-last element. + TOML_NODISCARD + iterator end() noexcept { return iterator{elems_.end()}; } + + /// \brief Returns an iterator to one-past-the-last element. + TOML_NODISCARD + const_iterator end() const noexcept { return const_iterator{elems_.cend()}; } + + /// \brief Returns an iterator to one-past-the-last element. + TOML_NODISCARD + const_iterator cend() const noexcept { return const_iterator{elems_.cend()}; } + + private: + /// \cond + + template <typename T, typename Array> + using for_each_elem_ref = + impl::copy_cvref<impl::wrap_node<impl::remove_cvref<impl::unwrap_node<T>>>, Array>; + + template <typename Func, typename Array, typename T> + using can_for_each = + std::disjunction<std::is_invocable<Func, for_each_elem_ref<T, Array>, size_t>, + std::is_invocable<Func, size_t, for_each_elem_ref<T, Array>>, + std::is_invocable<Func, for_each_elem_ref<T, Array>>>; + + template <typename Func, typename Array, typename T> + using can_for_each_nothrow = std::conditional_t< + // first form + std::is_invocable_v<Func, for_each_elem_ref<T, Array>, size_t>, + std::is_nothrow_invocable<Func, for_each_elem_ref<T, Array>, size_t>, + std::conditional_t< + // second form + std::is_invocable_v<Func, size_t, for_each_elem_ref<T, Array>>, + std::is_nothrow_invocable<Func, size_t, for_each_elem_ref<T, Array>>, + std::conditional_t< + // third form + std::is_invocable_v<Func, for_each_elem_ref<T, Array>>, + std::is_nothrow_invocable<Func, for_each_elem_ref<T, Array>>, std::false_type>>>; + + template <typename Func, typename Array> + using can_for_each_any = + std::disjunction<can_for_each<Func, Array, table>, can_for_each<Func, Array, array>, + can_for_each<Func, Array, std::string>, can_for_each<Func, Array, int64_t>, + can_for_each<Func, Array, double>, can_for_each<Func, Array, bool>, + can_for_each<Func, Array, date>, can_for_each<Func, Array, time>, + can_for_each<Func, Array, date_time>>; + + template <typename Func, typename Array, typename T> + using for_each_is_nothrow_one = + std::disjunction<std::negation<can_for_each<Func, Array, T>>, // + can_for_each_nothrow<Func, Array, T>>; + + template <typename Func, typename Array> + using for_each_is_nothrow = std::conjunction< + for_each_is_nothrow_one<Func, Array, table>, for_each_is_nothrow_one<Func, Array, array>, + for_each_is_nothrow_one<Func, Array, std::string>, + for_each_is_nothrow_one<Func, Array, int64_t>, for_each_is_nothrow_one<Func, Array, double>, + for_each_is_nothrow_one<Func, Array, bool>, for_each_is_nothrow_one<Func, Array, date>, + for_each_is_nothrow_one<Func, Array, time>, + for_each_is_nothrow_one<Func, Array, date_time>>; + + template <typename Func, typename Array> + static void do_for_each(Func&& visitor, Array&& arr) // + noexcept(for_each_is_nothrow<Func&&, Array&&>::value) { + static_assert( + can_for_each_any<Func&&, Array&&>::value, + "TOML array for_each visitors must be invocable for at least one of the toml::node " + "specializations:" TOML_SA_NODE_TYPE_LIST); + + for (size_t i = 0; i < arr.size(); i++) { + using node_ref = impl::copy_cvref<toml::node, Array&&>; + 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, Array, node_ref>, // - TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_MESSAGE); + static_assert(impl::always_false<Func, Array, node_ref>, // + TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN_MESSAGE); #endif - static_cast<node_ref>(static_cast<Array&&>(arr)[i]) - .visit( - [&]([[maybe_unused]] auto&& elem) // - noexcept(for_each_is_nothrow_one<Func&&, Array&&, decltype(elem)>::value) - { - using elem_ref = for_each_elem_ref<decltype(elem), Array&&>; - static_assert(std::is_reference_v<elem_ref>); - - // func(elem, i) - if constexpr (std::is_invocable_v<Func&&, elem_ref, size_t>) - { - static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i); - } - - // func(i, elem) - else if constexpr (std::is_invocable_v<Func&&, size_t, elem_ref>) - { - static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem)); - } - - // func(elem) - else if constexpr (std::is_invocable_v<Func&&, elem_ref>) - { - static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem)); - } - }); + static_cast<node_ref>(static_cast<Array&&>(arr)[i]) + .visit([&]([[maybe_unused]] auto&& elem) // + noexcept(for_each_is_nothrow_one<Func&&, Array&&, decltype(elem)>::value) { + using elem_ref = for_each_elem_ref<decltype(elem), Array&&>; + static_assert(std::is_reference_v<elem_ref>); + + // func(elem, i) + if constexpr (std::is_invocable_v<Func&&, elem_ref, size_t>) { + static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i); + } + + // func(i, elem) + else if constexpr (std::is_invocable_v<Func&&, size_t, elem_ref>) { + static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem)); + } + + // func(elem) + else if constexpr (std::is_invocable_v<Func&&, elem_ref>) { + static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem)); + } + }); #else - const auto keep_going = - static_cast<node_ref>(static_cast<Array&&>(arr)[i]) - .visit( - [&]([[maybe_unused]] auto&& elem) // - noexcept(for_each_is_nothrow_one<Func&&, Array&&, decltype(elem)>::value) - { - using elem_ref = for_each_elem_ref<decltype(elem), Array&&>; - static_assert(std::is_reference_v<elem_ref>); - - // func(elem, i) - if constexpr (std::is_invocable_v<Func&&, elem_ref, size_t>) - { - using return_type = - decltype(static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i)); - - if constexpr (impl::is_constructible_or_convertible<bool, return_type>) - { - return static_cast<bool>( - static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i)); - } - else - { - static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i); - return true; - } - } - - // func(i, elem) - else if constexpr (std::is_invocable_v<Func&&, size_t, elem_ref>) - { - using return_type = - decltype(static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem))); - - if constexpr (impl::is_constructible_or_convertible<bool, return_type>) - { - return static_cast<bool>( - static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem))); - } - else - { - static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem)); - return true; - } - } - - // func(elem) - else if constexpr (std::is_invocable_v<Func&&, elem_ref>) - { - using return_type = - decltype(static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem))); - - if constexpr (impl::is_constructible_or_convertible<bool, return_type>) - { - return static_cast<bool>( - static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem))); - } - else - { - static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem)); - return true; - } - } - - // visitor not compatible with this particular type - else - return true; - }); - - if (!keep_going) - return; + const auto keep_going = + static_cast<node_ref>(static_cast<Array&&>(arr)[i]) + .visit( + [&]([[maybe_unused]] auto&& elem) // + noexcept(for_each_is_nothrow_one<Func&&, Array&&, decltype(elem)>::value) { + using elem_ref = for_each_elem_ref<decltype(elem), Array&&>; + static_assert(std::is_reference_v<elem_ref>); + + // func(elem, i) + if constexpr (std::is_invocable_v<Func&&, elem_ref, size_t>) { + using return_type = + decltype(static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i)); + + if constexpr (impl::is_constructible_or_convertible<bool, return_type>) { + return static_cast<bool>( + static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i)); + } else { + static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem), i); + return true; + } + } + + // func(i, elem) + else if constexpr (std::is_invocable_v<Func&&, size_t, elem_ref>) { + using return_type = + decltype(static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem))); + + if constexpr (impl::is_constructible_or_convertible<bool, return_type>) { + return static_cast<bool>( + static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem))); + } else { + static_cast<Func&&>(visitor)(i, static_cast<elem_ref>(elem)); + return true; + } + } + + // func(elem) + else if constexpr (std::is_invocable_v<Func&&, elem_ref>) { + using return_type = + decltype(static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem))); + + if constexpr (impl::is_constructible_or_convertible<bool, return_type>) { + return static_cast<bool>( + static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem))); + } else { + static_cast<Func&&>(visitor)(static_cast<elem_ref>(elem)); + 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 element in the array. - /// - /// \tparam Func A callable type invocable with one of the following signatures: - /// <ul> - /// <li> `func(elem, index)` - /// <li> `func(elem)` - /// <li> `func(index, elem)` - /// </ul> - /// Where: - /// <ul> - /// <li> `elem` will recieve the element as it's concrete type with cvref-qualifications matching the array - /// <li> `index` will recieve a `size_t` indicating the element's index - /// </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 array. - /// - /// \details \cpp - /// toml::array arr{ 0, 1, 2, 3.0, "four", "five", 6 }; - /// - /// // select only the integers using a strongly-typed visitor - /// arr.for_each([](toml::value<int64_t>& elem) - /// { - /// std::cout << elem << ", "; - /// }); - /// std::cout << "\n"; - /// - /// // select all the numeric values using a generic visitor + is_number<> metafunction - /// arr.for_each([](auto&& elem) - /// { - /// if constexpr (toml::is_number<decltype(elem)>) - /// std::cout << elem << ", "; - /// }); - /// std::cout << "\n"; - /// - /// // select all the numeric values until we encounter something non-numeric - /// arr.for_each([](auto&& elem) - /// { - /// if constexpr (toml::is_number<decltype(elem)>) - /// { - /// std::cout << elem << ", "; - /// return true; // "keep going" - /// } - /// else - /// return false; // "stop!" - /// - /// }); - /// std::cout << "\n"; - /// - /// \ecpp - /// \out - /// 0, 1, 2, 6, - /// 0, 1, 2, 3.0, 6, - /// 0, 1, 2, 3.0, - /// \eout - /// - /// \see node::visit() - template <typename Func> - array& for_each(Func&& visitor) & // - noexcept(for_each_is_nothrow<Func&&, array&>::value) - { - do_for_each(static_cast<Func&&>(visitor), *this); - return *this; - } - - /// \brief Invokes a visitor on each element in the array (rvalue overload). - template <typename Func> - array&& for_each(Func&& visitor) && // - noexcept(for_each_is_nothrow<Func&&, array&&>::value) - { - do_for_each(static_cast<Func&&>(visitor), static_cast<array&&>(*this)); - return static_cast<array&&>(*this); - } - - /// \brief Invokes a visitor on each element in the array (const lvalue overload). - template <typename Func> - const array& for_each(Func&& visitor) const& // - noexcept(for_each_is_nothrow<Func&&, const array&>::value) - { - do_for_each(static_cast<Func&&>(visitor), *this); - return *this; - } - - /// \brief Invokes a visitor on each element in the array (const rvalue overload). - template <typename Func> - const array&& for_each(Func&& visitor) const&& // - noexcept(for_each_is_nothrow<Func&&, const array&&>::value) - { - do_for_each(static_cast<Func&&>(visitor), static_cast<const array&&>(*this)); - return static_cast<const array&&>(*this); - } - - /// @} - - /// \name Size and Capacity - /// @{ - - /// \brief Returns true if the array is empty. - TOML_NODISCARD - bool empty() const noexcept - { - return elems_.empty(); - } - - /// \brief Returns the number of elements in the array. - TOML_NODISCARD - size_t size() const noexcept - { - return elems_.size(); - } - - /// \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 - { - return elems_.max_size(); - } - - /// \brief Returns the current max number of elements that may be held in the array's internal storage. - TOML_NODISCARD - size_t capacity() const noexcept - { - return elems_.capacity(); - } - - /// \brief Reserves internal storage capacity up to a pre-determined number of elements. - TOML_EXPORTED_MEMBER_FUNCTION - void reserve(size_t new_capacity); - - /// \brief Requests the removal of any unused internal storage capacity. - TOML_EXPORTED_MEMBER_FUNCTION - 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_EXPORTED_MEMBER_FUNCTION - 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 <typename ElemType> - void resize(size_t new_size, - ElemType&& default_init_val, - value_flags default_init_flags = preserve_source_value_flags) - { - static_assert(!is_node_view<ElemType>, - "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<ElemType&&>(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_EXPORTED_MEMBER_FUNCTION - 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_EXPORTED_MEMBER_FUNCTION - 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_EXPORTED_MEMBER_FUNCTION - array& flatten() &; - - /// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself (rvalue overload). - array&& flatten() && - { - return static_cast<toml::array&&>(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_EXPORTED_MEMBER_FUNCTION - 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<toml::array&&>(this->prune(recursive)); - } - - /// \brief Removes the last element from the array. - TOML_EXPORTED_MEMBER_FUNCTION - void pop_back() noexcept; - - /// \brief Removes all elements from the array. - TOML_EXPORTED_MEMBER_FUNCTION - void clear() noexcept; - - /// @} - - /// \name Insertion and Emplacement - /// @{ - - /// \brief Inserts a new element at a specific position in the array. - /// - /// \detail \cpp - /// auto arr = toml::array{ 1, 3 }; - /// arr.insert(arr.cbegin() + 1, "two"); - /// arr.insert(arr.cend(), toml::array{ 4, 5 }); - /// std::cout << arr << "\n"; - /// \ecpp - /// - /// \out - /// [ 1, 'two', 3, [ 4, 5 ] ] - /// \eout - /// - /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type - /// (or a type promotable to one). - /// \param pos The insertion position. - /// \param val The node or value being inserted. - /// \param flags Value flags to apply to new values. - /// - /// \returns \conditional_return{Valid input} - /// An iterator to the newly-inserted element. - /// \conditional_return{Input is a null toml::node_view} - /// end() - /// - /// \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 <typename ElemType> - iterator insert(const_iterator pos, ElemType&& val, value_flags flags = preserve_source_value_flags) - { - if constexpr (is_node_view<ElemType>) - { - if (!val) - return end(); - } - return iterator{ insert_at(const_vector_iterator{ pos }, - impl::make_node(static_cast<ElemType&&>(val), flags)) }; - } - - /// \brief Repeatedly inserts a new element starting at a specific position in the array. - /// - /// \detail \cpp - /// auto arr = toml::array{ - /// "with an evil twinkle in its eye the goose said", - /// "and immediately we knew peace was never an option." - /// }; - /// arr.insert(arr.cbegin() + 1, 3, "honk"); - /// std::cout << arr << "\n"; - /// \ecpp - /// - /// \out - /// [ - /// 'with an evil twinkle in its eye the goose said', - /// 'honk', - /// 'honk', - /// 'honk', - /// 'and immediately we knew peace was never an option.' - /// ] - /// \eout - /// - /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type - /// (or a type promotable to one). - /// \param pos The insertion position. - /// \param count The number of times the node or value should be inserted. - /// \param val The node or value being inserted. - /// \param flags Value flags to apply to new values. - /// - /// \returns \conditional_return{Valid input} - /// An iterator to the newly-inserted element. - /// \conditional_return{count == 0} - /// A copy of pos - /// \conditional_return{Input is a null toml::node_view} - /// end() - /// - /// \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 <typename ElemType> - iterator insert(const_iterator pos, - size_t count, - ElemType&& val, - value_flags flags = preserve_source_value_flags) - { - if constexpr (is_node_view<ElemType>) - { - if (!val) - return end(); - } - switch (count) - { - case 0: return iterator{ elems_.begin() + (const_vector_iterator{ pos } - elems_.cbegin()) }; - case 1: return insert(pos, static_cast<ElemType&&>(val), flags); - default: - { - const auto start_idx = static_cast<size_t>(const_vector_iterator{ pos } - elems_.cbegin()); - preinsertion_resize(start_idx, count); - size_t i = start_idx; - for (size_t e = start_idx + count - 1u; i < e; i++) - elems_[i] = impl::make_node(val, flags); - - //# potentially move the initial value into the last element - elems_[i] = impl::make_node(static_cast<ElemType&&>(val), flags); - return iterator{ elems_.begin() + static_cast<ptrdiff_t>(start_idx) }; - } - } - } - - /// \brief Inserts a range of elements into the array at a specific position. - /// - /// \tparam Iter An iterator type. Must satisfy ForwardIterator. - /// \param pos The insertion position. - /// \param first Iterator to the first node or value being inserted. - /// \param last Iterator to the one-past-the-last node or value being inserted. - /// \param flags Value flags to apply to new values. - /// - /// \returns \conditional_return{Valid input} - /// An iterator to the first newly-inserted element. - /// \conditional_return{first >= last} - /// A copy of pos - /// \conditional_return{All objects in the range were null toml::node_views} - /// A copy of pos - template <typename Iter> - iterator insert(const_iterator pos, Iter first, Iter last, value_flags flags = preserve_source_value_flags) - { - const auto distance = std::distance(first, last); - if (distance <= 0) - return iterator{ elems_.begin() + (const_vector_iterator{ pos } - elems_.cbegin()) }; - else - { - auto count = distance; - using deref_type = decltype(*first); - if constexpr (is_node_view<deref_type>) - { - for (auto it = first; it != last; it++) - if (!(*it)) - count--; - if (!count) - return iterator{ elems_.begin() + (const_vector_iterator{ pos } - elems_.cbegin()) }; - } - const auto start_idx = static_cast<size_t>(const_vector_iterator{ pos } - elems_.cbegin()); - preinsertion_resize(start_idx, static_cast<size_t>(count)); - size_t i = start_idx; - for (auto it = first; it != last; it++) - { - if constexpr (is_node_view<deref_type>) - { - if (!(*it)) - continue; - } - if constexpr (std::is_rvalue_reference_v<deref_type>) - elems_[i++] = impl::make_node(std::move(*it), flags); - else - elems_[i++] = impl::make_node(*it, flags); - } - return iterator{ elems_.begin() + static_cast<ptrdiff_t>(start_idx) }; - } - } - - /// \brief Inserts a range of elements into the array at a specific position. - /// - /// \tparam ElemType toml::node_view, toml::table, toml::array, or a native TOML value type - /// (or a type promotable to one). - /// \param pos The insertion position. - /// \param ilist An initializer list containing the values to be inserted. - /// \param flags Value flags to apply to new values. - /// - /// \returns \conditional_return{Valid input} - /// 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 null toml::node_views} - /// A copy of pos - template <typename ElemType> - iterator insert(const_iterator pos, - std::initializer_list<ElemType> ilist, - value_flags flags = preserve_source_value_flags) - { - return insert(pos, ilist.begin(), ilist.end(), flags); - } - - /// \brief Emplaces a new element at a specific position in the array. - /// - /// \detail \cpp - /// auto arr = toml::array{ 1, 2 }; - /// - /// //add a string using std::string's substring constructor - /// arr.emplace<std::string>(arr.cbegin() + 1, "this is not a drill"sv, 14, 5); - /// std::cout << arr << "\n"; - /// \ecpp - /// - /// \out - /// [ 1, 'drill', 2 ] - /// \eout - /// - /// \tparam ElemType toml::table, toml::array, or any native TOML value type. - /// \tparam Args Value constructor argument types. - /// \param pos The insertion position. - /// \param args Arguments to forward to the value's constructor. - /// - /// \returns An iterator to the inserted element. - /// - /// \remarks There is no difference between insert() and emplace() - /// for trivial value types (floats, ints, bools). - template <typename ElemType = void, typename... Args> - iterator emplace(const_iterator pos, Args&&... args) - { - using raw_elem_type = impl::remove_cvref<ElemType>; - using elem_type = std::conditional_t<std::is_void_v<raw_elem_type>, // - impl::emplaced_type_of<Args&&...>, - raw_elem_type>; - - using type = impl::remove_cvref<impl::unwrap_node<elem_type>>; - static_assert(impl::is_native<type> || impl::is_one_of<type, table, array>, - "Emplacement type parameter must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - - return iterator{ insert_at(const_vector_iterator{ pos }, - impl::node_ptr{ new impl::wrap_node<type>{ static_cast<Args&&>(args)... } }) }; - } - - /// \brief Replaces the element at a specific position in the array with a different value. - /// - /// \detail \cpp - /// auto arr = toml::array{ 1, 2, 3 }; - /// std::cout << arr << "\n"; - /// arr.replace(arr.cbegin() + 1, "two"); - /// std::cout << arr << "\n"; - /// \ecpp - /// - /// \out - /// [ 1, 2, 3 ] - /// [ 1, 'two', 3 ] - /// \eout - /// - /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type - /// (or a type promotable to one). - /// \param pos The insertion position. - /// \param val The node or value being inserted. - /// \param flags Value flags to apply to new values. - /// - /// \returns \conditional_return{Valid input} - /// An iterator to the replaced element. - /// \conditional_return{Input is a null toml::node_view} - /// end() - /// - /// \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 <typename ElemType> - iterator replace(const_iterator pos, ElemType&& val, value_flags flags = preserve_source_value_flags) - { - TOML_ASSERT(pos >= cbegin() && pos < cend()); - - if constexpr (is_node_view<ElemType>) - { - if (!val) - return end(); - } - - const auto it = elems_.begin() + (const_vector_iterator{ pos } - elems_.cbegin()); - *it = impl::make_node(static_cast<ElemType&&>(val), flags); - return iterator{ it }; - } - - /// \brief Appends a new element to the end of the array. - /// - /// \detail \cpp - /// auto arr = toml::array{ 1, 2 }; - /// arr.push_back(3); - /// arr.push_back(4.0); - /// arr.push_back(toml::array{ 5, "six"sv }); - /// std::cout << arr << "\n"; - /// \ecpp - /// - /// \out - /// [ 1, 2, 3, 4.0, [ 5, 'six' ] ] - /// \eout - /// - /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML value type - /// \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 a null toml::node_view. - /// This is the only circumstance in which this can occur. - template <typename ElemType> - void push_back(ElemType&& val, value_flags flags = preserve_source_value_flags) - { - emplace_back_if_not_empty_view(static_cast<ElemType&&>(val), flags); - } - - /// \brief Emplaces a new element at the end of the array. - /// - /// \detail \cpp - /// auto arr = toml::array{ 1, 2 }; - /// arr.emplace_back<toml::array>(3, "four"sv); - /// std::cout << arr << "\n"; - /// \ecpp - /// - /// \out - /// [ 1, 2, [ 3, 'four' ] ] - /// \eout - /// - /// \tparam ElemType toml::table, toml::array, or a native TOML value type - /// \tparam Args Element constructor argument types. - /// \param args Arguments to forward to the elements's constructor. - /// - /// \returns A reference to the newly-constructed element. - /// - /// \remarks There is no difference between push_back() and emplace_back() - /// For trivial value types (floats, ints, bools). - template <typename ElemType = void, typename... Args> - decltype(auto) emplace_back(Args&&... args) - { - using raw_elem_type = impl::remove_cvref<ElemType>; - using elem_type = std::conditional_t<std::is_void_v<raw_elem_type>, // - impl::emplaced_type_of<Args&&...>, - raw_elem_type>; - - static constexpr auto moving_node_ptr = std::is_same_v<elem_type, impl::node_ptr> // - && sizeof...(Args) == 1u // - && impl::first_is_same<impl::node_ptr&&, Args&&...>; - - using unwrapped_type = impl::remove_cvref<impl::unwrap_node<elem_type>>; - - static_assert( - moving_node_ptr // - || impl::is_native<unwrapped_type> // - || impl::is_one_of<unwrapped_type, table, array>, // - "ElemType argument of array::emplace_back() must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); - - if constexpr (moving_node_ptr) - { - insert_at_back(static_cast<Args&&>(args)...); - return *elems_.back(); - } - else - { - auto ptr = new impl::wrap_node<unwrapped_type>{ static_cast<Args&&>(args)... }; - insert_at_back(impl::node_ptr{ ptr }); - return *ptr; - } - } - - /// @} - - private: - /// \cond - - TOML_NODISCARD - TOML_EXPORTED_STATIC_FUNCTION - static bool TOML_CALLCONV equal(const array&, const array&) noexcept; - - template <typename T> - TOML_NODISCARD - static bool equal_to_container(const array& lhs, const T& rhs) noexcept - { - using element_type = std::remove_const_t<typename T::value_type>; - static_assert(impl::is_losslessly_convertible_to_native<element_type>, - "Container element type must be losslessly convertible one of the native TOML value types"); - - if (lhs.size() != rhs.size()) - return false; - if (rhs.size() == 0u) - return true; - - size_t i{}; - for (auto& list_elem : rhs) - { - const auto elem = lhs.get_as<impl::native_type_of<element_type>>(i++); - if (!elem || *elem != list_elem) - return false; - } - - return true; - } - - /// \endcond - - public: - /// \name Equality - /// @{ - - /// \brief Equality operator. - /// - /// \param lhs The LHS array. - /// \param rhs The RHS array. - /// - /// \returns True if the arrays contained the same elements. - TOML_NODISCARD - friend bool operator==(const array& lhs, const array& rhs) noexcept - { - return equal(lhs, rhs); - } - - /// \brief Inequality operator. - /// - /// \param lhs The LHS array. - /// \param rhs The RHS array. - /// - /// \returns True if the arrays did not contain the same elements. - TOML_NODISCARD - friend bool operator!=(const array& lhs, const array& rhs) noexcept - { - return !equal(lhs, rhs); - } - - /// \brief Initializer list equality operator. - template <typename T> - TOML_NODISCARD - friend bool operator==(const array& lhs, const std::initializer_list<T>& rhs) noexcept - { - return equal_to_container(lhs, rhs); - } - TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::initializer_list<T>&, template <typename T>); - - /// \brief Vector equality operator. - template <typename T> - TOML_NODISCARD - friend bool operator==(const array& lhs, const std::vector<T>& rhs) noexcept - { - return equal_to_container(lhs, rhs); - } - TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::vector<T>&, template <typename T>); - - /// @} + } + } + + /// \endcond + + public: + /// \brief Invokes a visitor on each element in the array. + /// + /// \tparam Func A callable type invocable with one of the following signatures: + /// <ul> + /// <li> `func(elem, index)` + /// <li> `func(elem)` + /// <li> `func(index, elem)` + /// </ul> + /// Where: + /// <ul> + /// <li> `elem` will recieve the element as it's concrete type with cvref-qualifications + ///matching the array <li> `index` will recieve a `size_t` indicating the element's index + /// </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 array. + /// + /// \details \cpp + /// toml::array arr{ 0, 1, 2, 3.0, "four", "five", 6 }; + /// + /// // select only the integers using a strongly-typed visitor + /// arr.for_each([](toml::value<int64_t>& elem) + /// { + /// std::cout << elem << ", "; + /// }); + /// std::cout << "\n"; + /// + /// // select all the numeric values using a generic visitor + is_number<> metafunction + /// arr.for_each([](auto&& elem) + /// { + /// if constexpr (toml::is_number<decltype(elem)>) + /// std::cout << elem << ", "; + /// }); + /// std::cout << "\n"; + /// + /// // select all the numeric values until we encounter something non-numeric + /// arr.for_each([](auto&& elem) + /// { + /// if constexpr (toml::is_number<decltype(elem)>) + /// { + /// std::cout << elem << ", "; + /// return true; // "keep going" + /// } + /// else + /// return false; // "stop!" + /// + /// }); + /// std::cout << "\n"; + /// + /// \ecpp + /// \out + /// 0, 1, 2, 6, + /// 0, 1, 2, 3.0, 6, + /// 0, 1, 2, 3.0, + /// \eout + /// + /// \see node::visit() + template <typename Func> + array& for_each(Func&& visitor) & // + noexcept(for_each_is_nothrow<Func&&, array&>::value) { + do_for_each(static_cast<Func&&>(visitor), *this); + return *this; + } + + /// \brief Invokes a visitor on each element in the array (rvalue overload). + template <typename Func> + array&& for_each(Func&& visitor) && // + noexcept(for_each_is_nothrow<Func&&, array&&>::value) { + do_for_each(static_cast<Func&&>(visitor), static_cast<array&&>(*this)); + return static_cast<array&&>(*this); + } + + /// \brief Invokes a visitor on each element in the array (const lvalue overload). + template <typename Func> + const array& for_each(Func&& visitor) const& // + noexcept(for_each_is_nothrow<Func&&, const array&>::value) { + do_for_each(static_cast<Func&&>(visitor), *this); + return *this; + } + + /// \brief Invokes a visitor on each element in the array (const rvalue overload). + template <typename Func> + const array&& for_each(Func&& visitor) const&& // + noexcept(for_each_is_nothrow<Func&&, const array&&>::value) { + do_for_each(static_cast<Func&&>(visitor), static_cast<const array&&>(*this)); + return static_cast<const array&&>(*this); + } + + /// @} + + /// \name Size and Capacity + /// @{ + + /// \brief Returns true if the array is empty. + TOML_NODISCARD + bool empty() const noexcept { return elems_.empty(); } + + /// \brief Returns the number of elements in the array. + TOML_NODISCARD + size_t size() const noexcept { return elems_.size(); } + + /// \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 { return elems_.max_size(); } + + /// \brief Returns the current max number of elements that may be held in the array's internal + /// storage. + TOML_NODISCARD + size_t capacity() const noexcept { return elems_.capacity(); } + + /// \brief Reserves internal storage capacity up to a pre-determined number of elements. + TOML_EXPORTED_MEMBER_FUNCTION + void reserve(size_t new_capacity); + + /// \brief Requests the removal of any unused internal storage capacity. + TOML_EXPORTED_MEMBER_FUNCTION + 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_EXPORTED_MEMBER_FUNCTION + 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 <typename ElemType> + void resize(size_t new_size, ElemType&& default_init_val, + value_flags default_init_flags = preserve_source_value_flags) { + static_assert( + !is_node_view<ElemType>, + "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<ElemType&&>(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_EXPORTED_MEMBER_FUNCTION + 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_EXPORTED_MEMBER_FUNCTION + 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_EXPORTED_MEMBER_FUNCTION + array& flatten() &; + + /// \brief Flattens this array, recursively hoisting the contents of child arrays up into + /// itself (rvalue overload). + array&& flatten() && { return static_cast<toml::array&&>(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_EXPORTED_MEMBER_FUNCTION + 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<toml::array&&>(this->prune(recursive)); + } + + /// \brief Removes the last element from the array. + TOML_EXPORTED_MEMBER_FUNCTION + void pop_back() noexcept; + + /// \brief Removes all elements from the array. + TOML_EXPORTED_MEMBER_FUNCTION + void clear() noexcept; + + /// @} + + /// \name Insertion and Emplacement + /// @{ + + /// \brief Inserts a new element at a specific position in the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 3 }; + /// arr.insert(arr.cbegin() + 1, "two"); + /// arr.insert(arr.cend(), toml::array{ 4, 5 }); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 'two', 3, [ 4, 5 ] ] + /// \eout + /// + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML + /// value type (or a type promotable to one). + /// \param pos The insertion position. + /// \param val The node or value being inserted. + /// \param flags Value flags to apply to new values. + /// + /// \returns \conditional_return{Valid input} + /// An iterator to the newly-inserted element. + /// \conditional_return{Input is a null toml::node_view} + /// end() + /// + /// \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 <typename ElemType> + iterator insert(const_iterator pos, ElemType&& val, + value_flags flags = preserve_source_value_flags) { + if constexpr (is_node_view<ElemType>) { + if (!val) return end(); + } + return iterator{insert_at(const_vector_iterator{pos}, + impl::make_node(static_cast<ElemType&&>(val), flags))}; + } + + /// \brief Repeatedly inserts a new element starting at a specific position in the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ + /// "with an evil twinkle in its eye the goose said", + /// "and immediately we knew peace was never an option." + /// }; + /// arr.insert(arr.cbegin() + 1, 3, "honk"); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ + /// 'with an evil twinkle in its eye the goose said', + /// 'honk', + /// 'honk', + /// 'honk', + /// 'and immediately we knew peace was never an option.' + /// ] + /// \eout + /// + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML + /// value type (or a type promotable to one). + /// \param pos The insertion position. + /// \param count The number of times the node or value should be inserted. + /// \param val The node or value being inserted. + /// \param flags Value flags to apply to new values. + /// + /// \returns \conditional_return{Valid input} + /// An iterator to the newly-inserted element. + /// \conditional_return{count == 0} + /// A copy of pos + /// \conditional_return{Input is a null toml::node_view} + /// end() + /// + /// \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 <typename ElemType> + iterator insert(const_iterator pos, size_t count, ElemType&& val, + value_flags flags = preserve_source_value_flags) { + if constexpr (is_node_view<ElemType>) { + if (!val) return end(); + } + switch (count) { + case 0: + return iterator{elems_.begin() + (const_vector_iterator{pos} - elems_.cbegin())}; + case 1: + return insert(pos, static_cast<ElemType&&>(val), flags); + default: { + const auto start_idx = static_cast<size_t>(const_vector_iterator{pos} - elems_.cbegin()); + preinsertion_resize(start_idx, count); + size_t i = start_idx; + for (size_t e = start_idx + count - 1u; i < e; i++) + elems_[i] = impl::make_node(val, flags); + + // # potentially move the initial value into the last element + elems_[i] = impl::make_node(static_cast<ElemType&&>(val), flags); + return iterator{elems_.begin() + static_cast<ptrdiff_t>(start_idx)}; + } + } + } + + /// \brief Inserts a range of elements into the array at a specific position. + /// + /// \tparam Iter An iterator type. Must satisfy ForwardIterator. + /// \param pos The insertion position. + /// \param first Iterator to the first node or value being inserted. + /// \param last Iterator to the one-past-the-last node or value being inserted. + /// \param flags Value flags to apply to new values. + /// + /// \returns \conditional_return{Valid input} + /// An iterator to the first newly-inserted element. + /// \conditional_return{first >= last} + /// A copy of pos + /// \conditional_return{All objects in the range were null toml::node_views} + /// A copy of pos + template <typename Iter> + iterator insert(const_iterator pos, Iter first, Iter last, + value_flags flags = preserve_source_value_flags) { + const auto distance = std::distance(first, last); + if (distance <= 0) + return iterator{elems_.begin() + (const_vector_iterator{pos} - elems_.cbegin())}; + else { + auto count = distance; + using deref_type = decltype(*first); + if constexpr (is_node_view<deref_type>) { + for (auto it = first; it != last; it++) + if (!(*it)) count--; + if (!count) + return iterator{elems_.begin() + (const_vector_iterator{pos} - elems_.cbegin())}; + } + const auto start_idx = static_cast<size_t>(const_vector_iterator{pos} - elems_.cbegin()); + preinsertion_resize(start_idx, static_cast<size_t>(count)); + size_t i = start_idx; + for (auto it = first; it != last; it++) { + if constexpr (is_node_view<deref_type>) { + if (!(*it)) continue; + } + if constexpr (std::is_rvalue_reference_v<deref_type>) + elems_[i++] = impl::make_node(std::move(*it), flags); + else + elems_[i++] = impl::make_node(*it, flags); + } + return iterator{elems_.begin() + static_cast<ptrdiff_t>(start_idx)}; + } + } + + /// \brief Inserts a range of elements into the array at a specific position. + /// + /// \tparam ElemType toml::node_view, toml::table, toml::array, or a native TOML value type + /// (or a type promotable to one). + /// \param pos The insertion position. + /// \param ilist An initializer list containing the values to be inserted. + /// \param flags Value flags to apply to new values. + /// + /// \returns \conditional_return{Valid input} + /// 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 null toml::node_views} + /// A copy of pos + template <typename ElemType> + iterator insert(const_iterator pos, std::initializer_list<ElemType> ilist, + value_flags flags = preserve_source_value_flags) { + return insert(pos, ilist.begin(), ilist.end(), flags); + } + + /// \brief Emplaces a new element at a specific position in the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2 }; + /// + /// //add a string using std::string's substring constructor + /// arr.emplace<std::string>(arr.cbegin() + 1, "this is not a drill"sv, 14, 5); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 'drill', 2 ] + /// \eout + /// + /// \tparam ElemType toml::table, toml::array, or any native TOML value type. + /// \tparam Args Value constructor argument types. + /// \param pos The insertion position. + /// \param args Arguments to forward to the value's constructor. + /// + /// \returns An iterator to the inserted element. + /// + /// \remarks There is no difference between insert() and emplace() + /// for trivial value types (floats, ints, bools). + template <typename ElemType = void, typename... Args> + iterator emplace(const_iterator pos, Args&&... args) { + using raw_elem_type = impl::remove_cvref<ElemType>; + using elem_type = std::conditional_t<std::is_void_v<raw_elem_type>, // + impl::emplaced_type_of<Args&&...>, raw_elem_type>; + + using type = impl::remove_cvref<impl::unwrap_node<elem_type>>; + static_assert(impl::is_native<type> || impl::is_one_of<type, table, array>, + "Emplacement type parameter must be one of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + return iterator{ + insert_at(const_vector_iterator{pos}, + impl::node_ptr{new impl::wrap_node<type>{static_cast<Args&&>(args)...}})}; + } + + /// \brief Replaces the element at a specific position in the array with a different value. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2, 3 }; + /// std::cout << arr << "\n"; + /// arr.replace(arr.cbegin() + 1, "two"); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2, 3 ] + /// [ 1, 'two', 3 ] + /// \eout + /// + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML + /// value type (or a type promotable to one). + /// \param pos The insertion position. + /// \param val The node or value being inserted. + /// \param flags Value flags to apply to new values. + /// + /// \returns \conditional_return{Valid input} + /// An iterator to the replaced element. + /// \conditional_return{Input is a null toml::node_view} + /// end() + /// + /// \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 <typename ElemType> + iterator replace(const_iterator pos, ElemType&& val, + value_flags flags = preserve_source_value_flags) { + TOML_ASSERT(pos >= cbegin() && pos < cend()); + + if constexpr (is_node_view<ElemType>) { + if (!val) return end(); + } + + const auto it = elems_.begin() + (const_vector_iterator{pos} - elems_.cbegin()); + *it = impl::make_node(static_cast<ElemType&&>(val), flags); + return iterator{it}; + } + + /// \brief Appends a new element to the end of the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2 }; + /// arr.push_back(3); + /// arr.push_back(4.0); + /// arr.push_back(toml::array{ 5, "six"sv }); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2, 3, 4.0, [ 5, 'six' ] ] + /// \eout + /// + /// \tparam ElemType toml::node, toml::node_view, toml::table, toml::array, or a native TOML + /// value type + /// \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 a null toml::node_view. + /// This is the only circumstance in which this can occur. + template <typename ElemType> + void push_back(ElemType&& val, value_flags flags = preserve_source_value_flags) { + emplace_back_if_not_empty_view(static_cast<ElemType&&>(val), flags); + } + + /// \brief Emplaces a new element at the end of the array. + /// + /// \detail \cpp + /// auto arr = toml::array{ 1, 2 }; + /// arr.emplace_back<toml::array>(3, "four"sv); + /// std::cout << arr << "\n"; + /// \ecpp + /// + /// \out + /// [ 1, 2, [ 3, 'four' ] ] + /// \eout + /// + /// \tparam ElemType toml::table, toml::array, or a native TOML value type + /// \tparam Args Element constructor argument types. + /// \param args Arguments to forward to the elements's constructor. + /// + /// \returns A reference to the newly-constructed element. + /// + /// \remarks There is no difference between push_back() and emplace_back() + /// For trivial value types (floats, ints, bools). + template <typename ElemType = void, typename... Args> + decltype(auto) emplace_back(Args&&... args) { + using raw_elem_type = impl::remove_cvref<ElemType>; + using elem_type = std::conditional_t<std::is_void_v<raw_elem_type>, // + impl::emplaced_type_of<Args&&...>, raw_elem_type>; + + static constexpr auto moving_node_ptr = std::is_same_v<elem_type, impl::node_ptr> // + && sizeof...(Args) == 1u // + && impl::first_is_same<impl::node_ptr&&, Args&&...>; + + using unwrapped_type = impl::remove_cvref<impl::unwrap_node<elem_type>>; + + static_assert(moving_node_ptr // + || impl::is_native<unwrapped_type> // + || impl::is_one_of<unwrapped_type, table, array>, // + "ElemType argument of array::emplace_back() must be one " + "of:" TOML_SA_UNWRAPPED_NODE_TYPE_LIST); + + if constexpr (moving_node_ptr) { + insert_at_back(static_cast<Args&&>(args)...); + return *elems_.back(); + } else { + auto ptr = new impl::wrap_node<unwrapped_type>{static_cast<Args&&>(args)...}; + insert_at_back(impl::node_ptr{ptr}); + return *ptr; + } + } + + /// @} + + private: + /// \cond + + TOML_NODISCARD + TOML_EXPORTED_STATIC_FUNCTION + static bool TOML_CALLCONV equal(const array&, const array&) noexcept; + + template <typename T> + TOML_NODISCARD static bool equal_to_container(const array& lhs, const T& rhs) noexcept { + using element_type = std::remove_const_t<typename T::value_type>; + static_assert(impl::is_losslessly_convertible_to_native<element_type>, + "Container element type must be losslessly convertible one of the native TOML " + "value types"); + + if (lhs.size() != rhs.size()) return false; + if (rhs.size() == 0u) return true; + + size_t i{}; + for (auto& list_elem : rhs) { + const auto elem = lhs.get_as<impl::native_type_of<element_type>>(i++); + if (!elem || *elem != list_elem) return false; + } + + return true; + } + + /// \endcond + + public: + /// \name Equality + /// @{ + + /// \brief Equality operator. + /// + /// \param lhs The LHS array. + /// \param rhs The RHS array. + /// + /// \returns True if the arrays contained the same elements. + TOML_NODISCARD + friend bool operator==(const array& lhs, const array& rhs) noexcept { return equal(lhs, rhs); } + + /// \brief Inequality operator. + /// + /// \param lhs The LHS array. + /// \param rhs The RHS array. + /// + /// \returns True if the arrays did not contain the same elements. + TOML_NODISCARD + friend bool operator!=(const array& lhs, const array& rhs) noexcept { return !equal(lhs, rhs); } + + /// \brief Initializer list equality operator. + template <typename T> + TOML_NODISCARD friend bool operator==(const array& lhs, + const std::initializer_list<T>& rhs) noexcept { + return equal_to_container(lhs, rhs); + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::initializer_list<T>&, + template <typename T>); + + /// \brief Vector equality operator. + template <typename T> + TOML_NODISCARD friend bool operator==(const array& lhs, const std::vector<T>& rhs) noexcept { + return equal_to_container(lhs, rhs); + } + TOML_ASYMMETRICAL_EQUALITY_OPS(const array&, const std::vector<T>&, template <typename T>); + + /// @} #if TOML_ENABLE_FORMATTERS - /// \brief Prints the array 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 array& rhs) - { - impl::print_to_stream(lhs, rhs); - return lhs; - } + /// \brief Prints the array 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 array& rhs) { + impl::print_to_stream(lhs, rhs); + return lhs; + } #endif - }; + }; } TOML_NAMESPACE_END; |
