diff --git a/.gitmodules b/.gitmodules index 0dc47189..3263b211 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 -[submodule "external/Catch2"] - path = external/Catch2 - url = https://github.com/catchorg/Catch2.git - branch = v2.x + 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 + shallow = true diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a287e09..58b0e2d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index e7a0b8ca..41e55dd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/README.md b/README.md index 0f892ae2..e39dfa4a 100644 --- a/README.md +++ b/README.md @@ -99,13 +99,13 @@ You'll find some more code examples in the `examples` directory, and plenty more 2. `#include ` ### 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', ] ``` > ℹ️ _[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) ``` > ℹ️ _[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 +``` +> ℹ️ 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 diff --git a/docs/pages/main_page.dox b/docs/pages/main_page.dox index 13ac7a9a..3ffe9e61 100644 --- a/docs/pages/main_page.dox +++ b/docs/pages/main_page.dox @@ -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 diff --git a/external/Catch2 b/external/Catch2 index 958944d2..d71b4617 160000 --- a/external/Catch2 +++ b/external/Catch2 @@ -1 +1 @@ -Subproject commit 958944d27a2d2fb82aa008377bf4f8752f6b848e +Subproject commit d71b4617e9935f8589870af211f5b7552d743654 diff --git a/external/json b/external/json index e4643d1f..fcc36f99 160000 --- a/external/json +++ b/external/json @@ -1 +1 @@ -Subproject commit e4643d1f1b03fc7a1d7b65f17e012ca93680cad8 +Subproject commit fcc36f99ba1afc7baebe24e0c7429d2039d32d99 diff --git a/external/toml-test b/external/toml-test index 983c1a54..10cdc99d 160000 --- a/external/toml-test +++ b/external/toml-test @@ -1 +1 @@ -Subproject commit 983c1a54c5faad31b5d217b1e47c0e074dea7840 +Subproject commit 10cdc99d96fbf1350a8891a9d93295fcc85aa694 diff --git a/include/toml++/impl/array.h b/include/toml++/impl/array.h index 7372933d..29c9168b 100644 --- a/include/toml++/impl/array.h +++ b/include/toml++/impl/array.h @@ -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 + using for_each_elem_ref = impl::copy_cvref>>, Array>; + + template + static constexpr bool can_for_each = std::is_invocable_v, size_t> // + || std::is_invocable_v> // + || std::is_invocable_v>; + + template + static constexpr bool can_for_each_nothrow = + std::is_nothrow_invocable_v, size_t> // + || std::is_nothrow_invocable_v> // + || std::is_nothrow_invocable_v>; + + template + static constexpr bool can_for_each_any = can_for_each // + || can_for_each // + || can_for_each // + || can_for_each // + || can_for_each // + || can_for_each // + || can_for_each // + || can_for_each // + || can_for_each; + + template + static constexpr bool for_each_is_nothrow_one = !can_for_each // + || can_for_each_nothrow; + + // clang-format off + + + template + static constexpr bool for_each_is_nothrow = for_each_is_nothrow_one // + && for_each_is_nothrow_one // + && for_each_is_nothrow_one // + && for_each_is_nothrow_one // + && for_each_is_nothrow_one // + && for_each_is_nothrow_one // + && for_each_is_nothrow_one // + && for_each_is_nothrow_one // + && for_each_is_nothrow_one; + + // clang-format on + + template + static void do_for_each(Func&& visitor, Array&& arr) noexcept(for_each_is_nothrow) + { + static_assert(can_for_each_any, + "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; + static_assert(std::is_reference_v); + + const auto keep_going = + static_cast(static_cast(arr)[i]) + .visit( + [&](auto&& elem) noexcept(for_each_is_nothrow_one) + { + using elem_ref = for_each_elem_ref; + static_assert(std::is_reference_v); + + // func(elem, i) + if constexpr (std::is_invocable_v) + { + using return_type = + decltype(static_cast(visitor)(static_cast(elem), i)); + + if constexpr (impl::is_constructible_or_convertible) + { + return static_cast( + static_cast(visitor)(static_cast(elem), i)); + } + else + { + static_cast(visitor)(static_cast(elem), i); + return true; + } + } + + // func(i, elem) + else if constexpr (std::is_invocable_v) + { + using return_type = + decltype(static_cast(visitor)(i, static_cast(elem))); + + if constexpr (impl::is_constructible_or_convertible) + { + return static_cast( + static_cast(visitor)(i, static_cast(elem))); + } + else + { + static_cast(visitor)(i, static_cast(elem)); + return true; + } + } + + // func(elem) + else if constexpr (std::is_invocable_v) + { + using return_type = + decltype(static_cast(visitor)(static_cast(elem))); + + if constexpr (impl::is_constructible_or_convertible) + { + return static_cast( + static_cast(visitor)(static_cast(elem))); + } + else + { + static_cast(visitor)(static_cast(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: + ///
    + ///
  • `func(elem, index)` + ///
  • `func(elem)` + ///
  • `func(index, elem)` + ///
+ /// Where: + ///
    + ///
  • `elem` will recieve the element as it's concrete type with cvref-qualifications matching the array + ///
  • `index` will recieve a `size_t` indicating the element's index + ///
