diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3f15ff06..7749536d 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -132,6 +132,7 @@ jobs: runs-on: ubuntu-latest container: silkeh/clang:latest strategy: + fail-fast: false matrix: cxx_standard: [ "11", "14", "17", "20", "23" ] build_type: [ Debug, Release ] @@ -169,6 +170,7 @@ jobs: runs-on: ubuntu-latest container: gcc:latest strategy: + fail-fast: false matrix: cxx_standard: [ "11", "14", "17", "20", "23" ] build_type: [ Debug, Release ] @@ -199,6 +201,7 @@ jobs: ci_test_clang_versions: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: compiler: [ "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19" ] build_type: [ Debug, Release ] @@ -236,6 +239,7 @@ jobs: ci_test_gcc_versions: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: compiler: [ "7", "8", "9", "10", "11", "12", "13", "14", latest ] build_type: [ Debug, Release ] diff --git a/docs/docs/api/basic_node/constructor.md b/docs/docs/api/basic_node/constructor.md index 0d25876f..1387a720 100644 --- a/docs/docs/api/basic_node/constructor.md +++ b/docs/docs/api/basic_node/constructor.md @@ -64,8 +64,16 @@ Available overloads are: Template parameter `CompatibleType` includes, but not limited to, the following types: * sequences: * [`sequence_type`](sequence_type.md) + * container types which fulfills the following requirements, e.g., `std::vector` or `std::set`. + * Both `begin()` and `end()` are callable on a `CompatibleType` object. + * `CompatibleType` doesn't have both `key_type` and `mapped_type` member types. + * A [`string_type`](string_type.md) object is not constructible from a `CompatibleType` object. + * `std::pair` and `std::tuple` * mappings * [`mapping_type`](mapping_type.md) + * container types which fullfills the following requirements, e.g., `std::map` or `std::unordered_multimap`. + * Both `begin()` and `end()` are callable on a `CompatibleType` object. + * `CompatibleType` has both `key_type` and `mapped_type` member types. * null * [`std::nullptr_t`](https://en.cppreference.com/w/cpp/types/nullptr_t) * booleans diff --git a/examples/apis/basic_node/constructor_6.cpp b/examples/apis/basic_node/constructor_6.cpp index 1f1fe47b..af55e6b0 100644 --- a/examples/apis/basic_node/constructor_6.cpp +++ b/examples/apis/basic_node/constructor_6.cpp @@ -7,11 +7,32 @@ // SPDX-License-Identifier: MIT #include +#include +#include +#include #include int main() { - double pi = 3.141592; - fkyaml::node n = pi; - std::cout << n << std::endl; + // create nodes from objects of various types. + fkyaml::node sequence0 = std::list {true, false, false}; + fkyaml::node sequence1 = std::make_tuple("foo", 123, true); + std::unordered_map map_val = {{123, 3.14f}, {-456, 1.41f}}; + fkyaml::node mapping = std::move(map_val); + fkyaml::node null = nullptr; + fkyaml::node boolean = false; + fkyaml::node integer = 12345; + fkyaml::node floating_point = 3.141592; + const char str_chars[] = "test"; + fkyaml::node string = str_chars; + + // print node values + std::cout << sequence0 << std::endl; + std::cout << sequence1 << std::endl; + std::cout << mapping << std::endl; + std::cout << null << std::endl; + std::cout << boolean << std::endl; + std::cout << integer << std::endl; + std::cout << floating_point << std::endl; + std::cout << string << std::endl; return 0; } diff --git a/examples/apis/basic_node/constructor_6.output b/examples/apis/basic_node/constructor_6.output index 41bec393..9efc1709 100644 --- a/examples/apis/basic_node/constructor_6.output +++ b/examples/apis/basic_node/constructor_6.output @@ -1 +1,16 @@ +- true +- false +- false + +- foo +- 123 +- true + +-456: 1.41 +123: 3.14 + +null +false +12345 3.14159 +test diff --git a/include/fkYAML/detail/conversions/to_node.hpp b/include/fkYAML/detail/conversions/to_node.hpp index a8acb483..dc258948 100644 --- a/include/fkYAML/detail/conversions/to_node.hpp +++ b/include/fkYAML/detail/conversions/to_node.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -30,165 +31,57 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN /// @warning All the specialization must call n.m_node_value.destroy() first in the construct function to avoid /// memory leak. /// @tparam node_type The resulting YAML node value type. -template -struct external_node_constructor; - -/// @brief The specialization of external_node_constructor for sequence nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with const lvalue sequence. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param s A lvalue sequence value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, const typename BasicNodeType::sequence_type& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::seq_bit; - n.m_node_value.p_sequence = BasicNodeType::template create_object(s); - } - - /// @brief Constructs a basic_node object with rvalue sequence. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param s A rvalue sequence value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::sequence_type&& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::seq_bit; - n.m_node_value.p_sequence = - BasicNodeType::template create_object(std::move(s)); +template +struct external_node_constructor { + template + static void sequence(BasicNodeType& n, Args&&... args) { + destroy(n); + n.m_attrs |= node_attr_bits::seq_bit; + n.m_node_value.p_sequence = create_object(std::forward(args)...); } -}; -/// @brief The specialization of external_node_constructor for mapping nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with const lvalue mapping. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param m A lvalue mapping value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, const typename BasicNodeType::mapping_type& m) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::map_bit; - n.m_node_value.p_mapping = BasicNodeType::template create_object(m); + template + static void mapping(BasicNodeType& n, Args&&... args) { + destroy(n); + n.m_attrs |= node_attr_bits::map_bit; + n.m_node_value.p_mapping = create_object(std::forward(args)...); } - /// @brief Constructs a basic_node object with rvalue mapping. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param m A rvalue mapping value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::mapping_type&& m) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::map_bit; - n.m_node_value.p_mapping = - BasicNodeType::template create_object(std::move(m)); - } -}; - -/// @brief The specialization of external_node_constructor for null nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with nullptr. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param (unused) nullptr - template ::value, int> = 0> - static void construct(BasicNodeType& n, std::nullptr_t /*unused*/) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::null_bit; + static void null_scalar(BasicNodeType& n, std::nullptr_t) { + destroy(n); + n.m_attrs |= node_attr_bits::null_bit; n.m_node_value.p_mapping = nullptr; } -}; -/// @brief The specialization of external_node_constructor for boolean scalar nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with boolean. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param b A boolean value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::boolean_type b) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::bool_bit; + static void boolean_scalar(BasicNodeType& n, const typename BasicNodeType::boolean_type b) { + destroy(n); + n.m_attrs |= node_attr_bits::bool_bit; n.m_node_value.boolean = b; } -}; -/// @brief The specialization of external_node_constructor for integer scalar nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with integers. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param i An integer value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::integer_type i) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::int_bit; + static void integer_scalar(BasicNodeType& n, const typename BasicNodeType::integer_type i) { + destroy(n); + n.m_attrs |= node_attr_bits::int_bit; n.m_node_value.integer = i; } -}; -/// @brief The specialization of external_node_constructor for float number scalar nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with floating point numbers. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param f A floating point number. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::float_number_type f) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::float_bit; + static void float_scalar(BasicNodeType& n, const typename BasicNodeType::float_number_type f) { + destroy(n); + n.m_attrs |= node_attr_bits::float_bit; n.m_node_value.float_val = f; } -}; -/// @brief The specialization of external_node_constructor for string scalar nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with const lvalue strings. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param s A constant lvalue string. - template ::value, int> = 0> - static void construct(BasicNodeType& n, const typename BasicNodeType::string_type& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::string_bit; - n.m_node_value.p_string = BasicNodeType::template create_object(s); + template + static void string_scalar(BasicNodeType& n, Args&&... args) { + destroy(n); + n.m_attrs |= node_attr_bits::string_bit; + n.m_node_value.p_string = create_object(std::forward(args)...); } - /// @brief Constructs a basic_node object with rvalue strings. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param s A rvalue string. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::string_type&& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::string_bit; - n.m_node_value.p_string = - BasicNodeType::template create_object(std::move(s)); - } - - /// @brief Constructs a basic_node object with compatible strings. - /// @tparam BasicNodeType A basic_node template instance type. - /// @tparam CompatibleStringType A compatible string type. - /// @param n A basic_node object. - /// @param s A compatible string. - template < - typename BasicNodeType, typename CompatibleStringType, - enable_if_t< - conjunction< - is_basic_node, - negation>>::value, - int> = 0> - static void construct(BasicNodeType& n, const CompatibleStringType& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::string_bit; - n.m_node_value.p_string = BasicNodeType::template create_object(s); +private: + static void destroy(BasicNodeType& n) { + n.m_node_value.destroy(n.m_attrs & node_attr_mask::value); + n.m_attrs &= ~node_attr_mask::value; } }; @@ -209,7 +102,66 @@ template < std::is_same>>::value, int> = 0> inline void to_node(BasicNodeType& n, T&& s) noexcept { - external_node_constructor::construct(n, std::forward(s)); + external_node_constructor::sequence(n, std::forward(s)); +} + +/// @brief to_node function for compatible sequence types. +/// @note This overload is enabled when +/// * both begin()/end() functions are callable on a `CompatSeqType` object +/// * CompatSeqType doesn't have `mapped_type` (mapping-like type) +/// * BasicNodeType::string_type cannot be constructed from a CompatSeqType object (string-like type) +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam CompatSeqType A container type. +/// @param n A basic_node object. +/// @param s A container object. +template < + typename BasicNodeType, typename CompatSeqType, + enable_if_t< + conjunction< + is_basic_node, + negation>>, + negation>>, detect::has_begin_end, + negation, detect::has_mapped_type>>, + negation>>::value, + int> = 0> +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +inline void to_node(BasicNodeType& n, CompatSeqType&& s) { + using std::begin; + using std::end; + external_node_constructor::sequence(n, begin(s), end(s)); +} + +/// @brief to_node function for std::pair objects. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam T The first type of std::pair. +/// @tparam U The second type of std::pair. +/// @param n A basic_node object. +/// @param p A std::pair object. +template +inline void to_node(BasicNodeType& n, const std::pair& p) { + n = {p.first, p.second}; +} + +/// @brief concrete implementation of to_node function for std::tuple objects. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam ...Types The value types of std::tuple. +/// @tparam ...Idx Index sequence values for std::tuple value types. +/// @param n A basic_node object. +/// @param t A std::tuple object. +/// @param _ Index sequence values (unused) +template +inline void to_node_tuple_impl(BasicNodeType& n, const std::tuple& t, index_sequence /*unused*/) { + n = {std::get(t)...}; +} + +/// @brief to_node function for std::tuple objects. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam ...Types The value types of std::tuple. +/// @param n A basic_node object. +/// @param t A std::tuple object. +template +inline void to_node(BasicNodeType& n, const std::tuple& t) { + to_node_tuple_impl(n, t, index_sequence_for {}); } /// @brief to_node function for BasicNodeType::mapping_type objects. @@ -224,17 +176,40 @@ template < is_basic_node, std::is_same>>::value, int> = 0> inline void to_node(BasicNodeType& n, T&& m) noexcept { - external_node_constructor::construct(n, std::forward(m)); + external_node_constructor::mapping(n, std::forward(m)); +} + +/// @brief to_node function for compatible mapping types. +/// @note This overload is enabled when +/// * both begin()/end() functions are callable on a `CompatMapType` object +/// * CompatMapType has both `key_type` and `mapped_type` +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam CompatMapType A container type. +/// @param n A basic_node object. +/// @param m A container object. +template < + typename BasicNodeType, typename CompatMapType, + enable_if_t< + conjunction< + is_basic_node, negation>>, + negation>>, + detect::has_begin_end, detect::has_key_type, + detect::has_mapped_type>::value, + int> = 0> +inline void to_node(BasicNodeType& n, CompatMapType&& m) { + external_node_constructor::mapping(n); + auto& map = n.template get_value_ref(); + for (const auto& pair : std::forward(m)) { + map.emplace(pair.first, pair.second); + } } /// @brief to_node function for null objects. /// @tparam BasicNodeType A mapping node value type. /// @tparam NullType This must be std::nullptr_t type -template < - typename BasicNodeType, typename NullType, - enable_if_t, std::is_same>::value, int> = 0> -inline void to_node(BasicNodeType& n, NullType /*unused*/) { - external_node_constructor::construct(n, nullptr); +template ::value, int> = 0> +inline void to_node(BasicNodeType& n, std::nullptr_t /*unused*/) { + external_node_constructor::null_scalar(n, nullptr); } /// @brief to_node function for BasicNodeType::boolean_type objects. @@ -242,13 +217,9 @@ inline void to_node(BasicNodeType& n, NullType /*unused*/) { /// @tparam T A boolean scalar node value type. /// @param n A basic_node object. /// @param b A boolean scalar node value object. -template < - typename BasicNodeType, typename T, - enable_if_t< - conjunction, std::is_same>::value, int> = - 0> -inline void to_node(BasicNodeType& n, T b) noexcept { - external_node_constructor::construct(n, b); +template ::value, int> = 0> +inline void to_node(BasicNodeType& n, typename BasicNodeType::boolean_type b) noexcept { + external_node_constructor::boolean_scalar(n, b); } /// @brief to_node function for integers. @@ -260,7 +231,7 @@ template < typename BasicNodeType, typename T, enable_if_t, is_non_bool_integral>::value, int> = 0> inline void to_node(BasicNodeType& n, T i) noexcept { - external_node_constructor::construct(n, i); + external_node_constructor::integer_scalar(n, i); } /// @brief to_node function for floating point numbers. @@ -272,7 +243,7 @@ template < typename BasicNodeType, typename T, enable_if_t, std::is_floating_point>::value, int> = 0> inline void to_node(BasicNodeType& n, T f) noexcept { - external_node_constructor::construct(n, f); + external_node_constructor::float_scalar(n, f); } /// @brief to_node function for compatible strings. @@ -285,19 +256,10 @@ template < enable_if_t< conjunction< is_basic_node, negation>, - std::is_constructible>::value, + std::is_constructible>::value, int> = 0> -inline void to_node(BasicNodeType& n, const T& s) { - external_node_constructor::construct(n, s); -} - -/// @brief to_node function for rvalue string node values -/// @tparam BasicNodeType A basic_node template instance type -/// @param n A basic_node object. -/// @param s An rvalue string node value. -template ::value, int> = 0> -inline void to_node(BasicNodeType& n, typename BasicNodeType::string_type&& s) noexcept { - external_node_constructor::construct(n, std::move(s)); +inline void to_node(BasicNodeType& n, T&& s) { + external_node_constructor::string_scalar(n, std::forward(s)); } /// @brief A function object to call to_node functions. diff --git a/include/fkYAML/detail/exception_safe_allocation.hpp b/include/fkYAML/detail/exception_safe_allocation.hpp new file mode 100644 index 00000000..2779800c --- /dev/null +++ b/include/fkYAML/detail/exception_safe_allocation.hpp @@ -0,0 +1,108 @@ +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +// | __| _ < \_ _/| ___ | _ | |___ version 0.4.1 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#ifndef FK_YAML_DETAIL_EXCEPTION_SAFE_ALLOCATION_HPP +#define FK_YAML_DETAIL_EXCEPTION_SAFE_ALLOCATION_HPP + +#include +#include + +#include +#include + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief Helper struct which ensures destruction/deallocation of heap-allocated objects. +/// @tparam ObjT Object type. +/// @tparam AllocTraits Allocator traits type for the object. +template +struct tidy_guard { + tidy_guard() = delete; + + /// @brief Construct a tidy_guard with a pointer to the object. + /// @param p_obj + tidy_guard(ObjT* p_obj) noexcept + : p_obj(p_obj) { + } + + // move-only + tidy_guard(const tidy_guard&) = delete; + tidy_guard& operator=(const tidy_guard&) = delete; + + /// @brief Move constructs a tidy_guard object. + tidy_guard(tidy_guard&&) = default; + + /// @brief Move assigns a tidy_guard object. + /// @return Reference to this tidy_guard object. + tidy_guard& operator=(tidy_guard&&) = default; + + /// @brief Destroys this tidy_guard object. Destruction/deallocation happen if the pointer is not null. + ~tidy_guard() { + if FK_YAML_UNLIKELY (p_obj != nullptr) { + typename AllocTraits::allocator_type alloc {}; + AllocTraits::destroy(alloc, p_obj); + AllocTraits::deallocate(alloc, p_obj, 1); + p_obj = nullptr; + } + } + + /// @brief Get the pointer to the object. + /// @return The pointer to the object. + ObjT* get() const noexcept { + return p_obj; + } + + /// @brief Checks if the pointer is not null. + explicit operator bool() const noexcept { + return p_obj != nullptr; + } + + /// @brief Releases the pointer to the object. No destruction/deallocation happen after this function gets called. + /// @return The pointer to the object. + ObjT* release() noexcept { + ObjT* ret = p_obj; + p_obj = nullptr; + return ret; + } + + /// @brief The pointer to the object. + ObjT* p_obj {nullptr}; +}; + +/// @brief Allocates and constructs an `ObjT` object with given arguments. +/// @tparam ObjT The object type. +/// @tparam ...Args The argument types. +/// @param ...args The arguments for construction. +/// @return An address of allocated memory on the heap. +template +inline ObjT* create_object(Args&&... args) { + using alloc_type = std::allocator; + using alloc_traits_type = std::allocator_traits; + + alloc_type alloc {}; + tidy_guard tg {alloc_traits_type::allocate(alloc, 1)}; + alloc_traits_type::construct(alloc, tg.get(), std::forward(args)...); + + FK_YAML_ASSERT(tg); + return tg.release(); +} + +/// @brief Destroys and deallocates an `ObjT` object. +/// @tparam ObjT The object type. +/// @param p_obj A pointer to the object. +template +inline void destroy_object(ObjT* p_obj) { + FK_YAML_ASSERT(p_obj != nullptr); + std::allocator alloc; + std::allocator_traits::destroy(alloc, p_obj); + std::allocator_traits::deallocate(alloc, p_obj, 1); +} + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_EXCEPTION_SAFE_ALLOCATION_HPP */ diff --git a/include/fkYAML/detail/meta/detect.hpp b/include/fkYAML/detail/meta/detect.hpp index efb24445..32b98613 100644 --- a/include/fkYAML/detail/meta/detect.hpp +++ b/include/fkYAML/detail/meta/detect.hpp @@ -128,44 +128,40 @@ using emplace_fn_t = decltype(std::declval().emplace(std::declval()...) /// @brief The type which represents reserve member function. /// @tparam T A target type. template -using reserve_fn_t = decltype(std::declval().reserve(std::declval())); +using reserve_fn_t = decltype(std::declval().reserve(std::declval::size_type>())); /// @brief Type traits to check if T has `iterator` member type. /// @tparam T A target type. template -using has_iterator = is_detected; +using has_iterator = is_detected>; /// @brief Type traits to check if T has `key_type` member type. /// @tparam T A target type. template -using has_key_type = is_detected; +using has_key_type = is_detected>; /// @brief Type traits to check if T has `mapped_type` member type. /// @tparam T A target type. template -using has_mapped_type = is_detected; +using has_mapped_type = is_detected>; /// @brief Type traits to check if T has `value_type` member type. /// @tparam T A target type. template -using has_value_type = is_detected; +using has_value_type = is_detected>; /// @brief Type traits to check if T is a std::iterator_traits like type. /// @tparam T A target type. template struct is_iterator_traits : conjunction< - is_detected, has_value_type, is_detected, - is_detected, is_detected> {}; + is_detected>, has_value_type>, + is_detected>, is_detected>, + is_detected>> {}; /// @brief Type traits to check if T has `container_type` member type. /// @tparam T A target type. template -using has_container_type = is_detected; - -/// @brief Type traits to check if T has emplace member function. -/// @tparam T A target type. -template -using has_emplace = is_detected; +using has_container_type = is_detected>; /// @brief Type traits to check if T has reserve member function. /// @tparam T A target type. diff --git a/include/fkYAML/node.hpp b/include/fkYAML/node.hpp index 2ab04823..a393b7f0 100644 --- a/include/fkYAML/node.hpp +++ b/include/fkYAML/node.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -144,7 +145,7 @@ class basic_node { using const_map_range = fkyaml::detail::map_range_proxy; private: - template + template friend struct fkyaml::detail::external_node_constructor; template @@ -174,10 +175,10 @@ class basic_node { explicit node_value(detail::node_attr_t value_type_bit) { switch (value_type_bit) { case detail::node_attr_bits::seq_bit: - p_sequence = create_object(); + p_sequence = detail::create_object(); break; case detail::node_attr_bits::map_bit: - p_mapping = create_object(); + p_mapping = detail::create_object(); break; case detail::node_attr_bits::null_bit: p_mapping = nullptr; @@ -192,7 +193,7 @@ class basic_node { float_val = static_cast(0.0); break; case detail::node_attr_bits::string_bit: - p_string = create_object(); + p_string = detail::create_object(); break; default: // LCOV_EXCL_LINE detail::unreachable(); // LCOV_EXCL_LINE @@ -206,16 +207,16 @@ class basic_node { switch (value_type_bit) { case detail::node_attr_bits::seq_bit: p_sequence->clear(); - destroy_object(p_sequence); + detail::destroy_object(p_sequence); p_sequence = nullptr; break; case detail::node_attr_bits::map_bit: p_mapping->clear(); - destroy_object(p_mapping); + detail::destroy_object(p_mapping); p_mapping = nullptr; break; case detail::node_attr_bits::string_bit: - destroy_object(p_string); + detail::destroy_object(p_string); p_string = nullptr; break; default: @@ -237,42 +238,6 @@ class basic_node { string_type* p_string; }; -private: - /// @brief Allocates and constructs an object with a given type and arguments. - /// @tparam ObjType The target object type. - /// @tparam ArgTypes The packed argument types for constructor arguments. - /// @param[in] args A parameter pack for constructor arguments of the target object type. - /// @return ObjType* An address of allocated memory on the heap. - template - static ObjType* create_object(ArgTypes&&... args) { - using AllocType = std::allocator; - using AllocTraitsType = std::allocator_traits; - - AllocType alloc {}; - auto deleter = [&alloc](ObjType* obj) { - AllocTraitsType::destroy(alloc, obj); - AllocTraitsType::deallocate(alloc, obj, 1); - }; - - std::unique_ptr object(AllocTraitsType::allocate(alloc, 1), deleter); - AllocTraitsType::construct(alloc, object.get(), std::forward(args)...); - - FK_YAML_ASSERT(object != nullptr); - return object.release(); - } - - /// @brief Destroys and deallocates an object with specified type. - /// @warning Make sure the `obj` parameter is not nullptr before calling this function. - /// @tparam ObjType The target object type. - /// @param[in] obj A pointer to the target object to be destroyed. - template - static void destroy_object(ObjType* obj) { - FK_YAML_ASSERT(obj != nullptr); - std::allocator alloc; - std::allocator_traits::destroy(alloc, obj); - std::allocator_traits::deallocate(alloc, obj, 1); - } - public: /// @brief Constructs a new basic_node object of null type. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/ @@ -301,10 +266,10 @@ class basic_node { if FK_YAML_LIKELY (!has_anchor_name()) { switch (m_attrs & detail::node_attr_mask::value) { case detail::node_attr_bits::seq_bit: - m_node_value.p_sequence = create_object(*(rhs.m_node_value.p_sequence)); + m_node_value.p_sequence = detail::create_object(*(rhs.m_node_value.p_sequence)); break; case detail::node_attr_bits::map_bit: - m_node_value.p_mapping = create_object(*(rhs.m_node_value.p_mapping)); + m_node_value.p_mapping = detail::create_object(*(rhs.m_node_value.p_mapping)); break; case detail::node_attr_bits::null_bit: m_node_value.p_mapping = nullptr; @@ -319,7 +284,7 @@ class basic_node { m_node_value.float_val = rhs.m_node_value.float_val; break; case detail::node_attr_bits::string_bit: - m_node_value.p_string = create_object(*(rhs.m_node_value.p_string)); + m_node_value.p_string = detail::create_object(*(rhs.m_node_value.p_string)); break; default: // LCOV_EXCL_LINE detail::unreachable(); // LCOV_EXCL_LINE @@ -415,7 +380,7 @@ class basic_node { if (is_mapping) { m_attrs = detail::node_attr_bits::map_bit; - m_node_value.p_mapping = create_object(); + m_node_value.p_mapping = detail::create_object(); for (auto& elem_ref : init) { auto elem = elem_ref.release(); @@ -425,7 +390,7 @@ class basic_node { } else { m_attrs = detail::node_attr_bits::seq_bit; - m_node_value.p_sequence = create_object(); + m_node_value.p_sequence = detail::create_object(); m_node_value.p_sequence->reserve(std::distance(init.begin(), init.end())); for (auto& elem_ref : init) { m_node_value.p_sequence->emplace_back(std::move(elem_ref.release())); @@ -521,7 +486,7 @@ class basic_node { static basic_node sequence() { basic_node node; node.m_attrs = detail::node_attr_bits::seq_bit; - node.m_node_value.p_sequence = create_object(); + node.m_node_value.p_sequence = detail::create_object(); return node; } // LCOV_EXCL_LINE @@ -532,7 +497,7 @@ class basic_node { static basic_node sequence(const sequence_type& seq) { basic_node node; node.m_attrs = detail::node_attr_bits::seq_bit; - node.m_node_value.p_sequence = create_object(seq); + node.m_node_value.p_sequence = detail::create_object(seq); return node; } // LCOV_EXCL_LINE @@ -543,7 +508,7 @@ class basic_node { static basic_node sequence(sequence_type&& seq) { basic_node node; node.m_attrs = detail::node_attr_bits::seq_bit; - node.m_node_value.p_sequence = create_object(std::move(seq)); + node.m_node_value.p_sequence = detail::create_object(std::move(seq)); return node; } // LCOV_EXCL_LINE @@ -553,7 +518,7 @@ class basic_node { static basic_node mapping() { basic_node node; node.m_attrs = detail::node_attr_bits::map_bit; - node.m_node_value.p_mapping = create_object(); + node.m_node_value.p_mapping = detail::create_object(); return node; } // LCOV_EXCL_LINE @@ -564,7 +529,7 @@ class basic_node { static basic_node mapping(const mapping_type& map) { basic_node node; node.m_attrs = detail::node_attr_bits::map_bit; - node.m_node_value.p_mapping = create_object(map); + node.m_node_value.p_mapping = detail::create_object(map); return node; } // LCOV_EXCL_LINE @@ -575,7 +540,7 @@ class basic_node { static basic_node mapping(mapping_type&& map) { basic_node node; node.m_attrs = detail::node_attr_bits::map_bit; - node.m_node_value.p_mapping = create_object(std::move(map)); + node.m_node_value.p_mapping = detail::create_object(std::move(map)); return node; } // LCOV_EXCL_LINE diff --git a/single_include/fkYAML/node.hpp b/single_include/fkYAML/node.hpp index fae92847..9ecbbb96 100644 --- a/single_include/fkYAML/node.hpp +++ b/single_include/fkYAML/node.hpp @@ -698,44 +698,40 @@ using emplace_fn_t = decltype(std::declval().emplace(std::declval()...) /// @brief The type which represents reserve member function. /// @tparam T A target type. template -using reserve_fn_t = decltype(std::declval().reserve(std::declval())); +using reserve_fn_t = decltype(std::declval().reserve(std::declval::size_type>())); /// @brief Type traits to check if T has `iterator` member type. /// @tparam T A target type. template -using has_iterator = is_detected; +using has_iterator = is_detected>; /// @brief Type traits to check if T has `key_type` member type. /// @tparam T A target type. template -using has_key_type = is_detected; +using has_key_type = is_detected>; /// @brief Type traits to check if T has `mapped_type` member type. /// @tparam T A target type. template -using has_mapped_type = is_detected; +using has_mapped_type = is_detected>; /// @brief Type traits to check if T has `value_type` member type. /// @tparam T A target type. template -using has_value_type = is_detected; +using has_value_type = is_detected>; /// @brief Type traits to check if T is a std::iterator_traits like type. /// @tparam T A target type. template struct is_iterator_traits : conjunction< - is_detected, has_value_type, is_detected, - is_detected, is_detected> {}; + is_detected>, has_value_type>, + is_detected>, is_detected>, + is_detected>> {}; /// @brief Type traits to check if T has `container_type` member type. /// @tparam T A target type. template -using has_container_type = is_detected; - -/// @brief Type traits to check if T has emplace member function. -/// @tparam T A target type. -template -using has_emplace = is_detected; +using has_container_type = is_detected>; /// @brief Type traits to check if T has reserve member function. /// @tparam T A target type. @@ -1189,6 +1185,118 @@ FK_YAML_DETAIL_NAMESPACE_END #endif /* FK_YAML_DETAIL_DOCUMENT_METAINFO_HPP */ +// #include +// _______ __ __ __ _____ __ __ __ +// | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library +// | __| _ < \_ _/| ___ | _ | |___ version 0.4.1 +// |__| |_| \__| |_| |_| |_|___||___|______| https://github.com/fktn-k/fkYAML +// +// SPDX-FileCopyrightText: 2023-2025 Kensuke Fukutani +// SPDX-License-Identifier: MIT + +#ifndef FK_YAML_DETAIL_EXCEPTION_SAFE_ALLOCATION_HPP +#define FK_YAML_DETAIL_EXCEPTION_SAFE_ALLOCATION_HPP + +#include +#include + +// #include + +// #include + + +FK_YAML_DETAIL_NAMESPACE_BEGIN + +/// @brief Helper struct which ensures destruction/deallocation of heap-allocated objects. +/// @tparam ObjT Object type. +/// @tparam AllocTraits Allocator traits type for the object. +template +struct tidy_guard { + tidy_guard() = delete; + + /// @brief Construct a tidy_guard with a pointer to the object. + /// @param p_obj + tidy_guard(ObjT* p_obj) noexcept + : p_obj(p_obj) { + } + + // move-only + tidy_guard(const tidy_guard&) = delete; + tidy_guard& operator=(const tidy_guard&) = delete; + + /// @brief Move constructs a tidy_guard object. + tidy_guard(tidy_guard&&) = default; + + /// @brief Move assigns a tidy_guard object. + /// @return Reference to this tidy_guard object. + tidy_guard& operator=(tidy_guard&&) = default; + + /// @brief Destroys this tidy_guard object. Destruction/deallocation happen if the pointer is not null. + ~tidy_guard() { + if FK_YAML_UNLIKELY (p_obj != nullptr) { + typename AllocTraits::allocator_type alloc {}; + AllocTraits::destroy(alloc, p_obj); + AllocTraits::deallocate(alloc, p_obj, 1); + p_obj = nullptr; + } + } + + /// @brief Get the pointer to the object. + /// @return The pointer to the object. + ObjT* get() const noexcept { + return p_obj; + } + + /// @brief Checks if the pointer is not null. + explicit operator bool() const noexcept { + return p_obj != nullptr; + } + + /// @brief Releases the pointer to the object. No destruction/deallocation happen after this function gets called. + /// @return The pointer to the object. + ObjT* release() noexcept { + ObjT* ret = p_obj; + p_obj = nullptr; + return ret; + } + + /// @brief The pointer to the object. + ObjT* p_obj {nullptr}; +}; + +/// @brief Allocates and constructs an `ObjT` object with given arguments. +/// @tparam ObjT The object type. +/// @tparam ...Args The argument types. +/// @param ...args The arguments for construction. +/// @return An address of allocated memory on the heap. +template +inline ObjT* create_object(Args&&... args) { + using alloc_type = std::allocator; + using alloc_traits_type = std::allocator_traits; + + alloc_type alloc {}; + tidy_guard tg {alloc_traits_type::allocate(alloc, 1)}; + alloc_traits_type::construct(alloc, tg.get(), std::forward(args)...); + + FK_YAML_ASSERT(tg); + return tg.release(); +} + +/// @brief Destroys and deallocates an `ObjT` object. +/// @tparam ObjT The object type. +/// @param p_obj A pointer to the object. +template +inline void destroy_object(ObjT* p_obj) { + FK_YAML_ASSERT(p_obj != nullptr); + std::allocator alloc; + std::allocator_traits::destroy(alloc, p_obj); + std::allocator_traits::deallocate(alloc, p_obj, 1); +} + +FK_YAML_DETAIL_NAMESPACE_END + +#endif /* FK_YAML_DETAIL_EXCEPTION_SAFE_ALLOCATION_HPP */ + // #include // _______ __ __ __ _____ __ __ __ // | __| |_/ | \_/ |/ _ \ / \/ \| | fkYAML: A C++ header-only YAML library @@ -12048,6 +12156,8 @@ FK_YAML_NAMESPACE_END // #include +// #include + // #include // #include @@ -12071,165 +12181,57 @@ FK_YAML_DETAIL_NAMESPACE_BEGIN /// @warning All the specialization must call n.m_node_value.destroy() first in the construct function to avoid /// memory leak. /// @tparam node_type The resulting YAML node value type. -template -struct external_node_constructor; - -/// @brief The specialization of external_node_constructor for sequence nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with const lvalue sequence. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param s A lvalue sequence value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, const typename BasicNodeType::sequence_type& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::seq_bit; - n.m_node_value.p_sequence = BasicNodeType::template create_object(s); - } - - /// @brief Constructs a basic_node object with rvalue sequence. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param s A rvalue sequence value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::sequence_type&& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::seq_bit; - n.m_node_value.p_sequence = - BasicNodeType::template create_object(std::move(s)); - } -}; - -/// @brief The specialization of external_node_constructor for mapping nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with const lvalue mapping. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param m A lvalue mapping value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, const typename BasicNodeType::mapping_type& m) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::map_bit; - n.m_node_value.p_mapping = BasicNodeType::template create_object(m); +template +struct external_node_constructor { + template + static void sequence(BasicNodeType& n, Args&&... args) { + destroy(n); + n.m_attrs |= node_attr_bits::seq_bit; + n.m_node_value.p_sequence = create_object(std::forward(args)...); } - /// @brief Constructs a basic_node object with rvalue mapping. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param m A rvalue mapping value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::mapping_type&& m) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::map_bit; - n.m_node_value.p_mapping = - BasicNodeType::template create_object(std::move(m)); + template + static void mapping(BasicNodeType& n, Args&&... args) { + destroy(n); + n.m_attrs |= node_attr_bits::map_bit; + n.m_node_value.p_mapping = create_object(std::forward(args)...); } -}; -/// @brief The specialization of external_node_constructor for null nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with nullptr. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param (unused) nullptr - template ::value, int> = 0> - static void construct(BasicNodeType& n, std::nullptr_t /*unused*/) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::null_bit; + static void null_scalar(BasicNodeType& n, std::nullptr_t) { + destroy(n); + n.m_attrs |= node_attr_bits::null_bit; n.m_node_value.p_mapping = nullptr; } -}; -/// @brief The specialization of external_node_constructor for boolean scalar nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with boolean. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param b A boolean value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::boolean_type b) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::bool_bit; + static void boolean_scalar(BasicNodeType& n, const typename BasicNodeType::boolean_type b) { + destroy(n); + n.m_attrs |= node_attr_bits::bool_bit; n.m_node_value.boolean = b; } -}; -/// @brief The specialization of external_node_constructor for integer scalar nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with integers. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param i An integer value. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::integer_type i) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::int_bit; + static void integer_scalar(BasicNodeType& n, const typename BasicNodeType::integer_type i) { + destroy(n); + n.m_attrs |= node_attr_bits::int_bit; n.m_node_value.integer = i; } -}; -/// @brief The specialization of external_node_constructor for float number scalar nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with floating point numbers. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param f A floating point number. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::float_number_type f) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::float_bit; + static void float_scalar(BasicNodeType& n, const typename BasicNodeType::float_number_type f) { + destroy(n); + n.m_attrs |= node_attr_bits::float_bit; n.m_node_value.float_val = f; } -}; -/// @brief The specialization of external_node_constructor for string scalar nodes. -template <> -struct external_node_constructor { - /// @brief Constructs a basic_node object with const lvalue strings. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param s A constant lvalue string. - template ::value, int> = 0> - static void construct(BasicNodeType& n, const typename BasicNodeType::string_type& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::string_bit; - n.m_node_value.p_string = BasicNodeType::template create_object(s); + template + static void string_scalar(BasicNodeType& n, Args&&... args) { + destroy(n); + n.m_attrs |= node_attr_bits::string_bit; + n.m_node_value.p_string = create_object(std::forward(args)...); } - /// @brief Constructs a basic_node object with rvalue strings. - /// @tparam BasicNodeType A basic_node template instance type. - /// @param n A basic_node object. - /// @param s A rvalue string. - template ::value, int> = 0> - static void construct(BasicNodeType& n, typename BasicNodeType::string_type&& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::string_bit; - n.m_node_value.p_string = - BasicNodeType::template create_object(std::move(s)); - } - - /// @brief Constructs a basic_node object with compatible strings. - /// @tparam BasicNodeType A basic_node template instance type. - /// @tparam CompatibleStringType A compatible string type. - /// @param n A basic_node object. - /// @param s A compatible string. - template < - typename BasicNodeType, typename CompatibleStringType, - enable_if_t< - conjunction< - is_basic_node, - negation>>::value, - int> = 0> - static void construct(BasicNodeType& n, const CompatibleStringType& s) noexcept { - n.m_node_value.destroy(n.m_attrs & detail::node_attr_mask::value); - n.m_attrs = detail::node_attr_bits::string_bit; - n.m_node_value.p_string = BasicNodeType::template create_object(s); +private: + static void destroy(BasicNodeType& n) { + n.m_node_value.destroy(n.m_attrs & node_attr_mask::value); + n.m_attrs &= ~node_attr_mask::value; } }; @@ -12250,7 +12252,66 @@ template < std::is_same>>::value, int> = 0> inline void to_node(BasicNodeType& n, T&& s) noexcept { - external_node_constructor::construct(n, std::forward(s)); + external_node_constructor::sequence(n, std::forward(s)); +} + +/// @brief to_node function for compatible sequence types. +/// @note This overload is enabled when +/// * both begin()/end() functions are callable on a `CompatSeqType` object +/// * CompatSeqType doesn't have `mapped_type` (mapping-like type) +/// * BasicNodeType::string_type cannot be constructed from a CompatSeqType object (string-like type) +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam CompatSeqType A container type. +/// @param n A basic_node object. +/// @param s A container object. +template < + typename BasicNodeType, typename CompatSeqType, + enable_if_t< + conjunction< + is_basic_node, + negation>>, + negation>>, detect::has_begin_end, + negation, detect::has_mapped_type>>, + negation>>::value, + int> = 0> +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +inline void to_node(BasicNodeType& n, CompatSeqType&& s) { + using std::begin; + using std::end; + external_node_constructor::sequence(n, begin(s), end(s)); +} + +/// @brief to_node function for std::pair objects. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam T The first type of std::pair. +/// @tparam U The second type of std::pair. +/// @param n A basic_node object. +/// @param p A std::pair object. +template +inline void to_node(BasicNodeType& n, const std::pair& p) { + n = {p.first, p.second}; +} + +/// @brief concrete implementation of to_node function for std::tuple objects. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam ...Types The value types of std::tuple. +/// @tparam ...Idx Index sequence values for std::tuple value types. +/// @param n A basic_node object. +/// @param t A std::tuple object. +/// @param _ Index sequence values (unused) +template +inline void to_node_tuple_impl(BasicNodeType& n, const std::tuple& t, index_sequence /*unused*/) { + n = {std::get(t)...}; +} + +/// @brief to_node function for std::tuple objects. +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam ...Types The value types of std::tuple. +/// @param n A basic_node object. +/// @param t A std::tuple object. +template +inline void to_node(BasicNodeType& n, const std::tuple& t) { + to_node_tuple_impl(n, t, index_sequence_for {}); } /// @brief to_node function for BasicNodeType::mapping_type objects. @@ -12265,17 +12326,40 @@ template < is_basic_node, std::is_same>>::value, int> = 0> inline void to_node(BasicNodeType& n, T&& m) noexcept { - external_node_constructor::construct(n, std::forward(m)); + external_node_constructor::mapping(n, std::forward(m)); +} + +/// @brief to_node function for compatible mapping types. +/// @note This overload is enabled when +/// * both begin()/end() functions are callable on a `CompatMapType` object +/// * CompatMapType has both `key_type` and `mapped_type` +/// @tparam BasicNodeType A basic_node template instance type. +/// @tparam CompatMapType A container type. +/// @param n A basic_node object. +/// @param m A container object. +template < + typename BasicNodeType, typename CompatMapType, + enable_if_t< + conjunction< + is_basic_node, negation>>, + negation>>, + detect::has_begin_end, detect::has_key_type, + detect::has_mapped_type>::value, + int> = 0> +inline void to_node(BasicNodeType& n, CompatMapType&& m) { + external_node_constructor::mapping(n); + auto& map = n.template get_value_ref(); + for (const auto& pair : std::forward(m)) { + map.emplace(pair.first, pair.second); + } } /// @brief to_node function for null objects. /// @tparam BasicNodeType A mapping node value type. /// @tparam NullType This must be std::nullptr_t type -template < - typename BasicNodeType, typename NullType, - enable_if_t, std::is_same>::value, int> = 0> -inline void to_node(BasicNodeType& n, NullType /*unused*/) { - external_node_constructor::construct(n, nullptr); +template ::value, int> = 0> +inline void to_node(BasicNodeType& n, std::nullptr_t /*unused*/) { + external_node_constructor::null_scalar(n, nullptr); } /// @brief to_node function for BasicNodeType::boolean_type objects. @@ -12283,13 +12367,9 @@ inline void to_node(BasicNodeType& n, NullType /*unused*/) { /// @tparam T A boolean scalar node value type. /// @param n A basic_node object. /// @param b A boolean scalar node value object. -template < - typename BasicNodeType, typename T, - enable_if_t< - conjunction, std::is_same>::value, int> = - 0> -inline void to_node(BasicNodeType& n, T b) noexcept { - external_node_constructor::construct(n, b); +template ::value, int> = 0> +inline void to_node(BasicNodeType& n, typename BasicNodeType::boolean_type b) noexcept { + external_node_constructor::boolean_scalar(n, b); } /// @brief to_node function for integers. @@ -12301,7 +12381,7 @@ template < typename BasicNodeType, typename T, enable_if_t, is_non_bool_integral>::value, int> = 0> inline void to_node(BasicNodeType& n, T i) noexcept { - external_node_constructor::construct(n, i); + external_node_constructor::integer_scalar(n, i); } /// @brief to_node function for floating point numbers. @@ -12313,7 +12393,7 @@ template < typename BasicNodeType, typename T, enable_if_t, std::is_floating_point>::value, int> = 0> inline void to_node(BasicNodeType& n, T f) noexcept { - external_node_constructor::construct(n, f); + external_node_constructor::float_scalar(n, f); } /// @brief to_node function for compatible strings. @@ -12326,19 +12406,10 @@ template < enable_if_t< conjunction< is_basic_node, negation>, - std::is_constructible>::value, + std::is_constructible>::value, int> = 0> -inline void to_node(BasicNodeType& n, const T& s) { - external_node_constructor::construct(n, s); -} - -/// @brief to_node function for rvalue string node values -/// @tparam BasicNodeType A basic_node template instance type -/// @param n A basic_node object. -/// @param s An rvalue string node value. -template ::value, int> = 0> -inline void to_node(BasicNodeType& n, typename BasicNodeType::string_type&& s) noexcept { - external_node_constructor::construct(n, std::move(s)); +inline void to_node(BasicNodeType& n, T&& s) { + external_node_constructor::string_scalar(n, std::forward(s)); } /// @brief A function object to call to_node functions. @@ -12717,7 +12788,7 @@ class basic_node { using const_map_range = fkyaml::detail::map_range_proxy; private: - template + template friend struct fkyaml::detail::external_node_constructor; template @@ -12747,10 +12818,10 @@ class basic_node { explicit node_value(detail::node_attr_t value_type_bit) { switch (value_type_bit) { case detail::node_attr_bits::seq_bit: - p_sequence = create_object(); + p_sequence = detail::create_object(); break; case detail::node_attr_bits::map_bit: - p_mapping = create_object(); + p_mapping = detail::create_object(); break; case detail::node_attr_bits::null_bit: p_mapping = nullptr; @@ -12765,7 +12836,7 @@ class basic_node { float_val = static_cast(0.0); break; case detail::node_attr_bits::string_bit: - p_string = create_object(); + p_string = detail::create_object(); break; default: // LCOV_EXCL_LINE detail::unreachable(); // LCOV_EXCL_LINE @@ -12779,16 +12850,16 @@ class basic_node { switch (value_type_bit) { case detail::node_attr_bits::seq_bit: p_sequence->clear(); - destroy_object(p_sequence); + detail::destroy_object(p_sequence); p_sequence = nullptr; break; case detail::node_attr_bits::map_bit: p_mapping->clear(); - destroy_object(p_mapping); + detail::destroy_object(p_mapping); p_mapping = nullptr; break; case detail::node_attr_bits::string_bit: - destroy_object(p_string); + detail::destroy_object(p_string); p_string = nullptr; break; default: @@ -12810,42 +12881,6 @@ class basic_node { string_type* p_string; }; -private: - /// @brief Allocates and constructs an object with a given type and arguments. - /// @tparam ObjType The target object type. - /// @tparam ArgTypes The packed argument types for constructor arguments. - /// @param[in] args A parameter pack for constructor arguments of the target object type. - /// @return ObjType* An address of allocated memory on the heap. - template - static ObjType* create_object(ArgTypes&&... args) { - using AllocType = std::allocator; - using AllocTraitsType = std::allocator_traits; - - AllocType alloc {}; - auto deleter = [&alloc](ObjType* obj) { - AllocTraitsType::destroy(alloc, obj); - AllocTraitsType::deallocate(alloc, obj, 1); - }; - - std::unique_ptr object(AllocTraitsType::allocate(alloc, 1), deleter); - AllocTraitsType::construct(alloc, object.get(), std::forward(args)...); - - FK_YAML_ASSERT(object != nullptr); - return object.release(); - } - - /// @brief Destroys and deallocates an object with specified type. - /// @warning Make sure the `obj` parameter is not nullptr before calling this function. - /// @tparam ObjType The target object type. - /// @param[in] obj A pointer to the target object to be destroyed. - template - static void destroy_object(ObjType* obj) { - FK_YAML_ASSERT(obj != nullptr); - std::allocator alloc; - std::allocator_traits::destroy(alloc, obj); - std::allocator_traits::deallocate(alloc, obj, 1); - } - public: /// @brief Constructs a new basic_node object of null type. /// @sa https://fktn-k.github.io/fkYAML/api/basic_node/constructor/ @@ -12874,10 +12909,10 @@ class basic_node { if FK_YAML_LIKELY (!has_anchor_name()) { switch (m_attrs & detail::node_attr_mask::value) { case detail::node_attr_bits::seq_bit: - m_node_value.p_sequence = create_object(*(rhs.m_node_value.p_sequence)); + m_node_value.p_sequence = detail::create_object(*(rhs.m_node_value.p_sequence)); break; case detail::node_attr_bits::map_bit: - m_node_value.p_mapping = create_object(*(rhs.m_node_value.p_mapping)); + m_node_value.p_mapping = detail::create_object(*(rhs.m_node_value.p_mapping)); break; case detail::node_attr_bits::null_bit: m_node_value.p_mapping = nullptr; @@ -12892,7 +12927,7 @@ class basic_node { m_node_value.float_val = rhs.m_node_value.float_val; break; case detail::node_attr_bits::string_bit: - m_node_value.p_string = create_object(*(rhs.m_node_value.p_string)); + m_node_value.p_string = detail::create_object(*(rhs.m_node_value.p_string)); break; default: // LCOV_EXCL_LINE detail::unreachable(); // LCOV_EXCL_LINE @@ -12988,7 +13023,7 @@ class basic_node { if (is_mapping) { m_attrs = detail::node_attr_bits::map_bit; - m_node_value.p_mapping = create_object(); + m_node_value.p_mapping = detail::create_object(); for (auto& elem_ref : init) { auto elem = elem_ref.release(); @@ -12998,7 +13033,7 @@ class basic_node { } else { m_attrs = detail::node_attr_bits::seq_bit; - m_node_value.p_sequence = create_object(); + m_node_value.p_sequence = detail::create_object(); m_node_value.p_sequence->reserve(std::distance(init.begin(), init.end())); for (auto& elem_ref : init) { m_node_value.p_sequence->emplace_back(std::move(elem_ref.release())); @@ -13094,7 +13129,7 @@ class basic_node { static basic_node sequence() { basic_node node; node.m_attrs = detail::node_attr_bits::seq_bit; - node.m_node_value.p_sequence = create_object(); + node.m_node_value.p_sequence = detail::create_object(); return node; } // LCOV_EXCL_LINE @@ -13105,7 +13140,7 @@ class basic_node { static basic_node sequence(const sequence_type& seq) { basic_node node; node.m_attrs = detail::node_attr_bits::seq_bit; - node.m_node_value.p_sequence = create_object(seq); + node.m_node_value.p_sequence = detail::create_object(seq); return node; } // LCOV_EXCL_LINE @@ -13116,7 +13151,7 @@ class basic_node { static basic_node sequence(sequence_type&& seq) { basic_node node; node.m_attrs = detail::node_attr_bits::seq_bit; - node.m_node_value.p_sequence = create_object(std::move(seq)); + node.m_node_value.p_sequence = detail::create_object(std::move(seq)); return node; } // LCOV_EXCL_LINE @@ -13126,7 +13161,7 @@ class basic_node { static basic_node mapping() { basic_node node; node.m_attrs = detail::node_attr_bits::map_bit; - node.m_node_value.p_mapping = create_object(); + node.m_node_value.p_mapping = detail::create_object(); return node; } // LCOV_EXCL_LINE @@ -13137,7 +13172,7 @@ class basic_node { static basic_node mapping(const mapping_type& map) { basic_node node; node.m_attrs = detail::node_attr_bits::map_bit; - node.m_node_value.p_mapping = create_object(map); + node.m_node_value.p_mapping = detail::create_object(map); return node; } // LCOV_EXCL_LINE @@ -13148,7 +13183,7 @@ class basic_node { static basic_node mapping(mapping_type&& map) { basic_node node; node.m_attrs = detail::node_attr_bits::map_bit; - node.m_node_value.p_mapping = create_object(std::move(map)); + node.m_node_value.p_mapping = detail::create_object(std::move(map)); return node; } // LCOV_EXCL_LINE diff --git a/tests/unit_test/test_node_class.cpp b/tests/unit_test/test_node_class.cpp index d2ad8157..f39aaa6e 100644 --- a/tests/unit_test/test_node_class.cpp +++ b/tests/unit_test/test_node_class.cpp @@ -132,72 +132,473 @@ TEST_CASE("Node_ThrowingSpecializationTypeCtor") { REQUIRE_THROWS_AS(NodeType(fkyaml::node_type::STRING), fkyaml::exception); } -TEST_CASE("Node_SequenceCtor") { - fkyaml::node node(fkyaml::node::sequence_type {fkyaml::node(true), fkyaml::node(false)}); - REQUIRE(node.get_type() == fkyaml::node_type::SEQUENCE); - REQUIRE(node.is_sequence()); - REQUIRE(node.size() == 2); - REQUIRE(node[0].is_boolean()); - REQUIRE(node[0].get_value_ref() == true); - REQUIRE(node[1].is_boolean()); - REQUIRE(node[1].get_value_ref() == false); -} +TEST_CASE("Node_CtorWithCompatibleType") { + // sequence-like types + + SECTION("1D C-style array") { + fkyaml::node ints_node_val[2] {1, 2}; + int ints_val[2] {1, 2}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_integer()); + REQUIRE(n[0].get_value() == 1); + REQUIRE(n[1].is_integer()); + REQUIRE(n[1].get_value() == 2); + }; + + fkyaml::node ints_node(ints_node_val); + validate(ints_node); + + fkyaml::node ints(ints_val); + validate(ints); + } + + SECTION("2D C-style array") { + fkyaml::node ints_node_val[3][3] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + int ints_val[3][3] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; + + auto validate = [](const fkyaml::node& n, int i, int j) { + REQUIRE(n.at(i).at(j).is_integer()); + REQUIRE(n.at(i).at(j).get_value_ref() == i * 3 + j + 1); + }; + + fkyaml::node ints_node(ints_node_val); + fkyaml::node ints(ints_val); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + validate(ints_node, i, j); + validate(ints, i, j); + } + } + } -TEST_CASE("Node_MappingCtor") { - fkyaml::node node(fkyaml::node::mapping_type {{"test", fkyaml::node(true)}}); - REQUIRE(node.get_type() == fkyaml::node_type::MAPPING); - REQUIRE(node.is_mapping()); - REQUIRE(node.size() == 1); - REQUIRE(node.contains("test")); - REQUIRE(node["test"].is_boolean()); - REQUIRE(node["test"].get_value_ref() == true); -} + SECTION("3D C-style array") { + fkyaml::node ints_node_val[3][3][3] { + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, + {{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}, + {{19, 20, 21}, {22, 23, 24}, {25, 26, 27}}}; + int ints_val[3][3][3] { + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, + {{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}, + {{19, 20, 21}, {22, 23, 24}, {25, 26, 27}}}; + + auto validate = [](const fkyaml::node& n, int i, int j, int k) { + REQUIRE(n.at(i).at(j).at(k).is_integer()); + REQUIRE(n.at(i).at(j).at(k).get_value_ref() == i * 9 + j * 3 + k + 1); + }; + + fkyaml::node ints_node(ints_node_val); + fkyaml::node ints(ints_val); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + for (int k = 0; k < 3; k++) { + validate(ints_node, i, j, k); + validate(ints, i, j, k); + } + } + } + } -TEST_CASE("Node_NullCtor") { - fkyaml::node node(nullptr); - REQUIRE(node.get_type() == fkyaml::node_type::NULL_OBJECT); - REQUIRE(node.is_null()); -} + SECTION("std::vector") { + std::vector vector_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::vector vector_bool_val {true, false}; -TEST_CASE("Node_BooleanCtor") { - fkyaml::node node(true); - REQUIRE(node.get_type() == fkyaml::node_type::BOOLEAN); - REQUIRE(node.is_boolean()); - REQUIRE(node.get_value_ref() == true); -} + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + REQUIRE(n[0].get_value() == true); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == false); + }; -TEST_CASE("Node_IntegerCtor") { - fkyaml::node node(23467); - REQUIRE(node.get_type() == fkyaml::node_type::INTEGER); - REQUIRE(node.is_integer()); - REQUIRE(node.get_value_ref() == 23467); -} + fkyaml::node vector_node(vector_node_val); + validate(vector_node); -TEST_CASE("Node_FloatNumberCtor") { - fkyaml::node node(3.14); - REQUIRE(node.get_type() == fkyaml::node_type::FLOAT); - REQUIRE(node.is_float_number()); - REQUIRE(node.get_value_ref() == 3.14); -} + fkyaml::node vector_bool(vector_bool_val); + validate(vector_bool); + } -TEST_CASE("Node_StringCtor") { - auto node = GENERATE(fkyaml::node(std::string("test"))); - REQUIRE(node.get_type() == fkyaml::node_type::STRING); - REQUIRE(node.is_string()); - REQUIRE(node.size() == 4); - REQUIRE(node.get_value_ref() == "test"); -} + SECTION("std::array") { + std::array array_node_val {{fkyaml::node(true), fkyaml::node(false)}}; + std::array array_bool_val {{true, false}}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + REQUIRE(n[0].get_value() == true); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == false); + }; + + fkyaml::node array_node(array_node_val); + validate(array_node); + + fkyaml::node array_bool(array_bool_val); + validate(array_bool); + } + + SECTION("std::valarray") { + std::valarray valarray_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::valarray valarray_bool_val {true, false}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + REQUIRE(n[0].get_value() == true); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == false); + }; + + fkyaml::node valarray_node(valarray_node_val); + validate(valarray_node); + + fkyaml::node valarray_bool(valarray_bool_val); + validate(valarray_bool); + } + + SECTION("std::forward_list") { + std::forward_list forward_list_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::forward_list forward_list_bool_val {true, false}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + REQUIRE(n[0].get_value() == true); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == false); + }; + + fkyaml::node forward_list_node(forward_list_node_val); + validate(forward_list_node); + + fkyaml::node forward_list_bool(forward_list_bool_val); + validate(forward_list_bool); + } + + SECTION("std::deque") { + std::deque deque_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::deque deque_bool_val {true, false}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + REQUIRE(n[0].get_value() == true); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == false); + }; + + fkyaml::node deque_node(deque_node_val); + validate(deque_node); + + fkyaml::node deque_bool(deque_bool_val); + validate(deque_bool); + } + + SECTION("std::list") { + std::list list_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::list list_bool_val {true, false}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + REQUIRE(n[0].get_value() == true); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == false); + }; + + fkyaml::node list_node(list_node_val); + validate(list_node); + + fkyaml::node list_bool(list_bool_val); + validate(list_bool); + } + + SECTION("std::set") { + std::set set_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::set set_bool_val {true, false}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + REQUIRE(n[0].get_value() == false); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == true); + }; + + fkyaml::node set_node(set_node_val); + validate(set_node); + + fkyaml::node set_bool(set_bool_val); + validate(set_bool); + } + + SECTION("std::multiset") { + std::multiset multiset_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::multiset multiset_bool_val {true, false}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + REQUIRE(n[0].get_value() == false); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == true); + }; + + fkyaml::node multiset_node(multiset_node_val); + validate(multiset_node); + + fkyaml::node multiset_bool(multiset_bool_val); + validate(multiset_bool); + } + + SECTION("std::unordered_set") { + std::unordered_set unordered_set_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::unordered_set unordered_set_bool_val {true, false}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + bool ret = n[0].get_value(); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == !ret); + }; + + fkyaml::node unordered_set_node(unordered_set_node_val); + validate(unordered_set_node); + + fkyaml::node unordered_set_bool(unordered_set_bool_val); + validate(unordered_set_bool); + } + + SECTION("std::unordered_multiset") { + std::unordered_multiset unordered_multiset_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::unordered_multiset unordered_multiset_bool_val {true, false}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_boolean()); + bool ret = n[0].get_value(); + REQUIRE(n[1].is_boolean()); + REQUIRE(n[1].get_value() == !ret); + }; + + fkyaml::node unordered_multiset_node(unordered_multiset_node_val); + validate(unordered_multiset_node); + + fkyaml::node unordered_multiset_bool(unordered_multiset_bool_val); + validate(unordered_multiset_bool); + } + + SECTION("std::pair") { + std::pair pair_node_val = {"test", 123}; + std::pair pair_val = {"test", 123}; + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 2); + REQUIRE(n[0].is_string()); + REQUIRE(n[0].get_value_ref() == "test"); + REQUIRE(n[1].is_integer()); + REQUIRE(n[1].get_value_ref() == 123); + }; + + fkyaml::node pair_node(pair_node_val); + validate(pair_node); + + fkyaml::node pair(pair_val); + validate(pair); + } + + SECTION("std::tuple") { + std::tuple tuple_node_val("test", 123, true); + std::tuple tuple_val("test", 123, true); + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_sequence()); + REQUIRE(n.size() == 3); + REQUIRE(n[0].is_string()); + REQUIRE(n[0].get_value_ref() == "test"); + REQUIRE(n[1].is_integer()); + REQUIRE(n[1].get_value_ref() == 123); + REQUIRE(n[2].is_boolean()); + REQUIRE(n[2].get_value_ref() == true); + }; + + fkyaml::node tuple_node(tuple_node_val); + validate(tuple_node); + + fkyaml::node tuple(tuple_val); + validate(tuple); + } + + // mapping-like types + + SECTION("std::map") { + std::map map_node_val; + map_node_val.emplace("test", 123); + map_node_val.emplace("foo", -456); + std::map map_val; + map_val.emplace("test", 123); + map_val.emplace("foo", -456); + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_mapping()); + REQUIRE(n.size() == 2); + REQUIRE(n.at("test").is_integer()); + REQUIRE(n.at("test").get_value_ref() == 123); + REQUIRE(n.at("foo").is_integer()); + REQUIRE(n.at("foo").get_value_ref() == -456); + }; + + fkyaml::node map_node(map_node_val); + validate(map_node); + + fkyaml::node map(map_val); + validate(map); + } + + SECTION("std::multimap") { + std::multimap multimap_node_val; + multimap_node_val.emplace("test", 123); + multimap_node_val.emplace("foo", -456); + std::multimap multimap_val; + multimap_val.emplace("test", 123); + multimap_val.emplace("foo", -456); + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_mapping()); + REQUIRE(n.size() == 2); + REQUIRE(n.at("test").is_integer()); + REQUIRE(n.at("test").get_value_ref() == 123); + REQUIRE(n.at("foo").is_integer()); + REQUIRE(n.at("foo").get_value_ref() == -456); + }; + + fkyaml::node multimap_node(multimap_node_val); + validate(multimap_node); + + fkyaml::node multimap(multimap_val); + validate(multimap); + } + + SECTION("std::unordered_map") { + std::unordered_map unordered_map_node_val; + unordered_map_node_val.emplace("test", 123); + unordered_map_node_val.emplace("foo", -456); + std::unordered_map unordered_map_val; + unordered_map_val.emplace("test", 123); + unordered_map_val.emplace("foo", -456); + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_mapping()); + REQUIRE(n.size() == 2); + REQUIRE(n.at("test").is_integer()); + REQUIRE(n.at("test").get_value_ref() == 123); + REQUIRE(n.at("foo").is_integer()); + REQUIRE(n.at("foo").get_value_ref() == -456); + }; + + fkyaml::node unordered_map_node(unordered_map_node_val); + validate(unordered_map_node); + + fkyaml::node unordered_map(unordered_map_val); + validate(unordered_map); + } + + SECTION("std::unordered_multimap") { + std::unordered_multimap unordered_multimap_node_val; + unordered_multimap_node_val.emplace("test", 123); + unordered_multimap_node_val.emplace("foo", -456); + std::unordered_multimap unordered_multimap_val; + unordered_multimap_val.emplace("test", 123); + unordered_multimap_val.emplace("foo", -456); + + auto validate = [](const fkyaml::node& n) { + REQUIRE(n.is_mapping()); + REQUIRE(n.size() == 2); + REQUIRE(n.at("test").is_integer()); + REQUIRE(n.at("test").get_value_ref() == 123); + REQUIRE(n.at("foo").is_integer()); + REQUIRE(n.at("foo").get_value_ref() == -456); + }; + + fkyaml::node unordered_multimap_node(unordered_multimap_node_val); + validate(unordered_multimap_node); + + fkyaml::node unordered_multimap(unordered_multimap_val); + validate(unordered_multimap); + } + + SECTION("std::nullptr_t") { + fkyaml::node node(nullptr); + REQUIRE(node.is_null()); + } + + SECTION("boolean") { + fkyaml::node node(true); + REQUIRE(node.is_boolean()); + REQUIRE(node.get_value_ref() == true); + } + + SECTION("integer") { + fkyaml::node node(23467); + REQUIRE(node.is_integer()); + REQUIRE(node.get_value_ref() == 23467); + } + + SECTION("floating point") { + fkyaml::node node(3.14); + REQUIRE(node.is_float_number()); + REQUIRE(node.get_value_ref() == 3.14); + } + + // string-like types + + SECTION("std::string") { + fkyaml::node node(std::string("test")); + REQUIRE(node.is_string()); + REQUIRE(node.size() == 4); + REQUIRE(node.get_value_ref() == "test"); + } + + SECTION("C-style char array") { + fkyaml::node node("test"); + REQUIRE(node.is_string()); + REQUIRE(node.size() == 4); + REQUIRE(node.get_value_ref() == "test"); + } + + SECTION("pointer to a null-terminated char sequence") { + const char buff[] = "test"; + const char* p_buff = &buff[0]; + fkyaml::node node(p_buff); + REQUIRE(node.is_string()); + REQUIRE(node.size() == 4); + REQUIRE(node.get_value_ref() == "test"); + } #ifdef FK_YAML_HAS_CXX_17 -TEST_CASE("Node_StringViewCtor") { - using namespace std::string_view_literals; - auto node = fkyaml::node("test"sv); - REQUIRE(node.get_type() == fkyaml::node_type::STRING); - REQUIRE(node.is_string()); - REQUIRE(node.size() == 4); - REQUIRE(node.get_value_ref() == "test"); -} + SECTION("Node_StringViewCtor") { + using namespace std::string_view_literals; + auto node = fkyaml::node("test"sv); + REQUIRE(node.is_string()); + REQUIRE(node.size() == 4); + REQUIRE(node.get_value_ref() == "test"); + } #endif +} TEST_CASE("Node_SequenceCopyCtor") { fkyaml::node n = "test"; @@ -2668,7 +3069,7 @@ TEST_CASE("Node_GetValue_GetValueInplace") { REQUIRE(uset_bool_inplace.find(false) != uset_bool_inplace.end()); } - SECTION("sequence value (std::unordered_set)") { + SECTION("sequence value (std::unordered_multiset)") { auto umset_node = node.get_value>(); REQUIRE(umset_node.size() == 2); REQUIRE(umset_node.find(fkyaml::node(true)) != umset_node.end());