added for_each() for tables and arrays

also:
- refactoring
- documentation fixes
- updated conformance tests
- made submodules shallow
This commit is contained in:
Mark Gillard 2022-04-24 20:21:59 +03:00
parent db04ac8918
commit bf13bbd42e
56 changed files with 1446 additions and 236 deletions

20
.gitmodules vendored
View File

@ -1,19 +1,21 @@
[submodule "external/toml-test"]
path = external/toml-test
url = https://github.com/BurntSushi/toml-test.git
shallow = true
[submodule "external/toml-spec-tests"]
path = external/toml-spec-tests
url = https://github.com/iarna/toml-spec-tests.git
[submodule "external/dox"]
path = external/dox
url = https://github.com/marzer/dox.git
shallow = true
[submodule "external/tloptional"]
path = external/tloptional
url = https://github.com/TartanLlama/optional.git
shallow = true
[submodule "external/json"]
path = external/json
url = https://github.com/nlohmann/json.git
shallow = true
[submodule "external/Catch2"]
path = external/Catch2
url = https://github.com/catchorg/Catch2.git
branch = v2.x
[submodule "external/tloptional"]
path = external/tloptional
url = https://github.com/TartanLlama/optional.git
[submodule "external/json"]
path = external/json
url = https://github.com/nlohmann/json.git
shallow = true

View File