+ /// 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& 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) + /// 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) + /// { + /// 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 + array& for_each(Func&& visitor) & noexcept(for_each_is_nothrow) + { + do_for_each(static_cast(visitor), *this); + return *this; + } + + /// \brief Invokes a visitor on each element in the array (rvalue overload). + template + array&& for_each(Func&& visitor) && noexcept(for_each_is_nothrow) + { + do_for_each(static_cast(visitor), static_cast(*this)); + return static_cast(*this); + } + + /// \brief Invokes a visitor on each element in the array (const lvalue overload). + template + const array& for_each(Func&& visitor) const& noexcept(for_each_is_nothrow) + { + do_for_each(static_cast(visitor), *this); + return *this; + } + + /// \brief Invokes a visitor on each element in the array (const rvalue overload). + template + const array&& for_each(Func&& visitor) const&& noexcept(for_each_is_nothrow) + { + do_for_each(static_cast(visitor), static_cast(*this)); + return static_cast(*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(this->flatten()); diff --git a/include/toml++/impl/forward_declarations.h b/include/toml++/impl/forward_declarations.h index 2cd795e5..d89ea407 100644 --- a/include/toml++/impl/forward_declarations.h +++ b/include/toml++/impl/forward_declarations.h @@ -916,6 +916,10 @@ TOML_IMPL_NAMESPACE_START }; template inline constexpr node_type node_type_of = node_type_getter>>::value; + + template + inline constexpr bool is_constructible_or_convertible = std::is_constructible_v // + || std::is_convertible_v; } 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 - inline constexpr bool is_node = - std::is_same_v> || std::is_base_of_v>; + inline constexpr bool is_node = std::is_same_v> // + || std::is_base_of_v>; /// \brief Metafunction for determining if a type is, or is a reference to, a toml::node_view. template diff --git a/include/toml++/impl/key.h b/include/toml++/impl/key.h index 9ef7b0a3..da407980 100644 --- a/include/toml++/impl/key.h +++ b/include/toml++/impl/key.h @@ -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 - inline constexpr bool is_key_or_convertible = - is_key || std::is_constructible_v || std::is_convertible_v; + inline constexpr bool is_key_or_convertible = is_key // + || impl::is_constructible_or_convertible; } TOML_NAMESPACE_END; diff --git a/include/toml++/impl/node.h b/include/toml++/impl/node.h index bd8f8e51..5634a13b 100644 --- a/include/toml++/impl/node.h +++ b/include/toml++/impl/node.h @@ -530,7 +530,7 @@ TOML_NAMESPACE_START /// { /// std::cout << "- " << std::setw(18) << std::left << type_name; /// using type = std::remove_pointer_t; - /// if (std::optional val = tbl.get(key)->value()) + /// if (auto val = tbl.get(key)->value(); val) /// std::cout << *val << "\n"; /// else /// std::cout << "n/a\n"; @@ -732,145 +732,141 @@ TOML_NAMESPACE_START private: /// \cond + template + static constexpr bool can_visit = std::is_invocable_v>; + + template + static constexpr bool can_visit_nothrow = std::is_nothrow_invocable_v>; + + template + static constexpr bool can_visit_any = can_visit // + || can_visit // + || can_visit // + || can_visit // + || can_visit // + || can_visit // + || can_visit // + || can_visit // + || can_visit; + // clang-format off - template - static constexpr bool can_visit = std::is_invocable_v>; - - template - static constexpr bool can_visit_any = - can_visit - || can_visit - || can_visit - || can_visit - || can_visit - || can_visit - || can_visit - || can_visit - || can_visit; - - template - static constexpr bool can_visit_all = - can_visit - && can_visit - && can_visit - && can_visit - && can_visit - && can_visit - && can_visit - && can_visit - && can_visit; - - template - static constexpr bool visit_is_nothrow_one = - !can_visit - || std::is_nothrow_invocable_v>; - - template - static constexpr bool visit_is_nothrow = - visit_is_nothrow_one - && visit_is_nothrow_one - && visit_is_nothrow_one - && visit_is_nothrow_one - && visit_is_nothrow_one - && visit_is_nothrow_one - && visit_is_nothrow_one - && visit_is_nothrow_one - && visit_is_nothrow_one; + template + static constexpr bool can_visit_all = can_visit // + && can_visit // + && can_visit // + && can_visit // + && can_visit // + && can_visit // + && can_visit // + && can_visit // + && can_visit; + + template + static constexpr bool visit_is_nothrow_one = !can_visit || can_visit_nothrow; + + template + static constexpr bool visit_is_nothrow = visit_is_nothrow_one // + && visit_is_nothrow_one // + && visit_is_nothrow_one // + && visit_is_nothrow_one // + && visit_is_nothrow_one // + && visit_is_nothrow_one // + && visit_is_nothrow_one // + && visit_is_nothrow_one // + && visit_is_nothrow_one; // clang-format on - template > + template > struct visit_return_type_ { - using type = decltype(std::declval()(std::declval>())); + using type = decltype(std::declval()(std::declval>())); }; - template - struct visit_return_type_ + template + struct visit_return_type_ { using type = void; }; - template - using visit_return_type = typename visit_return_type_::type; + template + using visit_return_type = typename visit_return_type_::type; template using nonvoid = std::conditional_t, B, A>; - template - static decltype(auto) do_visit(N&& n, Func&& visitor) noexcept(visit_is_nothrow) + template + static decltype(auto) do_visit(Func&& visitor, Node&& n) noexcept(visit_is_nothrow) { - static_assert(can_visit_any, + static_assert(can_visit_any, "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) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast
()); break; case node_type::array: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::string: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::integer: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::floating_point: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::boolean: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::date: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::time: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast
()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast
()); break; case node_type::array: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::string: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::integer: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::floating_point: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::boolean: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::date: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast()); + if constexpr (can_visit) + return static_cast(visitor)(static_cast(n).template ref_cast()); break; case node_type::time: - if constexpr (can_visit) - return static_cast(visitor)(static_cast(n).template ref_cast