diff options
Diffstat (limited to 'vendor/toml++/impl/array.hpp')
| -rw-r--r-- | vendor/toml++/impl/array.hpp | 1790 |
1 files changed, 1790 insertions, 0 deletions
diff --git a/vendor/toml++/impl/array.hpp b/vendor/toml++/impl/array.hpp new file mode 100644 index 0000000..ad79379 --- /dev/null +++ b/vendor/toml++/impl/array.hpp @@ -0,0 +1,1790 @@ +//# 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 "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 +#define TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN 1 +#else +#define TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN 0 +#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." +#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_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>); + +#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); +#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)); + } + }); + +#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; +#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>); + + /// @} + +#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; + } + +#endif + }; +} +TOML_NAMESPACE_END; + +#include "header_end.hpp" |