@ -14,7 +14,7 @@ template:
-->
## Unreleased
## [v3.1.0](https://github.com/marzer/tomlplusplus/releases/tag/v3.1.0) - 2022-04-22
#### Fixes:
- Fixed potential segfault when calling `at_path()` with an empty string
@ -22,9 +22,11 @@ template:
- Fixed a number of spurious warnings with Clang 10 (#145, #146) (@chronoxor)
#### Additions:
- Added `toml::array::for_each()`
- Added `toml::table::for_each()`
- Added config options `TOML_EXPORTED_CLASS`, `TOML_EXPORTED_MEMBER_FUNCTION`, `TOML_EXPORTED_STATIC_FUNCTION` & `TOML_EXPORTED_FREE_FUNCTION`
- Add support for escape sequence `\e` when using `TOML_ENABLE_UNRELEASED_FEATURES` ([toml/790](https://github.com/toml-lang/toml/pull/790))
- Add support for more unicode in bare keys when using `TOML_ENABLE_UNRELEASED_FEATURES` ([toml/891](https://github.com/toml-lang/toml/pull/891))
- Added support for escape sequence `\e` when using `TOML_ENABLE_UNRELEASED_FEATURES` ([toml/790](https://github.com/toml-lang/toml/pull/790))
- Added support for more unicode in bare keys when using `TOML_ENABLE_UNRELEASED_FEATURES` ([toml/891](https://github.com/toml-lang/toml/pull/891))
#### Removals/Deprecations:
- Deprecated old `TOML_API` option in favour new `TOML_EXPORTED_X` options

View File

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.14)
project(
tomlplusplus
VERSION 3.0.1
VERSION 3.1.0
DESCRIPTION "Header-only TOML config file parser and serializer for C++17"
HOMEPAGE_URL "https://marzer.github.io/tomlplusplus/"
LANGUAGES CXX

View File

@ -99,13 +99,13 @@ You'll find some more code examples in the `examples` directory, and plenty more
2. `#include <toml++/toml.h>`
### Conan
Add `tomlplusplus/3.0.1` to your conanfile.
Add `tomlplusplus/3.1.0` to your conanfile.
### DDS
Add `tomlpp` to your `package.json5`, e.g.:
```
depends: [
'tomlpp^3.0.1',
'tomlpp^3.1.0',
]
```
> &#xFE0F; _[What is DDS?](https://dds.pizza/)_
@ -121,12 +121,21 @@ include(FetchContent)
FetchContent_Declare(
tomlplusplus
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
GIT_TAG v3.0.1
GIT_TAG v3.1.0
)
FetchContent_MakeAvailable(tomlplusplus)
```
> &#xFE0F; _[What is FetchContent?](https://cmake.org/cmake/help/latest/module/FetchContent.html)_
### Git submodules
```
git submodule add --depth 1 https://github.com/marzer/tomlplusplus.git tomlplusplus
git config -f .gitmodules submodule.tomlplusplus.shallow true
```
> &#xFE0F; The toml++ repository has some submodules of its own, but **they are only used for testing**!
> You do not need to use the `--recursive` option for regular library consumption.
### Other environments and package managers
`toml++` is a fairly new project and I'm not up-to-speed with all of the available packaging and integration options
in the C++ ecosystem. I'm also a cmake novice, for better or worse. If there's an integration option missing be

View File

@ -457,7 +457,7 @@
\subsection mainpage-adding-lib-conan Conan
Add `tomlplusplus/3.0.1` to your conanfile.
Add `tomlplusplus/3.1.0` to your conanfile.
@ -465,7 +465,7 @@
Add `tomlpp` to your `package.json5`, e.g.:
\bash
depends: [
'tomlpp^3.0.1',
'tomlpp^3.1.0',
]
\ebash
@ -497,7 +497,7 @@
FetchContent_Declare(
tomlplusplus
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
GIT_TAG v3.0.1
GIT_TAG v3.1.0
)
FetchContent_MakeAvailable(tomlplusplus)
\endcode
@ -506,6 +506,16 @@
\subsection mainpage-adding-lib-git-submodules Git submodules
\bash
git submodule add --depth 1 https://github.com/marzer/tomlplusplus.git tomlplusplus
git config -f .gitmodules submodule.tomlplusplus.shallow true
\ebash
\note The toml++ repository has some submodules of its own, but **they are only used for testing**! You do not need to
use the `--recursive` option for regular library consumption.
\subsection mainpage-adding-lib-other Other environments and package managers
toml++ is a fairly new project and I'm not up-to-speed with all of the available packaging and integration options
in the C++ ecosystem. I'm also a cmake novice, for better or worse. If there's an integration option missing

2
external/Catch2 vendored

@ -1 +1 @@
Subproject commit 958944d27a2d2fb82aa008377bf4f8752f6b848e
Subproject commit d71b4617e9935f8589870af211f5b7552d743654

2
external/json vendored

@ -1 +1 @@
Subproject commit e4643d1f1b03fc7a1d7b65f17e012ca93680cad8
Subproject commit fcc36f99ba1afc7baebe24e0c7429d2039d32d99

2
external/toml-test vendored

@ -1 +1 @@
Subproject commit 983c1a54c5faad31b5d217b1e47c0e074dea7840
Subproject commit 10cdc99d96fbf1350a8891a9d93295fcc85aa694

View File

@ -773,7 +773,7 @@ TOML_NAMESPACE_START
/// @}
/// \name Iterators
/// \name Iteration
/// @{
/// \brief A RandomAccessIterator for iterating over elements in a toml::array.
@ -824,6 +824,232 @@ TOML_NAMESPACE_START
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>
static constexpr bool can_for_each = std::is_invocable_v<Func, for_each_elem_ref<T, Array>, size_t> //
|| std::is_invocable_v<Func, size_t, for_each_elem_ref<T, Array>> //
|| std::is_invocable_v<Func, for_each_elem_ref<T, Array>>;
template <typename Func, typename Array, typename T>
static constexpr bool can_for_each_nothrow =
std::is_nothrow_invocable_v<Func, for_each_elem_ref<T, Array>, size_t> //
|| std::is_nothrow_invocable_v<Func, size_t, for_each_elem_ref<T, Array>> //
|| std::is_nothrow_invocable_v<Func, for_each_elem_ref<T, Array>>;
template <typename Func, typename Array>
static constexpr bool can_for_each_any = 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>
static constexpr bool for_each_is_nothrow_one = !can_for_each<Func, Array, T> //
|| can_for_each_nothrow<Func, Array, T>;
// clang-format off
template <typename Func, typename Array>
static constexpr bool for_each_is_nothrow = 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>;
// clang-format on
template <typename Func, typename Array>
static void do_for_each(Func&& visitor, Array&& arr) noexcept(for_each_is_nothrow<Func&&, Array&&>)
{
static_assert(can_for_each_any<Func&&, Array&&>,
"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>);
const auto keep_going =
static_cast<node_ref>(static_cast<Array&&>(arr)[i])
.visit(
[&](auto&& elem) noexcept(for_each_is_nothrow_one<Func&&, Array&&, decltype(elem)>)
{
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;
}
}
/// \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&>)
{
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&&>)
{
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&>)
{
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&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<const array&&>(*this));
return static_cast<const array&&>(*this);
}
/// @}
/// \name Size and Capacity
@ -1004,8 +1230,6 @@ TOML_NAMESPACE_START
array& flatten() &;
/// \brief Flattens this array, recursively hoisting the contents of child arrays up into itself (rvalue overload).
///
/// \returns An rvalue reference to the array.
array&& flatten() &&
{
return static_cast<toml::array&&>(this->flatten());

View File

@ -916,6 +916,10 @@ TOML_IMPL_NAMESPACE_START
};
template <typename T>
inline constexpr node_type node_type_of = node_type_getter<unwrap_node<remove_cvref<T>>>::value;
template <typename T, typename ConvertFrom>
inline constexpr bool is_constructible_or_convertible = std::is_constructible_v<T, ConvertFrom> //
|| std::is_convertible_v<ConvertFrom, T>;
}
TOML_IMPL_NAMESPACE_END;
/// \endcond
@ -990,8 +994,8 @@ TOML_NAMESPACE_START
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::node (or one of its subclasses).
template <typename T>
inline constexpr bool is_node =
std::is_same_v<toml::node, impl::remove_cvref<T>> || std::is_base_of_v<toml::node, impl::remove_cvref<T>>;
inline constexpr bool is_node = std::is_same_v<toml::node, impl::remove_cvref<T>> //
|| std::is_base_of_v<toml::node, impl::remove_cvref<T>>;
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::node_view.
template <typename T>

View File

@ -287,7 +287,7 @@ TOML_NAMESPACE_START
/// @}
/// \name Iterators
/// \name Iteration
/// @{
/// A const iterator for iterating over the characters in the key.
@ -327,8 +327,8 @@ TOML_NAMESPACE_START
/// \brief Metafunction for determining if a type is, or is a reference to, a toml::key,
/// or is implicitly or explicitly convertible to one.
template <typename T>
inline constexpr bool is_key_or_convertible =
is_key<T> || std::is_constructible_v<toml::key, T> || std::is_convertible_v<T, toml::key>;
inline constexpr bool is_key_or_convertible = is_key<T> //
|| impl::is_constructible_or_convertible<toml::key, T>;
}
TOML_NAMESPACE_END;

View File

@ -530,7 +530,7 @@ TOML_NAMESPACE_START
/// {
/// std::cout << "- " << std::setw(18) << std::left << type_name;
/// using type = std::remove_pointer_t<decltype(dummy)>;
/// if (std::optional<type> val = tbl.get(key)->value<type>())
/// if (auto val = tbl.get(key)->value<type>(); val)
/// std::cout << *val << "\n";
/// else
/// std::cout << "n/a\n";
@ -732,145 +732,141 @@ TOML_NAMESPACE_START
private:
/// \cond
template <typename Func, typename Node, typename T>
static constexpr bool can_visit = std::is_invocable_v<Func, ref_cast_type<T, Node>>;
template <typename Func, typename Node, typename T>
static constexpr bool can_visit_nothrow = std::is_nothrow_invocable_v<Func, ref_cast_type<T, Node>>;
template <typename Func, typename Node>
static constexpr bool can_visit_any = can_visit<Func, Node, table> //
|| can_visit<Func, Node, array> //
|| can_visit<Func, Node, std::string> //
|| can_visit<Func, Node, int64_t> //
|| can_visit<Func, Node, double> //
|| can_visit<Func, Node, bool> //
|| can_visit<Func, Node, date> //
|| can_visit<Func, Node, time> //
|| can_visit<Func, Node, date_time>;
// clang-format off
template <typename Func, typename N, typename T>
static constexpr bool can_visit = std::is_invocable_v<Func, ref_cast_type<T, N>>;
template <typename Func, typename Node>
static constexpr bool can_visit_all = can_visit<Func, Node, table> //
&& can_visit<Func, Node, array> //
&& can_visit<Func, Node, std::string> //
&& can_visit<Func, Node, int64_t> //
&& can_visit<Func, Node, double> //
&& can_visit<Func, Node, bool> //
&& can_visit<Func, Node, date> //
&& can_visit<Func, Node, time> //
&& can_visit<Func, Node, date_time>;
template <typename Func, typename N>
static constexpr bool can_visit_any =
can_visit<Func, N, table>
|| can_visit<Func, N, array>
|| can_visit<Func, N, std::string>
|| can_visit<Func, N, int64_t>
|| can_visit<Func, N, double>
|| can_visit<Func, N, bool>
|| can_visit<Func, N, date>
|| can_visit<Func, N, time>
|| can_visit<Func, N, date_time>;
template <typename Func, typename Node, typename T>
static constexpr bool visit_is_nothrow_one = !can_visit<Func, Node, T> || can_visit_nothrow<Func, Node, T>;
template <typename Func, typename N>
static constexpr bool can_visit_all =
can_visit<Func, N, table>
&& can_visit<Func, N, array>
&& can_visit<Func, N, std::string>
&& can_visit<Func, N, int64_t>
&& can_visit<Func, N, double>
&& can_visit<Func, N, bool>
&& can_visit<Func, N, date>
&& can_visit<Func, N, time>
&& can_visit<Func, N, date_time>;
template <typename Func, typename N, typename T>
static constexpr bool visit_is_nothrow_one =
!can_visit<Func, N, T>
|| std::is_nothrow_invocable_v<Func, ref_cast_type<T, N>>;
template <typename Func, typename N>
static constexpr bool visit_is_nothrow =
visit_is_nothrow_one<Func, N, table>
&& visit_is_nothrow_one<Func, N, array>
&& visit_is_nothrow_one<Func, N, std::string>
&& visit_is_nothrow_one<Func, N, int64_t>
&& visit_is_nothrow_one<Func, N, double>
&& visit_is_nothrow_one<Func, N, bool>
&& visit_is_nothrow_one<Func, N, date>
&& visit_is_nothrow_one<Func, N, time>
&& visit_is_nothrow_one<Func, N, date_time>;
template <typename Func, typename Node>
static constexpr bool visit_is_nothrow = visit_is_nothrow_one<Func, Node, table> //
&& visit_is_nothrow_one<Func, Node, array> //
&& visit_is_nothrow_one<Func, Node, std::string> //
&& visit_is_nothrow_one<Func, Node, int64_t> //
&& visit_is_nothrow_one<Func, Node, double> //
&& visit_is_nothrow_one<Func, Node, bool> //
&& visit_is_nothrow_one<Func, Node, date> //
&& visit_is_nothrow_one<Func, Node, time> //
&& visit_is_nothrow_one<Func, Node, date_time>;
// clang-format on
template <typename Func, typename N, typename T, bool = can_visit<Func, N, T>>
template <typename Func, typename Node, typename T, bool = can_visit<Func, Node, T>>
struct visit_return_type_
{
using type = decltype(std::declval<Func>()(std::declval<ref_cast_type<T, N>>()));
using type = decltype(std::declval<Func>()(std::declval<ref_cast_type<T, Node>>()));
};
template <typename Func, typename N, typename T>
struct visit_return_type_<Func, N, T, false>
template <typename Func, typename Node, typename T>
struct visit_return_type_<Func, Node, T, false>
{
using type = void;
};
template <typename Func, typename N, typename T>
using visit_return_type = typename visit_return_type_<Func, N, T>::type;
template <typename Func, typename Node, typename T>
using visit_return_type = typename visit_return_type_<Func, Node, T>::type;
template <typename A, typename B>
using nonvoid = std::conditional_t<std::is_void_v<A>, B, A>;
template <typename N, typename Func>
static decltype(auto) do_visit(N&& n, Func&& visitor) noexcept(visit_is_nothrow<Func&&, N&&>)
template <typename Func, typename Node>
static decltype(auto) do_visit(Func&& visitor, Node&& n) noexcept(visit_is_nothrow<Func&&, Node&&>)
{
static_assert(can_visit_any<Func&&, N&&>,
static_assert(can_visit_any<Func&&, Node&&>,
"TOML node visitors must be invocable for at least one of the toml::node "
"specializations:" TOML_SA_NODE_TYPE_LIST);
switch (n.type())
{
case node_type::table:
if constexpr (can_visit<Func&&, N&&, table>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<table>());
if constexpr (can_visit<Func&&, Node&&, table>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<table>());
break;
case node_type::array:
if constexpr (can_visit<Func&&, N&&, array>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<array>());
if constexpr (can_visit<Func&&, Node&&, array>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<array>());
break;
case node_type::string:
if constexpr (can_visit<Func&&, N&&, std::string>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<std::string>());
if constexpr (can_visit<Func&&, Node&&, std::string>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<std::string>());
break;
case node_type::integer:
if constexpr (can_visit<Func&&, N&&, int64_t>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<int64_t>());
if constexpr (can_visit<Func&&, Node&&, int64_t>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<int64_t>());
break;
case node_type::floating_point:
if constexpr (can_visit<Func&&, N&&, double>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<double>());
if constexpr (can_visit<Func&&, Node&&, double>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<double>());
break;
case node_type::boolean:
if constexpr (can_visit<Func&&, N&&, bool>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<bool>());
if constexpr (can_visit<Func&&, Node&&, bool>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<bool>());
break;
case node_type::date:
if constexpr (can_visit<Func&&, N&&, date>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<date>());
if constexpr (can_visit<Func&&, Node&&, date>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<date>());
break;
case node_type::time:
if constexpr (can_visit<Func&&, N&&, time>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<time>());
if constexpr (can_visit<Func&&, Node&&, time>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<time>());
break;
case node_type::date_time:
if constexpr (can_visit<Func&&, N&&, date_time>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<date_time>());
if constexpr (can_visit<Func&&, Node&&, date_time>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<date_time>());
break;
case node_type::none: TOML_UNREACHABLE;
default: TOML_UNREACHABLE;
}
if constexpr (can_visit_all<Func&&, N&&>)
TOML_UNREACHABLE;
else
if constexpr (!can_visit_all<Func&&, Node&&>)
{
// clang-format off
using return_type =
nonvoid<visit_return_type<Func&&, N&&, table>,
nonvoid<visit_return_type<Func&&, N&&, array>,
nonvoid<visit_return_type<Func&&, N&&, std::string>,
nonvoid<visit_return_type<Func&&, N&&, int64_t>,
nonvoid<visit_return_type<Func&&, N&&, double>,
nonvoid<visit_return_type<Func&&, N&&, bool>,
nonvoid<visit_return_type<Func&&, N&&, date>,
nonvoid<visit_return_type<Func&&, N&&, time>,
visit_return_type<Func&&, N&&, date_time>
nonvoid<visit_return_type<Func&&, Node&&, table>,
nonvoid<visit_return_type<Func&&, Node&&, array>,
nonvoid<visit_return_type<Func&&, Node&&, std::string>,
nonvoid<visit_return_type<Func&&, Node&&, int64_t>,
nonvoid<visit_return_type<Func&&, Node&&, double>,
nonvoid<visit_return_type<Func&&, Node&&, bool>,
nonvoid<visit_return_type<Func&&, Node&&, date>,
nonvoid<visit_return_type<Func&&, Node&&, time>,
visit_return_type<Func&&, Node&&, date_time>
>>>>>>>>;
// clang-format on
@ -903,13 +899,17 @@ TOML_NAMESPACE_START
/// do_something_with_a_string(*n)); //n is a toml::value<std::string>
/// else if constexpr (toml::is_integer<decltype(n)>)
/// do_something_with_an_int(*n); //n is a toml::value<int64_t>
/// else
/// throw std::exception{ "Expected string or integer" };
/// });
/// \ecpp
///
/// \tparam Func A callable type invocable with one or more of the
/// toml++ node types.
/// Visitor functions need not be generic; specifying a concrete node type as the input argument type
/// effectively acts a 'filter', only invoking the visitor if the concrete type is compatible.
/// Thus the example above can be re-written as: \cpp
/// node.visit([](toml::value<std::string>& s) { do_something_with_a_string(*s)); });
/// node.visit([](toml::value<int64_t>& i) { do_something_with_an_int(*i)); });
/// \ecpp
///
/// \tparam Func A callable type invocable with one or more of the toml++ node types.
///
/// \param visitor The visitor object.
///
@ -920,28 +920,28 @@ TOML_NAMESPACE_START
template <typename Func>
decltype(auto) visit(Func&& visitor) & noexcept(visit_is_nothrow<Func&&, node&>)
{
return do_visit(*this, static_cast<Func&&>(visitor));
return do_visit(static_cast<Func&&>(visitor), *this);
}
/// \brief Invokes a visitor on the node based on the node's concrete type (rvalue overload).
template <typename Func>
decltype(auto) visit(Func&& visitor) && noexcept(visit_is_nothrow<Func&&, node&&>)
{
return do_visit(static_cast<node&&>(*this), static_cast<Func&&>(visitor));
return do_visit(static_cast<Func&&>(visitor), static_cast<node&&>(*this));
}
/// \brief Invokes a visitor on the node based on the node's concrete type (const lvalue overload).
template <typename Func>
decltype(auto) visit(Func&& visitor) const& noexcept(visit_is_nothrow<Func&&, const node&>)
{
return do_visit(*this, static_cast<Func&&>(visitor));
return do_visit(static_cast<Func&&>(visitor), *this);
}
/// \brief Invokes a visitor on the node based on the node's concrete type (const rvalue overload).
template <typename Func>
decltype(auto) visit(Func&& visitor) const&& noexcept(visit_is_nothrow<Func&&, const node&&>)
{
return do_visit(static_cast<const node&&>(*this), static_cast<Func&&>(visitor));
return do_visit(static_cast<Func&&>(visitor), static_cast<const node&&>(*this));
}
/// @}

View File

@ -102,16 +102,13 @@ TOML_IMPL_NAMESPACE_START
if ((!lhs != !rhs) || lhs->type() != rhs->type())
return false;
bool same;
lhs->visit(
[=, &same](auto& l) noexcept
return lhs->visit(
[=](auto& l) noexcept
{
using concrete_type = remove_cvref<decltype(l)>;
same = (l == *(rhs->as<concrete_type>()));
return l == *(rhs->as<concrete_type>());
});
return same;
}
}
TOML_IMPL_NAMESPACE_END;

View File

@ -269,7 +269,7 @@ TOML_NAMESPACE_START
/// @}
/// \name Iterators
/// \name Iteration
/// @{
/// \brief A BidirectionalIterator for iterating over key-value pairs in a wrapped toml::table.

View File

@ -1102,8 +1102,8 @@
/// \def TOML_SMALL_FLOAT_TYPE
/// \brief If your codebase has an additional 'small' float type (e.g. half-precision), this tells toml++ about it.
/// \detail Not defined by default.
/// \remark If you're building for a platform that has a built-in half precision float (e.g. `_Float16`), you don't
/// need to use this configuration option to make toml++ aware of it; the library comes with that built-in.
/// \remark If you're building for a platform that has `_Float16` and/or `__fp16`, you don't
/// need to use this configuration option to make toml++ aware of them. The library comes with that built-in.
#define TOML_SMALL_INT_TYPE

View File

@ -221,6 +221,7 @@ TOML_NAMESPACE_START
/// \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_;
@ -784,7 +785,7 @@ TOML_NAMESPACE_START
/// @}
/// \name Iterators
/// \name Iteration
/// @{
/// \brief A BidirectionalIterator for iterating over key-value pairs in a toml::table.
@ -835,6 +836,236 @@ TOML_NAMESPACE_START
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>
static constexpr bool can_for_each = std::is_invocable_v<Func, const key&, for_each_value_ref<T, Table>> //
|| std::is_invocable_v<Func, for_each_value_ref<T, Table>>;
template <typename Func, typename Table, typename T>
static constexpr bool can_for_each_nothrow =
std::is_nothrow_invocable_v<Func, const key&, for_each_value_ref<T, Table>> //
|| std::is_nothrow_invocable_v<Func, for_each_value_ref<T, Table>>;
template <typename Func, typename Table>
static constexpr bool can_for_each_any = 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>
static constexpr bool for_each_is_nothrow_one = !can_for_each<Func, Table, T> //
|| can_for_each_nothrow<Func, Table, T>;
// clang-format off
template <typename Func, typename Table>
static constexpr bool for_each_is_nothrow = 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>;
// clang-format on
template <typename Func, typename Table>
static void do_for_each(Func&& visitor, Table&& tbl) noexcept(for_each_is_nothrow<Func&&, Table&&>)
{
static_assert(can_for_each_any<Func&&, Table&&>,
"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>);
const auto keep_going =
static_cast<node_ref>(*kvp.second)
.visit(
[&](auto&& v) noexcept(for_each_is_nothrow_one<Func&&, Table&&, decltype(v)>)
{
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;
}
}
/// \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&>)
{
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&&>)
{
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&>)
{
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&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<const table&&>(*this));
return static_cast<const table&&>(*this);
}
/// @}
/// \name Size and Capacity

View File

@ -5,8 +5,8 @@
#pragma once
#define TOML_LIB_MAJOR 3
#define TOML_LIB_MINOR 0
#define TOML_LIB_PATCH 1
#define TOML_LIB_MINOR 1
#define TOML_LIB_PATCH 0
#define TOML_LANG_MAJOR 1
#define TOML_LANG_MINOR 0

View File

@ -1,7 +1,7 @@
project(
'tomlplusplus',
'cpp',
version: '3.0.1',
version: '3.1.0',
meson_version: '>=0.54.0',
license: 'MIT',
subproject_dir: 'external',

View File

@ -555,6 +555,12 @@ backslash = 'This string has a \\ backslash character.')"sv;
static constexpr auto string_with_pound = R"(pound = "We see no # comments here."
poundcomment = "But there are # some comments here." # Did I # mess you up?)"sv;
#if TOML_LANG_UNRELEASED
static constexpr auto string_escape_esc = R"(esc = "\e There is no escape! \e")"sv;
#endif // TOML_LANG_UNRELEASED
#if UNICODE_LITERALS_OK
static constexpr auto string_escape_tricky = R"(end_esc = "String does not end here\" but ends here\\"
@ -2287,6 +2293,20 @@ in it.)"sv },
REQUIRE(tbl == expected);
});
#if TOML_LANG_UNRELEASED
parsing_should_succeed(FILE_LINE_ARGS,
string_escape_esc,
[](toml::table&& tbl) // string-escape-esc
{
const auto expected = toml::table{
{ R"(esc)"sv, "\x1B There is no escape! \x1B"sv },
};
REQUIRE(tbl == expected);
});
#endif // TOML_LANG_UNRELEASED
#if UNICODE_LITERALS_OK
parsing_should_succeed(FILE_LINE_ARGS,

372
tests/for_each.cpp Normal file
View File

@ -0,0 +1,372 @@
// 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
#include "tests.h"
TOML_DISABLE_SPAM_WARNINGS;
TEST_CASE("array::for_each")
{
toml::array arr{ 0, 1, 2, 3.0, "four", "five", 6 };
// check lvalue propagates correctly
static_cast<array&>(arr).for_each(
[](auto&& elem, size_t) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<array&>(arr).for_each(
[](size_t, auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<array&>(arr).for_each(
[](auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
// check rvalue propagates correctly
static_cast<array&&>(arr).for_each(
[](auto&& elem, size_t) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<array&&>(arr).for_each(
[](size_t, auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<array&&>(arr).for_each(
[](auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
// check const lvalue propagates correctly
static_cast<const array&>(arr).for_each(
[](auto&& elem, size_t) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<const array&>(arr).for_each(
[](size_t, auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<const array&>(arr).for_each(
[](auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
// check const rvalue propagates correctly
static_cast<const array&&>(arr).for_each(
[](auto&& elem, size_t) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<const array&&>(arr).for_each(
[](size_t, auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<const array&&>(arr).for_each(
[](auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
// check noexcept - func(elem, i)
{
static constexpr auto throwing_visitor = [](auto&&, size_t) noexcept(false) {};
static constexpr auto non_throwing_visitor = [](auto&&, size_t) noexcept(true) {};
static_assert(!noexcept(static_cast<array&>(arr).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<array&&>(arr).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const array&>(arr).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const array&&>(arr).for_each(throwing_visitor)));
static_assert(noexcept(static_cast<array&>(arr).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<array&&>(arr).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const array&>(arr).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const array&&>(arr).for_each(non_throwing_visitor)));
}
// check noexcept - func(i, elem)
{
static constexpr auto throwing_visitor = [](size_t, auto&&) noexcept(false) {};
static constexpr auto non_throwing_visitor = [](size_t, auto&&) noexcept(true) {};
static_assert(!noexcept(static_cast<array&>(arr).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<array&&>(arr).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const array&>(arr).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const array&&>(arr).for_each(throwing_visitor)));
static_assert(noexcept(static_cast<array&>(arr).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<array&&>(arr).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const array&>(arr).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const array&&>(arr).for_each(non_throwing_visitor)));
}
// check noexcept - func(elem)
{
static constexpr auto throwing_visitor = [](auto&&) noexcept(false) {};
static constexpr auto non_throwing_visitor = [](auto&&) noexcept(true) {};
static_assert(!noexcept(static_cast<array&>(arr).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<array&&>(arr).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const array&>(arr).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const array&&>(arr).for_each(throwing_visitor)));
static_assert(noexcept(static_cast<array&>(arr).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<array&&>(arr).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const array&>(arr).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const array&&>(arr).for_each(non_throwing_visitor)));
}
// check that the iteration actually does what it says on the box
{
toml::array arr2;
arr.for_each([&](const auto& val) { arr2.push_back(val); });
CHECK(arr == arr2);
}
// check that visitation works for a specific type
{
toml::array arr2;
arr.for_each([&](const toml::value<int64_t>& val) { arr2.push_back(val); });
CHECK(arr2 == toml::array{ 0, 1, 2, 6 });
}
// check that early-stopping works
{
toml::array arr2;
arr.for_each(
[&](const auto& val)
{
if constexpr (!toml::is_number<decltype(val)>)
return false;
else
{
arr2.push_back(val);
return true;
}
});
CHECK(arr2 == toml::array{ 0, 1, 2, 3.0 });
}
}
TEST_CASE("table::for_each")
{
table tbl{ { "zero", 0 }, //
{ "one", 1 }, //
{ "two", 2 }, //
{ "three", 3.0 }, //
{ "four", "four" }, //
{ "five", "five" }, //
{ "six", 6 } };
// check lvalue propagates correctly
static_cast<table&>(tbl).for_each(
[](const toml::key&, auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<table&>(tbl).for_each(
[](auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
// check rvalue propagates correctly
static_cast<table&&>(tbl).for_each(
[](const toml::key&, auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<table&&>(tbl).for_each(
[](auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(!std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
// check const lvalue propagates correctly
static_cast<const table&>(tbl).for_each(
[](const toml::key&, auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<const table&>(tbl).for_each(
[](auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_lvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
// check const rvalue propagates correctly
static_cast<const table&&>(tbl).for_each(
[](const toml::key&, auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
static_cast<const table&&>(tbl).for_each(
[](auto&& elem) noexcept
{
using elem_ref_type = decltype(elem);
static_assert(std::is_rvalue_reference_v<elem_ref_type>);
using elem_type = std::remove_reference_t<elem_ref_type>;
static_assert(std::is_const_v<elem_type>);
static_assert(!std::is_volatile_v<elem_type>);
});
// check noexcept - func(key, value)
{
static constexpr auto throwing_visitor = [](const toml::key&, auto&&) noexcept(false) {};
static constexpr auto non_throwing_visitor = [](const toml::key&, auto&&) noexcept(true) {};
static_assert(!noexcept(static_cast<table&>(tbl).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<table&&>(tbl).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const table&>(tbl).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const table&&>(tbl).for_each(throwing_visitor)));
static_assert(noexcept(static_cast<table&>(tbl).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<table&&>(tbl).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const table&>(tbl).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const table&&>(tbl).for_each(non_throwing_visitor)));
}
// check noexcept - func(value)
{
static constexpr auto throwing_visitor = [](auto&&) noexcept(false) {};
static constexpr auto non_throwing_visitor = [](auto&&) noexcept(true) {};
static_assert(!noexcept(static_cast<table&>(tbl).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<table&&>(tbl).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const table&>(tbl).for_each(throwing_visitor)));
static_assert(!noexcept(static_cast<const table&&>(tbl).for_each(throwing_visitor)));
static_assert(noexcept(static_cast<table&>(tbl).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<table&&>(tbl).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const table&>(tbl).for_each(non_throwing_visitor)));
static_assert(noexcept(static_cast<const table&&>(tbl).for_each(non_throwing_visitor)));
}
// check that the iteration actually does what it says on the box
{
toml::table tbl2;
tbl.for_each([&](auto&& key, auto&& val) { tbl2.insert_or_assign(key, val); });
CHECK(tbl == tbl2);
}
// check that visitation works for a specific type
{
toml::table tbl2;
tbl.for_each([&](auto&& key, const toml::value<int64_t>& val) { tbl2.insert_or_assign(key, val); });
CHECK(tbl2
== table{ { "zero", 0 }, //
{ "one", 1 }, //
{ "two", 2 }, //
{ "six", 6 } });
}
// check that early-stopping works
{
toml::table tbl2;
size_t added{};
tbl.for_each(
[&](auto&& key, const auto& val)
{
tbl2.insert_or_assign(key, val);
added++;
return added < 3u;
});
CHECK(tbl2.size() == 3u);
}
}

View File

@ -5,25 +5,27 @@ test_sources = [
'conformance_iarna_invalid.cpp',
'conformance_iarna_valid.cpp',
'formatters.cpp',
'for_each.cpp',
'impl_toml.cpp',
'tests.cpp',
'parsing_floats.cpp',
'main.cpp',
'manipulating_arrays.cpp',
'manipulating_parse_result.cpp',
'manipulating_tables.cpp',
'manipulating_values.cpp',
'parsing_arrays.cpp',
'parsing_booleans.cpp',
'parsing_comments.cpp',
'parsing_dates_and_times.cpp',
'parsing_floats.cpp',
'parsing_integers.cpp',
'parsing_key_value_pairs.cpp',
'parsing_spec_example.cpp',
'parsing_strings.cpp',
'parsing_tables.cpp',
'main.cpp',
'manipulating_arrays.cpp',
'manipulating_tables.cpp',
'manipulating_values.cpp',
'visit.cpp',
'tests.cpp',
'user_feedback.cpp',
'using_iterators.cpp',
'visit.cpp',
'windows_compat.cpp'
]

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

View File

@ -78,6 +78,7 @@
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>

495
toml.hpp
View File

@ -1,6 +1,6 @@
//----------------------------------------------------------------------------------------------------------------------
//
// toml++ v3.0.1
// toml++ v3.1.0
// https://github.com/marzer/tomlplusplus
// SPDX-License-Identifier: MIT
//
@ -846,8 +846,8 @@
//******** impl/version.h ********************************************************************************************
#define TOML_LIB_MAJOR 3
#define TOML_LIB_MINOR 0
#define TOML_LIB_PATCH 1
#define TOML_LIB_MINOR 1
#define TOML_LIB_PATCH 0
#define TOML_LANG_MAJOR 1
#define TOML_LANG_MINOR 0
@ -1892,6 +1892,10 @@ TOML_IMPL_NAMESPACE_START
};
template <typename T>
inline constexpr node_type node_type_of = node_type_getter<unwrap_node<remove_cvref<T>>>::value;
template <typename T, typename ConvertFrom>
inline constexpr bool is_constructible_or_convertible = std::is_constructible_v<T, ConvertFrom> //
|| std::is_convertible_v<ConvertFrom, T>;
}
TOML_IMPL_NAMESPACE_END;
@ -1951,8 +1955,8 @@ TOML_NAMESPACE_START
inline constexpr bool is_value = is_string<T> || is_number<T> || is_boolean<T> || is_chronological<T>;
template <typename T>
inline constexpr bool is_node =
std::is_same_v<toml::node, impl::remove_cvref<T>> || std::is_base_of_v<toml::node, impl::remove_cvref<T>>;
inline constexpr bool is_node = std::is_same_v<toml::node, impl::remove_cvref<T>> //
|| std::is_base_of_v<toml::node, impl::remove_cvref<T>>;
template <typename T>
inline constexpr bool is_node_view = impl::is_one_of<impl::remove_cvref<T>, node_view<node>, node_view<const node>>;
@ -2990,145 +2994,141 @@ TOML_NAMESPACE_START
private:
template <typename Func, typename Node, typename T>
static constexpr bool can_visit = std::is_invocable_v<Func, ref_cast_type<T, Node>>;
template <typename Func, typename Node, typename T>
static constexpr bool can_visit_nothrow = std::is_nothrow_invocable_v<Func, ref_cast_type<T, Node>>;
template <typename Func, typename Node>
static constexpr bool can_visit_any = can_visit<Func, Node, table> //
|| can_visit<Func, Node, array> //
|| can_visit<Func, Node, std::string> //
|| can_visit<Func, Node, int64_t> //
|| can_visit<Func, Node, double> //
|| can_visit<Func, Node, bool> //
|| can_visit<Func, Node, date> //
|| can_visit<Func, Node, time> //
|| can_visit<Func, Node, date_time>;
// clang-format off
template <typename Func, typename N, typename T>
static constexpr bool can_visit = std::is_invocable_v<Func, ref_cast_type<T, N>>;
template <typename Func, typename Node>
static constexpr bool can_visit_all = can_visit<Func, Node, table> //
&& can_visit<Func, Node, array> //
&& can_visit<Func, Node, std::string> //
&& can_visit<Func, Node, int64_t> //
&& can_visit<Func, Node, double> //
&& can_visit<Func, Node, bool> //
&& can_visit<Func, Node, date> //
&& can_visit<Func, Node, time> //
&& can_visit<Func, Node, date_time>;
template <typename Func, typename N>
static constexpr bool can_visit_any =
can_visit<Func, N, table>
|| can_visit<Func, N, array>
|| can_visit<Func, N, std::string>
|| can_visit<Func, N, int64_t>
|| can_visit<Func, N, double>
|| can_visit<Func, N, bool>
|| can_visit<Func, N, date>
|| can_visit<Func, N, time>
|| can_visit<Func, N, date_time>;
template <typename Func, typename Node, typename T>
static constexpr bool visit_is_nothrow_one = !can_visit<Func, Node, T> || can_visit_nothrow<Func, Node, T>;
template <typename Func, typename N>
static constexpr bool can_visit_all =
can_visit<Func, N, table>
&& can_visit<Func, N, array>
&& can_visit<Func, N, std::string>
&& can_visit<Func, N, int64_t>
&& can_visit<Func, N, double>
&& can_visit<Func, N, bool>
&& can_visit<Func, N, date>
&& can_visit<Func, N, time>
&& can_visit<Func, N, date_time>;
template <typename Func, typename N, typename T>
static constexpr bool visit_is_nothrow_one =
!can_visit<Func, N, T>
|| std::is_nothrow_invocable_v<Func, ref_cast_type<T, N>>;
template <typename Func, typename N>
static constexpr bool visit_is_nothrow =
visit_is_nothrow_one<Func, N, table>
&& visit_is_nothrow_one<Func, N, array>
&& visit_is_nothrow_one<Func, N, std::string>
&& visit_is_nothrow_one<Func, N, int64_t>
&& visit_is_nothrow_one<Func, N, double>
&& visit_is_nothrow_one<Func, N, bool>
&& visit_is_nothrow_one<Func, N, date>
&& visit_is_nothrow_one<Func, N, time>
&& visit_is_nothrow_one<Func, N, date_time>;
template <typename Func, typename Node>
static constexpr bool visit_is_nothrow = visit_is_nothrow_one<Func, Node, table> //
&& visit_is_nothrow_one<Func, Node, array> //
&& visit_is_nothrow_one<Func, Node, std::string> //
&& visit_is_nothrow_one<Func, Node, int64_t> //
&& visit_is_nothrow_one<Func, Node, double> //
&& visit_is_nothrow_one<Func, Node, bool> //
&& visit_is_nothrow_one<Func, Node, date> //
&& visit_is_nothrow_one<Func, Node, time> //
&& visit_is_nothrow_one<Func, Node, date_time>;
// clang-format on
template <typename Func, typename N, typename T, bool = can_visit<Func, N, T>>
template <typename Func, typename Node, typename T, bool = can_visit<Func, Node, T>>
struct visit_return_type_
{
using type = decltype(std::declval<Func>()(std::declval<ref_cast_type<T, N>>()));
using type = decltype(std::declval<Func>()(std::declval<ref_cast_type<T, Node>>()));
};
template <typename Func, typename N, typename T>
struct visit_return_type_<Func, N, T, false>
template <typename Func, typename Node, typename T>
struct visit_return_type_<Func, Node, T, false>
{
using type = void;
};
template <typename Func, typename N, typename T>
using visit_return_type = typename visit_return_type_<Func, N, T>::type;
template <typename Func, typename Node, typename T>
using visit_return_type = typename visit_return_type_<Func, Node, T>::type;
template <typename A, typename B>
using nonvoid = std::conditional_t<std::is_void_v<A>, B, A>;
template <typename N, typename Func>
static decltype(auto) do_visit(N&& n, Func&& visitor) noexcept(visit_is_nothrow<Func&&, N&&>)
template <typename Func, typename Node>
static decltype(auto) do_visit(Func&& visitor, Node&& n) noexcept(visit_is_nothrow<Func&&, Node&&>)
{
static_assert(can_visit_any<Func&&, N&&>,
static_assert(can_visit_any<Func&&, Node&&>,
"TOML node visitors must be invocable for at least one of the toml::node "
"specializations:" TOML_SA_NODE_TYPE_LIST);
switch (n.type())
{
case node_type::table:
if constexpr (can_visit<Func&&, N&&, table>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<table>());
if constexpr (can_visit<Func&&, Node&&, table>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<table>());
break;
case node_type::array:
if constexpr (can_visit<Func&&, N&&, array>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<array>());
if constexpr (can_visit<Func&&, Node&&, array>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<array>());
break;
case node_type::string:
if constexpr (can_visit<Func&&, N&&, std::string>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<std::string>());
if constexpr (can_visit<Func&&, Node&&, std::string>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<std::string>());
break;
case node_type::integer:
if constexpr (can_visit<Func&&, N&&, int64_t>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<int64_t>());
if constexpr (can_visit<Func&&, Node&&, int64_t>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<int64_t>());
break;
case node_type::floating_point:
if constexpr (can_visit<Func&&, N&&, double>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<double>());
if constexpr (can_visit<Func&&, Node&&, double>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<double>());
break;
case node_type::boolean:
if constexpr (can_visit<Func&&, N&&, bool>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<bool>());
if constexpr (can_visit<Func&&, Node&&, bool>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<bool>());
break;
case node_type::date:
if constexpr (can_visit<Func&&, N&&, date>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<date>());
if constexpr (can_visit<Func&&, Node&&, date>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<date>());
break;
case node_type::time:
if constexpr (can_visit<Func&&, N&&, time>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<time>());
if constexpr (can_visit<Func&&, Node&&, time>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<time>());
break;
case node_type::date_time:
if constexpr (can_visit<Func&&, N&&, date_time>)
return static_cast<Func&&>(visitor)(static_cast<N&&>(n).template ref_cast<date_time>());
if constexpr (can_visit<Func&&, Node&&, date_time>)
return static_cast<Func&&>(visitor)(static_cast<Node&&>(n).template ref_cast<date_time>());
break;
case node_type::none: TOML_UNREACHABLE;
default: TOML_UNREACHABLE;
}
if constexpr (can_visit_all<Func&&, N&&>)
TOML_UNREACHABLE;
else
if constexpr (!can_visit_all<Func&&, Node&&>)
{
// clang-format off
using return_type =
nonvoid<visit_return_type<Func&&, N&&, table>,
nonvoid<visit_return_type<Func&&, N&&, array>,
nonvoid<visit_return_type<Func&&, N&&, std::string>,
nonvoid<visit_return_type<Func&&, N&&, int64_t>,
nonvoid<visit_return_type<Func&&, N&&, double>,
nonvoid<visit_return_type<Func&&, N&&, bool>,
nonvoid<visit_return_type<Func&&, N&&, date>,
nonvoid<visit_return_type<Func&&, N&&, time>,
visit_return_type<Func&&, N&&, date_time>
nonvoid<visit_return_type<Func&&, Node&&, table>,
nonvoid<visit_return_type<Func&&, Node&&, array>,
nonvoid<visit_return_type<Func&&, Node&&, std::string>,
nonvoid<visit_return_type<Func&&, Node&&, int64_t>,
nonvoid<visit_return_type<Func&&, Node&&, double>,
nonvoid<visit_return_type<Func&&, Node&&, bool>,
nonvoid<visit_return_type<Func&&, Node&&, date>,
nonvoid<visit_return_type<Func&&, Node&&, time>,
visit_return_type<Func&&, Node&&, date_time>
>>>>>>>>;
// clang-format on
@ -3147,25 +3147,25 @@ TOML_NAMESPACE_START
template <typename Func>
decltype(auto) visit(Func&& visitor) & noexcept(visit_is_nothrow<Func&&, node&>)
{
return do_visit(*this, static_cast<Func&&>(visitor));
return do_visit(static_cast<Func&&>(visitor), *this);
}
template <typename Func>
decltype(auto) visit(Func&& visitor) && noexcept(visit_is_nothrow<Func&&, node&&>)
{
return do_visit(static_cast<node&&>(*this), static_cast<Func&&>(visitor));
return do_visit(static_cast<Func&&>(visitor), static_cast<node&&>(*this));
}
template <typename Func>
decltype(auto) visit(Func&& visitor) const& noexcept(visit_is_nothrow<Func&&, const node&>)
{
return do_visit(*this, static_cast<Func&&>(visitor));
return do_visit(static_cast<Func&&>(visitor), *this);
}
template <typename Func>
decltype(auto) visit(Func&& visitor) const&& noexcept(visit_is_nothrow<Func&&, const node&&>)
{
return do_visit(static_cast<const node&&>(*this), static_cast<Func&&>(visitor));
return do_visit(static_cast<Func&&>(visitor), static_cast<const node&&>(*this));
}
TOML_NODISCARD
@ -5564,6 +5564,166 @@ TOML_NAMESPACE_START
return const_iterator{ elems_.cend() };
}
private:
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>
static constexpr bool can_for_each = std::is_invocable_v<Func, for_each_elem_ref<T, Array>, size_t> //
|| std::is_invocable_v<Func, size_t, for_each_elem_ref<T, Array>> //
|| std::is_invocable_v<Func, for_each_elem_ref<T, Array>>;
template <typename Func, typename Array, typename T>
static constexpr bool can_for_each_nothrow =
std::is_nothrow_invocable_v<Func, for_each_elem_ref<T, Array>, size_t> //
|| std::is_nothrow_invocable_v<Func, size_t, for_each_elem_ref<T, Array>> //
|| std::is_nothrow_invocable_v<Func, for_each_elem_ref<T, Array>>;
template <typename Func, typename Array>
static constexpr bool can_for_each_any = 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>
static constexpr bool for_each_is_nothrow_one = !can_for_each<Func, Array, T> //
|| can_for_each_nothrow<Func, Array, T>;
// clang-format off
template <typename Func, typename Array>
static constexpr bool for_each_is_nothrow = 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>;
// clang-format on
template <typename Func, typename Array>
static void do_for_each(Func&& visitor, Array&& arr) noexcept(for_each_is_nothrow<Func&&, Array&&>)
{
static_assert(can_for_each_any<Func&&, Array&&>,
"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>);
const auto keep_going =
static_cast<node_ref>(static_cast<Array&&>(arr)[i])
.visit(
[&](auto&& elem) noexcept(for_each_is_nothrow_one<Func&&, Array&&, decltype(elem)>)
{
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;
}
}
public:
template <typename Func>
array& for_each(Func&& visitor) & noexcept(for_each_is_nothrow<Func&&, array&>)
{
do_for_each(static_cast<Func&&>(visitor), *this);
return *this;
}
template <typename Func>
array&& for_each(Func&& visitor) && noexcept(for_each_is_nothrow<Func&&, array&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<array&&>(*this));
return static_cast<array&&>(*this);
}
template <typename Func>
const array& for_each(Func&& visitor) const& noexcept(for_each_is_nothrow<Func&&, const array&>)
{
do_for_each(static_cast<Func&&>(visitor), *this);
return *this;
}
template <typename Func>
const array&& for_each(Func&& visitor) const&& noexcept(for_each_is_nothrow<Func&&, const array&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<const array&&>(*this));
return static_cast<const array&&>(*this);
}
TOML_NODISCARD
bool empty() const noexcept
{
@ -6115,8 +6275,8 @@ TOML_NAMESPACE_START
inline constexpr bool is_key = std::is_same_v<impl::remove_cvref<T>, toml::key>;
template <typename T>
inline constexpr bool is_key_or_convertible =
is_key<T> || std::is_constructible_v<toml::key, T> || std::is_convertible_v<T, toml::key>;
inline constexpr bool is_key_or_convertible = is_key<T> //
|| impl::is_constructible_or_convertible<toml::key, T>;
}
TOML_NAMESPACE_END;
@ -6316,6 +6476,7 @@ TOML_NAMESPACE_START
private:
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_;
@ -6709,6 +6870,149 @@ TOML_NAMESPACE_START
return const_iterator{ map_.cend() };
}
private:
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>
static constexpr bool can_for_each = std::is_invocable_v<Func, const key&, for_each_value_ref<T, Table>> //
|| std::is_invocable_v<Func, for_each_value_ref<T, Table>>;
template <typename Func, typename Table, typename T>
static constexpr bool can_for_each_nothrow =
std::is_nothrow_invocable_v<Func, const key&, for_each_value_ref<T, Table>> //
|| std::is_nothrow_invocable_v<Func, for_each_value_ref<T, Table>>;
template <typename Func, typename Table>
static constexpr bool can_for_each_any = 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>
static constexpr bool for_each_is_nothrow_one = !can_for_each<Func, Table, T> //
|| can_for_each_nothrow<Func, Table, T>;
// clang-format off
template <typename Func, typename Table>
static constexpr bool for_each_is_nothrow = 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>;
// clang-format on
template <typename Func, typename Table>
static void do_for_each(Func&& visitor, Table&& tbl) noexcept(for_each_is_nothrow<Func&&, Table&&>)
{
static_assert(can_for_each_any<Func&&, Table&&>,
"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>);
const auto keep_going =
static_cast<node_ref>(*kvp.second)
.visit(
[&](auto&& v) noexcept(for_each_is_nothrow_one<Func&&, Table&&, decltype(v)>)
{
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;
}
}
public:
template <typename Func>
table& for_each(Func&& visitor) & noexcept(for_each_is_nothrow<Func&&, table&>)
{
do_for_each(static_cast<Func&&>(visitor), *this);
return *this;
}
template <typename Func>
table&& for_each(Func&& visitor) && noexcept(for_each_is_nothrow<Func&&, table&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<table&&>(*this));
return static_cast<table&&>(*this);
}
template <typename Func>
const table& for_each(Func&& visitor) const& noexcept(for_each_is_nothrow<Func&&, const table&>)
{
do_for_each(static_cast<Func&&>(visitor), *this);
return *this;
}
template <typename Func>
const table&& for_each(Func&& visitor) const&& noexcept(for_each_is_nothrow<Func&&, const table&&>)
{
do_for_each(static_cast<Func&&>(visitor), static_cast<const table&&>(*this));
return static_cast<const table&&>(*this);
}
TOML_PURE_INLINE_GETTER
bool empty() const noexcept
{
@ -9190,16 +9494,13 @@ TOML_IMPL_NAMESPACE_START
if ((!lhs != !rhs) || lhs->type() != rhs->type())
return false;
bool same;
lhs->visit(
[=, &same](auto& l) noexcept
return lhs->visit(
[=](auto& l) noexcept
{
using concrete_type = remove_cvref<decltype(l)>;
same = (l == *(rhs->as<concrete_type>()));
return l == *(rhs->as<concrete_type>());
});
return same;
}
}
TOML_IMPL_NAMESPACE_END;

View File

@ -446,6 +446,9 @@ def load_valid_inputs(tests, extern_root):
add_condition(tests['valid']['burntsushi'], '!TOML_MSVC', (
'inline-table-key-dotted', # causes MSVC to run out of heap space during compilation O_o
))
add_condition(tests['valid']['burntsushi'], 'TOML_LANG_UNRELEASED', (
'string-escape-esc', # \e in strings
))
tests['valid']['iarna'] = load_tests(Path(extern_root, 'toml-spec-tests', 'values'), True, (
# these are stress-tests for 'large' datasets. I test these separately. Having them inline in C++ code is insane.

View File

@ -121,6 +121,7 @@ def main():
<ClCompile Include="..\conformance_burntsushi_valid.cpp" />
<ClCompile Include="..\conformance_iarna_invalid.cpp" />
<ClCompile Include="..\conformance_iarna_valid.cpp" />
<ClCompile Include="..\for_each.cpp" />
<ClCompile Include="..\formatters.cpp" />
<ClCompile Include="..\impl_toml.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>