tomlplusplus/include/toml++/impl/table.hpp
Mark Gillard d46cac705a fixed #197
also:
- fixed `noexcept(...)` sometimes being incorrectly derived on `for_each()
- refactors
2023-09-05 13:53:23 +03:00

1997 lines
56 KiB
C++

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