From b9cd76369f18e45c6197a02886d62eef74e6af34 Mon Sep 17 00:00:00 2001 From: fktn Date: Sun, 12 Jan 2025 23:00:05 +0900 Subject: [PATCH 01/12] extract create_object/destroy_object as utility functions --- include/fkYAML/detail/conversions/to_node.hpp | 18 +- .../detail/exception_safe_allocation.hpp | 108 ++++++++++ include/fkYAML/node.hpp | 71 ++----- single_include/fkYAML/node.hpp | 201 ++++++++++++------ 4 files changed, 272 insertions(+), 126 deletions(-) create mode 100644 include/fkYAML/detail/exception_safe_allocation.hpp diff --git a/include/fkYAML/detail/conversions/to_node.hpp b/include/fkYAML/detail/conversions/to_node.hpp index a8acb483..cc6e2f19 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 @@ -44,7 +45,7 @@ struct external_node_constructor { 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); + n.m_node_value.p_sequence = create_object(s); } /// @brief Constructs a basic_node object with rvalue sequence. @@ -55,8 +56,7 @@ struct external_node_constructor { 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)); + n.m_node_value.p_sequence = create_object(std::move(s)); } }; @@ -71,7 +71,7 @@ struct external_node_constructor { 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); + n.m_node_value.p_mapping = create_object(m); } /// @brief Constructs a basic_node object with rvalue mapping. @@ -82,8 +82,7 @@ struct external_node_constructor { 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)); + n.m_node_value.p_mapping = create_object(std::move(m)); } }; @@ -158,7 +157,7 @@ struct external_node_constructor { 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); + n.m_node_value.p_string = create_object(s); } /// @brief Constructs a basic_node object with rvalue strings. @@ -169,8 +168,7 @@ struct external_node_constructor { 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)); + n.m_node_value.p_string = create_object(std::move(s)); } /// @brief Constructs a basic_node object with compatible strings. @@ -188,7 +186,7 @@ struct external_node_constructor { 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); + n.m_node_value.p_string = create_object(s); } }; 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/node.hpp b/include/fkYAML/node.hpp index 2ab04823..683e7794 100644 --- a/include/fkYAML/node.hpp +++ b/include/fkYAML/node.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -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..9c551fd6 100644 --- a/single_include/fkYAML/node.hpp +++ b/single_include/fkYAML/node.hpp @@ -1189,6 +1189,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 +12160,8 @@ FK_YAML_NAMESPACE_END // #include +// #include + // #include // #include @@ -12085,7 +12199,7 @@ struct external_node_constructor { 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); + n.m_node_value.p_sequence = create_object(s); } /// @brief Constructs a basic_node object with rvalue sequence. @@ -12096,8 +12210,7 @@ struct external_node_constructor { 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)); + n.m_node_value.p_sequence = create_object(std::move(s)); } }; @@ -12112,7 +12225,7 @@ struct external_node_constructor { 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); + n.m_node_value.p_mapping = create_object(m); } /// @brief Constructs a basic_node object with rvalue mapping. @@ -12123,8 +12236,7 @@ struct external_node_constructor { 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)); + n.m_node_value.p_mapping = create_object(std::move(m)); } }; @@ -12199,7 +12311,7 @@ struct external_node_constructor { 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); + n.m_node_value.p_string = create_object(s); } /// @brief Constructs a basic_node object with rvalue strings. @@ -12210,8 +12322,7 @@ struct external_node_constructor { 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)); + n.m_node_value.p_string = create_object(std::move(s)); } /// @brief Constructs a basic_node object with compatible strings. @@ -12229,7 +12340,7 @@ struct external_node_constructor { 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); + n.m_node_value.p_string = create_object(s); } }; @@ -12747,10 +12858,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 +12876,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 +12890,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 +12921,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 +12949,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 +12967,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 +13063,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 +13073,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 +13169,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 +13180,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 +13191,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 +13201,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 +13212,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 +13223,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 From 6678c75ff2b4383bb8e5d2b9446ad7ba0e7b1889 Mon Sep 17 00:00:00 2001 From: fktn Date: Mon, 13 Jan 2025 00:57:49 +0900 Subject: [PATCH 02/12] refactored external_node_constructor --- include/fkYAML/detail/conversions/to_node.hpp | 212 ++++-------------- include/fkYAML/node.hpp | 2 +- 2 files changed, 47 insertions(+), 167 deletions(-) diff --git a/include/fkYAML/detail/conversions/to_node.hpp b/include/fkYAML/detail/conversions/to_node.hpp index cc6e2f19..0c1ffd89 100644 --- a/include/fkYAML/detail/conversions/to_node.hpp +++ b/include/fkYAML/detail/conversions/to_node.hpp @@ -31,162 +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 = 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 = 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 = 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 = 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 = 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 = 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 = 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; } }; @@ -207,7 +102,7 @@ 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 BasicNodeType::mapping_type objects. @@ -222,17 +117,15 @@ 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 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. @@ -240,13 +133,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. @@ -258,7 +147,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. @@ -270,7 +159,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. @@ -283,19 +172,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/node.hpp b/include/fkYAML/node.hpp index 683e7794..a393b7f0 100644 --- a/include/fkYAML/node.hpp +++ b/include/fkYAML/node.hpp @@ -145,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 From 080516a91d5151e19d84fc72d82cabc25de7e272 Mon Sep 17 00:00:00 2001 From: fktn Date: Mon, 13 Jan 2025 03:28:43 +0900 Subject: [PATCH 03/12] added builtin to_node impls for sequence-like types --- include/fkYAML/detail/conversions/to_node.hpp | 25 ++ single_include/fkYAML/node.hpp | 239 +++++---------- tests/unit_test/test_node_class.cpp | 284 +++++++++++++++++- 3 files changed, 371 insertions(+), 177 deletions(-) diff --git a/include/fkYAML/detail/conversions/to_node.hpp b/include/fkYAML/detail/conversions/to_node.hpp index 0c1ffd89..6abd5b60 100644 --- a/include/fkYAML/detail/conversions/to_node.hpp +++ b/include/fkYAML/detail/conversions/to_node.hpp @@ -105,6 +105,31 @@ inline void to_node(BasicNodeType& n, T&& s) noexcept { 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 +/// * 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>, + negation>>::value, + int> = 0> +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 BasicNodeType::mapping_type objects. /// @tparam BasicNodeType A basic_node template instance type. /// @tparam T A mapping node value type. diff --git a/single_include/fkYAML/node.hpp b/single_include/fkYAML/node.hpp index 9c551fd6..c9d0f886 100644 --- a/single_include/fkYAML/node.hpp +++ b/single_include/fkYAML/node.hpp @@ -12185,162 +12185,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 = 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 = 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 = 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 = 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 = 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 = 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 = 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; } }; @@ -12361,7 +12256,32 @@ 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 +/// * 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>, + negation>>::value, + int> = 0> +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 BasicNodeType::mapping_type objects. @@ -12376,17 +12296,15 @@ 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 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. @@ -12394,13 +12312,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. @@ -12412,7 +12326,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. @@ -12424,7 +12338,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. @@ -12437,19 +12351,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. @@ -12828,7 +12733,7 @@ class basic_node { using const_map_range = fkyaml::detail::map_range_proxy; private: - template + template friend struct fkyaml::detail::external_node_constructor; template diff --git a/tests/unit_test/test_node_class.cpp b/tests/unit_test/test_node_class.cpp index d2ad8157..711c772c 100644 --- a/tests/unit_test/test_node_class.cpp +++ b/tests/unit_test/test_node_class.cpp @@ -132,15 +132,279 @@ 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") { + SECTION("1D C-style array") { + fkyaml::node ints_node_val[2] {1, 2}; + int ints_val[2] {1, 2}; + + fkyaml::node ints_node(ints_node_val); + REQUIRE(ints_node.is_sequence()); + REQUIRE(ints_node.size() == 2); + REQUIRE(ints_node[0].is_integer()); + REQUIRE(ints_node[0].get_value() == 1); + REQUIRE(ints_node[1].is_integer()); + REQUIRE(ints_node[1].get_value() == 2); + + fkyaml::node ints(ints_val); + REQUIRE(ints.is_sequence()); + REQUIRE(ints.size() == 2); + REQUIRE(ints[0].is_integer()); + REQUIRE(ints[0].get_value() == 1); + REQUIRE(ints[1].is_integer()); + REQUIRE(ints[1].get_value() == 2); + } + + 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}}; + + 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++) { + REQUIRE(ints_node.at(i).at(j).is_integer()); + REQUIRE(ints_node.at(i).at(j).get_value() == i * 3 + j + 1); + + REQUIRE(ints.at(i).at(j).is_integer()); + REQUIRE(ints.at(i).at(j).get_value() == i * 3 + j + 1); + } + } + } + + 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}}}; + + 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++) { + REQUIRE(ints_node.at(i).at(j).at(k).is_integer()); + REQUIRE(ints_node.at(i).at(j).at(k).get_value() == i * 9 + j * 3 + k + 1); + + REQUIRE(ints.at(i).at(j).at(k).is_integer()); + REQUIRE(ints.at(i).at(j).at(k).get_value() == i * 9 + j * 3 + k + 1); + } + } + } + } + + SECTION("std::vector") { + std::vector vector_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::vector vector_bool_val {true, false}; + + fkyaml::node vector_node(vector_node_val); + REQUIRE(vector_node.is_sequence()); + REQUIRE(vector_node.size() == 2); + REQUIRE(vector_node[0].is_boolean()); + REQUIRE(vector_node[0].get_value() == true); + REQUIRE(vector_node[1].is_boolean()); + REQUIRE(vector_node[1].get_value() == false); + + fkyaml::node vector_bool(vector_bool_val); + REQUIRE(vector_bool.is_sequence()); + REQUIRE(vector_bool.size() == 2); + REQUIRE(vector_bool[0].is_boolean()); + REQUIRE(vector_bool[0].get_value() == true); + REQUIRE(vector_bool[1].is_boolean()); + REQUIRE(vector_bool[1].get_value() == false); + } + + SECTION("std::array") { + std::array array_node_val {{fkyaml::node(true), fkyaml::node(false)}}; + std::array array_bool_val {true, false}; + + fkyaml::node array_node(array_node_val); + REQUIRE(array_node.is_sequence()); + REQUIRE(array_node.size() == 2); + REQUIRE(array_node[0].is_boolean()); + REQUIRE(array_node[0].get_value() == true); + REQUIRE(array_node[1].is_boolean()); + REQUIRE(array_node[1].get_value() == false); + + fkyaml::node array_bool(array_bool_val); + REQUIRE(array_bool.is_sequence()); + REQUIRE(array_bool.size() == 2); + REQUIRE(array_bool[0].is_boolean()); + REQUIRE(array_bool[0].get_value() == true); + REQUIRE(array_bool[1].is_boolean()); + REQUIRE(array_bool[1].get_value() == false); + } + + SECTION("std::valarray") { + std::valarray valarray_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::valarray valarray_bool_val {true, false}; + + fkyaml::node valarray_node(valarray_node_val); + REQUIRE(valarray_node.is_sequence()); + REQUIRE(valarray_node.size() == 2); + REQUIRE(valarray_node[0].is_boolean()); + REQUIRE(valarray_node[0].get_value() == true); + REQUIRE(valarray_node[1].is_boolean()); + REQUIRE(valarray_node[1].get_value() == false); + + fkyaml::node valarray_bool(valarray_bool_val); + REQUIRE(valarray_bool.is_sequence()); + REQUIRE(valarray_bool.size() == 2); + REQUIRE(valarray_bool[0].is_boolean()); + REQUIRE(valarray_bool[0].get_value() == true); + REQUIRE(valarray_bool[1].is_boolean()); + REQUIRE(valarray_bool[1].get_value() == false); + } + + 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}; + + fkyaml::node forward_list_node(forward_list_node_val); + REQUIRE(forward_list_node.is_sequence()); + REQUIRE(forward_list_node.size() == 2); + REQUIRE(forward_list_node[0].is_boolean()); + REQUIRE(forward_list_node[0].get_value() == true); + REQUIRE(forward_list_node[1].is_boolean()); + REQUIRE(forward_list_node[1].get_value() == false); + + fkyaml::node forward_list_bool(forward_list_bool_val); + REQUIRE(forward_list_bool.is_sequence()); + REQUIRE(forward_list_bool.size() == 2); + REQUIRE(forward_list_bool[0].is_boolean()); + REQUIRE(forward_list_bool[0].get_value() == true); + REQUIRE(forward_list_bool[1].is_boolean()); + REQUIRE(forward_list_bool[1].get_value() == false); + } + + SECTION("std::deque") { + std::deque deque_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::deque deque_bool_val {true, false}; + + fkyaml::node deque_node(deque_node_val); + REQUIRE(deque_node.is_sequence()); + REQUIRE(deque_node.size() == 2); + REQUIRE(deque_node[0].is_boolean()); + REQUIRE(deque_node[0].get_value() == true); + REQUIRE(deque_node[1].is_boolean()); + REQUIRE(deque_node[1].get_value() == false); + + fkyaml::node deque_bool(deque_bool_val); + REQUIRE(deque_bool.is_sequence()); + REQUIRE(deque_bool.size() == 2); + REQUIRE(deque_bool[0].is_boolean()); + REQUIRE(deque_bool[0].get_value() == true); + REQUIRE(deque_bool[1].is_boolean()); + REQUIRE(deque_bool[1].get_value() == false); + } + + SECTION("std::list") { + std::list list_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::list list_bool_val {true, false}; + + fkyaml::node list_node(list_node_val); + REQUIRE(list_node.is_sequence()); + REQUIRE(list_node.size() == 2); + REQUIRE(list_node[0].is_boolean()); + REQUIRE(list_node[0].get_value() == true); + REQUIRE(list_node[1].is_boolean()); + REQUIRE(list_node[1].get_value() == false); + + fkyaml::node list_bool(list_bool_val); + REQUIRE(list_bool.is_sequence()); + REQUIRE(list_bool.size() == 2); + REQUIRE(list_bool[0].is_boolean()); + REQUIRE(list_bool[0].get_value() == true); + REQUIRE(list_bool[1].is_boolean()); + REQUIRE(list_bool[1].get_value() == false); + } + + SECTION("std::set") { + std::set set_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::set set_bool_val {true, false}; + + fkyaml::node set_node(set_node_val); + REQUIRE(set_node.is_sequence()); + REQUIRE(set_node.size() == 2); + REQUIRE(set_node[0].is_boolean()); + REQUIRE(set_node[0].get_value() == false); + REQUIRE(set_node[1].is_boolean()); + REQUIRE(set_node[1].get_value() == true); + + fkyaml::node set_bool(set_bool_val); + REQUIRE(set_bool.is_sequence()); + REQUIRE(set_bool.size() == 2); + REQUIRE(set_bool[0].is_boolean()); + REQUIRE(set_bool[0].get_value() == false); + REQUIRE(set_bool[1].is_boolean()); + REQUIRE(set_bool[1].get_value() == true); + } + + SECTION("std::multiset") { + std::multiset multiset_node_val {fkyaml::node(true), fkyaml::node(false)}; + std::multiset multiset_bool_val {true, false}; + + fkyaml::node multiset_node(multiset_node_val); + REQUIRE(multiset_node.is_sequence()); + REQUIRE(multiset_node.size() == 2); + REQUIRE(multiset_node[0].is_boolean()); + REQUIRE(multiset_node[0].get_value() == false); + REQUIRE(multiset_node[1].is_boolean()); + REQUIRE(multiset_node[1].get_value() == true); + + fkyaml::node multiset_bool(multiset_bool_val); + REQUIRE(multiset_bool.is_sequence()); + REQUIRE(multiset_bool.size() == 2); + REQUIRE(multiset_bool[0].is_boolean()); + REQUIRE(multiset_bool[0].get_value() == false); + REQUIRE(multiset_bool[1].is_boolean()); + REQUIRE(multiset_bool[1].get_value() == true); + } + + 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}; + + fkyaml::node unordered_set_node(unordered_set_node_val); + REQUIRE(unordered_set_node.is_sequence()); + REQUIRE(unordered_set_node.size() == 2); + REQUIRE(unordered_set_node[0].is_boolean()); + bool ret = unordered_set_node[0].get_value(); + REQUIRE(unordered_set_node[1].is_boolean()); + REQUIRE(unordered_set_node[1].get_value() == !ret); + + fkyaml::node unordered_set_bool(unordered_set_bool_val); + REQUIRE(unordered_set_bool.is_sequence()); + REQUIRE(unordered_set_bool.size() == 2); + REQUIRE(unordered_set_bool[0].is_boolean()); + ret = unordered_set_bool[0].get_value(); + REQUIRE(unordered_set_bool[1].is_boolean()); + REQUIRE(unordered_set_bool[1].get_value() == !ret); + } + + 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}; + + fkyaml::node unordered_multiset_node(unordered_multiset_node_val); + REQUIRE(unordered_multiset_node.is_sequence()); + REQUIRE(unordered_multiset_node.size() == 2); + REQUIRE(unordered_multiset_node[0].is_boolean()); + bool ret = unordered_multiset_node[0].get_value(); + REQUIRE(unordered_multiset_node[1].is_boolean()); + REQUIRE(unordered_multiset_node[1].get_value() == !ret); + + fkyaml::node unordered_multiset_bool(unordered_multiset_bool_val); + REQUIRE(unordered_multiset_bool.is_sequence()); + REQUIRE(unordered_multiset_bool.size() == 2); + REQUIRE(unordered_multiset_bool[0].is_boolean()); + ret = unordered_multiset_bool[0].get_value(); + REQUIRE(unordered_multiset_bool[1].is_boolean()); + REQUIRE(unordered_multiset_bool[1].get_value() == !ret); + } } TEST_CASE("Node_MappingCtor") { @@ -2668,7 +2932,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()); From d0924a6e1b918f21eb539243b5a7022753396431 Mon Sep 17 00:00:00 2001 From: fktn Date: Mon, 13 Jan 2025 19:45:18 +0900 Subject: [PATCH 04/12] ensure cv-qualifiers and references are removed in detecting member types --- include/fkYAML/detail/meta/detect.hpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) 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. From f60c66a8fe0faed0c975a024972ff7fa449aaf67 Mon Sep 17 00:00:00 2001 From: fktn Date: Mon, 13 Jan 2025 19:52:42 +0900 Subject: [PATCH 05/12] added builtin to_node impls for mapping-like types --- include/fkYAML/detail/conversions/to_node.hpp | 28 ++++- tests/unit_test/test_node_class.cpp | 112 +++++++++++++++--- 2 files changed, 124 insertions(+), 16 deletions(-) diff --git a/include/fkYAML/detail/conversions/to_node.hpp b/include/fkYAML/detail/conversions/to_node.hpp index 6abd5b60..b92914b7 100644 --- a/include/fkYAML/detail/conversions/to_node.hpp +++ b/include/fkYAML/detail/conversions/to_node.hpp @@ -121,7 +121,7 @@ template < is_basic_node, negation>>, negation>>, detect::has_begin_end, - negation>, + negation, detect::has_mapped_type>>, negation>>::value, int> = 0> inline void to_node(BasicNodeType& n, CompatSeqType&& s) { @@ -145,6 +145,32 @@ inline void to_node(BasicNodeType& n, T&& m) noexcept { external_node_constructor::mapping(n, std::forward(m)); } +/// @brief to_node function for compatible sequence types. +/// @note This overload is enabled when +/// * both begin()/end() functions are callable +/// * 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 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 : 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 diff --git a/tests/unit_test/test_node_class.cpp b/tests/unit_test/test_node_class.cpp index 711c772c..e885f0ec 100644 --- a/tests/unit_test/test_node_class.cpp +++ b/tests/unit_test/test_node_class.cpp @@ -405,22 +405,104 @@ TEST_CASE("Node_CtorWithCompatibleType") { REQUIRE(unordered_multiset_bool[1].is_boolean()); REQUIRE(unordered_multiset_bool[1].get_value() == !ret); } -} -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); -} - -TEST_CASE("Node_NullCtor") { - fkyaml::node node(nullptr); - REQUIRE(node.get_type() == fkyaml::node_type::NULL_OBJECT); - REQUIRE(node.is_null()); + // 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() == 123); + REQUIRE(n.at("foo").is_integer()); + REQUIRE(n.at("foo").get_value() == -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() == 123); + REQUIRE(n.at("foo").is_integer()); + REQUIRE(n.at("foo").get_value() == -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() == 123); + REQUIRE(n.at("foo").is_integer()); + REQUIRE(n.at("foo").get_value() == -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() == 123); + REQUIRE(n.at("foo").is_integer()); + REQUIRE(n.at("foo").get_value() == -456); + }; + + fkyaml::node unordered_multimap_node(unordered_multimap_node_val); + validate(unordered_multimap_node); + + fkyaml::node unordered_multimap(unordered_multimap_val); + validate(unordered_multimap); + } } TEST_CASE("Node_BooleanCtor") { From ee8a29d13614ced27602e13ca4a0b6f153b52263 Mon Sep 17 00:00:00 2001 From: fktn Date: Mon, 13 Jan 2025 20:05:08 +0900 Subject: [PATCH 06/12] cleaned up test codes --- tests/unit_test/test_node_class.cpp | 294 ++++++++++++++-------------- 1 file changed, 148 insertions(+), 146 deletions(-) diff --git a/tests/unit_test/test_node_class.cpp b/tests/unit_test/test_node_class.cpp index e885f0ec..934e5e71 100644 --- a/tests/unit_test/test_node_class.cpp +++ b/tests/unit_test/test_node_class.cpp @@ -133,40 +133,43 @@ TEST_CASE("Node_ThrowingSpecializationTypeCtor") { } 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); - REQUIRE(ints_node.is_sequence()); - REQUIRE(ints_node.size() == 2); - REQUIRE(ints_node[0].is_integer()); - REQUIRE(ints_node[0].get_value() == 1); - REQUIRE(ints_node[1].is_integer()); - REQUIRE(ints_node[1].get_value() == 2); + validate(ints_node); fkyaml::node ints(ints_val); - REQUIRE(ints.is_sequence()); - REQUIRE(ints.size() == 2); - REQUIRE(ints[0].is_integer()); - REQUIRE(ints[0].get_value() == 1); - REQUIRE(ints[1].is_integer()); - REQUIRE(ints[1].get_value() == 2); + 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() == 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++) { - REQUIRE(ints_node.at(i).at(j).is_integer()); - REQUIRE(ints_node.at(i).at(j).get_value() == i * 3 + j + 1); - - REQUIRE(ints.at(i).at(j).is_integer()); - REQUIRE(ints.at(i).at(j).get_value() == i * 3 + j + 1); + validate(ints_node, i, j); + validate(ints, i, j); } } } @@ -181,16 +184,18 @@ TEST_CASE("Node_CtorWithCompatibleType") { {{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() == 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++) { - REQUIRE(ints_node.at(i).at(j).at(k).is_integer()); - REQUIRE(ints_node.at(i).at(j).at(k).get_value() == i * 9 + j * 3 + k + 1); - - REQUIRE(ints.at(i).at(j).at(k).is_integer()); - REQUIRE(ints.at(i).at(j).at(k).get_value() == i * 9 + j * 3 + k + 1); + validate(ints_node, i, j, k); + validate(ints, i, j, k); } } } @@ -200,210 +205,200 @@ TEST_CASE("Node_CtorWithCompatibleType") { std::vector vector_node_val {fkyaml::node(true), fkyaml::node(false)}; std::vector vector_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 vector_node(vector_node_val); - REQUIRE(vector_node.is_sequence()); - REQUIRE(vector_node.size() == 2); - REQUIRE(vector_node[0].is_boolean()); - REQUIRE(vector_node[0].get_value() == true); - REQUIRE(vector_node[1].is_boolean()); - REQUIRE(vector_node[1].get_value() == false); + validate(vector_node); fkyaml::node vector_bool(vector_bool_val); - REQUIRE(vector_bool.is_sequence()); - REQUIRE(vector_bool.size() == 2); - REQUIRE(vector_bool[0].is_boolean()); - REQUIRE(vector_bool[0].get_value() == true); - REQUIRE(vector_bool[1].is_boolean()); - REQUIRE(vector_bool[1].get_value() == false); + validate(vector_bool); } 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); - REQUIRE(array_node.is_sequence()); - REQUIRE(array_node.size() == 2); - REQUIRE(array_node[0].is_boolean()); - REQUIRE(array_node[0].get_value() == true); - REQUIRE(array_node[1].is_boolean()); - REQUIRE(array_node[1].get_value() == false); + validate(array_node); fkyaml::node array_bool(array_bool_val); - REQUIRE(array_bool.is_sequence()); - REQUIRE(array_bool.size() == 2); - REQUIRE(array_bool[0].is_boolean()); - REQUIRE(array_bool[0].get_value() == true); - REQUIRE(array_bool[1].is_boolean()); - REQUIRE(array_bool[1].get_value() == false); + 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); - REQUIRE(valarray_node.is_sequence()); - REQUIRE(valarray_node.size() == 2); - REQUIRE(valarray_node[0].is_boolean()); - REQUIRE(valarray_node[0].get_value() == true); - REQUIRE(valarray_node[1].is_boolean()); - REQUIRE(valarray_node[1].get_value() == false); + validate(valarray_node); fkyaml::node valarray_bool(valarray_bool_val); - REQUIRE(valarray_bool.is_sequence()); - REQUIRE(valarray_bool.size() == 2); - REQUIRE(valarray_bool[0].is_boolean()); - REQUIRE(valarray_bool[0].get_value() == true); - REQUIRE(valarray_bool[1].is_boolean()); - REQUIRE(valarray_bool[1].get_value() == false); + 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); - REQUIRE(forward_list_node.is_sequence()); - REQUIRE(forward_list_node.size() == 2); - REQUIRE(forward_list_node[0].is_boolean()); - REQUIRE(forward_list_node[0].get_value() == true); - REQUIRE(forward_list_node[1].is_boolean()); - REQUIRE(forward_list_node[1].get_value() == false); + validate(forward_list_node); fkyaml::node forward_list_bool(forward_list_bool_val); - REQUIRE(forward_list_bool.is_sequence()); - REQUIRE(forward_list_bool.size() == 2); - REQUIRE(forward_list_bool[0].is_boolean()); - REQUIRE(forward_list_bool[0].get_value() == true); - REQUIRE(forward_list_bool[1].is_boolean()); - REQUIRE(forward_list_bool[1].get_value() == false); + 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); - REQUIRE(deque_node.is_sequence()); - REQUIRE(deque_node.size() == 2); - REQUIRE(deque_node[0].is_boolean()); - REQUIRE(deque_node[0].get_value() == true); - REQUIRE(deque_node[1].is_boolean()); - REQUIRE(deque_node[1].get_value() == false); + validate(deque_node); fkyaml::node deque_bool(deque_bool_val); - REQUIRE(deque_bool.is_sequence()); - REQUIRE(deque_bool.size() == 2); - REQUIRE(deque_bool[0].is_boolean()); - REQUIRE(deque_bool[0].get_value() == true); - REQUIRE(deque_bool[1].is_boolean()); - REQUIRE(deque_bool[1].get_value() == false); + 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); - REQUIRE(list_node.is_sequence()); - REQUIRE(list_node.size() == 2); - REQUIRE(list_node[0].is_boolean()); - REQUIRE(list_node[0].get_value() == true); - REQUIRE(list_node[1].is_boolean()); - REQUIRE(list_node[1].get_value() == false); + validate(list_node); fkyaml::node list_bool(list_bool_val); - REQUIRE(list_bool.is_sequence()); - REQUIRE(list_bool.size() == 2); - REQUIRE(list_bool[0].is_boolean()); - REQUIRE(list_bool[0].get_value() == true); - REQUIRE(list_bool[1].is_boolean()); - REQUIRE(list_bool[1].get_value() == false); + 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); - REQUIRE(set_node.is_sequence()); - REQUIRE(set_node.size() == 2); - REQUIRE(set_node[0].is_boolean()); - REQUIRE(set_node[0].get_value() == false); - REQUIRE(set_node[1].is_boolean()); - REQUIRE(set_node[1].get_value() == true); + validate(set_node); fkyaml::node set_bool(set_bool_val); - REQUIRE(set_bool.is_sequence()); - REQUIRE(set_bool.size() == 2); - REQUIRE(set_bool[0].is_boolean()); - REQUIRE(set_bool[0].get_value() == false); - REQUIRE(set_bool[1].is_boolean()); - REQUIRE(set_bool[1].get_value() == true); + 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); - REQUIRE(multiset_node.is_sequence()); - REQUIRE(multiset_node.size() == 2); - REQUIRE(multiset_node[0].is_boolean()); - REQUIRE(multiset_node[0].get_value() == false); - REQUIRE(multiset_node[1].is_boolean()); - REQUIRE(multiset_node[1].get_value() == true); + validate(multiset_node); fkyaml::node multiset_bool(multiset_bool_val); - REQUIRE(multiset_bool.is_sequence()); - REQUIRE(multiset_bool.size() == 2); - REQUIRE(multiset_bool[0].is_boolean()); - REQUIRE(multiset_bool[0].get_value() == false); - REQUIRE(multiset_bool[1].is_boolean()); - REQUIRE(multiset_bool[1].get_value() == true); + 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); - REQUIRE(unordered_set_node.is_sequence()); - REQUIRE(unordered_set_node.size() == 2); - REQUIRE(unordered_set_node[0].is_boolean()); - bool ret = unordered_set_node[0].get_value(); - REQUIRE(unordered_set_node[1].is_boolean()); - REQUIRE(unordered_set_node[1].get_value() == !ret); + validate(unordered_set_node); fkyaml::node unordered_set_bool(unordered_set_bool_val); - REQUIRE(unordered_set_bool.is_sequence()); - REQUIRE(unordered_set_bool.size() == 2); - REQUIRE(unordered_set_bool[0].is_boolean()); - ret = unordered_set_bool[0].get_value(); - REQUIRE(unordered_set_bool[1].is_boolean()); - REQUIRE(unordered_set_bool[1].get_value() == !ret); + 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); - REQUIRE(unordered_multiset_node.is_sequence()); - REQUIRE(unordered_multiset_node.size() == 2); - REQUIRE(unordered_multiset_node[0].is_boolean()); - bool ret = unordered_multiset_node[0].get_value(); - REQUIRE(unordered_multiset_node[1].is_boolean()); - REQUIRE(unordered_multiset_node[1].get_value() == !ret); + validate(unordered_multiset_node); fkyaml::node unordered_multiset_bool(unordered_multiset_bool_val); - REQUIRE(unordered_multiset_bool.is_sequence()); - REQUIRE(unordered_multiset_bool.size() == 2); - REQUIRE(unordered_multiset_bool[0].is_boolean()); - ret = unordered_multiset_bool[0].get_value(); - REQUIRE(unordered_multiset_bool[1].is_boolean()); - REQUIRE(unordered_multiset_bool[1].get_value() == !ret); + validate(unordered_multiset_bool); } // mapping-like types @@ -503,13 +498,20 @@ TEST_CASE("Node_CtorWithCompatibleType") { fkyaml::node unordered_multimap(unordered_multimap_val); validate(unordered_multimap); } + + SECTION("std::nullptr_t") { + fkyaml::node node(nullptr); + REQUIRE(node.is_null()); + } + + SECTION("bool") { + fkyaml::node node(true); + REQUIRE(node.is_boolean()); + REQUIRE(node.get_value() == true); + } } 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); } TEST_CASE("Node_IntegerCtor") { From 785c69065a6d893e73ac4ae534f3620ed2be1465 Mon Sep 17 00:00:00 2001 From: fktn Date: Wed, 15 Jan 2025 00:12:00 +0900 Subject: [PATCH 07/12] added tests of conversions from compatible types to nodes --- tests/unit_test/test_node_class.cpp | 95 ++++++++++++++++------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/tests/unit_test/test_node_class.cpp b/tests/unit_test/test_node_class.cpp index 934e5e71..55a98f8c 100644 --- a/tests/unit_test/test_node_class.cpp +++ b/tests/unit_test/test_node_class.cpp @@ -161,7 +161,7 @@ TEST_CASE("Node_CtorWithCompatibleType") { 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() == i * 3 + j + 1); + REQUIRE(n.at(i).at(j).get_value_ref() == i * 3 + j + 1); }; fkyaml::node ints_node(ints_node_val); @@ -186,7 +186,7 @@ TEST_CASE("Node_CtorWithCompatibleType") { 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() == i * 9 + j * 3 + k + 1); + REQUIRE(n.at(i).at(j).at(k).get_value_ref() == i * 9 + j * 3 + k + 1); }; fkyaml::node ints_node(ints_node_val); @@ -415,9 +415,9 @@ TEST_CASE("Node_CtorWithCompatibleType") { REQUIRE(n.is_mapping()); REQUIRE(n.size() == 2); REQUIRE(n.at("test").is_integer()); - REQUIRE(n.at("test").get_value() == 123); + REQUIRE(n.at("test").get_value_ref() == 123); REQUIRE(n.at("foo").is_integer()); - REQUIRE(n.at("foo").get_value() == -456); + REQUIRE(n.at("foo").get_value_ref() == -456); }; fkyaml::node map_node(map_node_val); @@ -439,9 +439,9 @@ TEST_CASE("Node_CtorWithCompatibleType") { REQUIRE(n.is_mapping()); REQUIRE(n.size() == 2); REQUIRE(n.at("test").is_integer()); - REQUIRE(n.at("test").get_value() == 123); + REQUIRE(n.at("test").get_value_ref() == 123); REQUIRE(n.at("foo").is_integer()); - REQUIRE(n.at("foo").get_value() == -456); + REQUIRE(n.at("foo").get_value_ref() == -456); }; fkyaml::node multimap_node(multimap_node_val); @@ -463,9 +463,9 @@ TEST_CASE("Node_CtorWithCompatibleType") { REQUIRE(n.is_mapping()); REQUIRE(n.size() == 2); REQUIRE(n.at("test").is_integer()); - REQUIRE(n.at("test").get_value() == 123); + REQUIRE(n.at("test").get_value_ref() == 123); REQUIRE(n.at("foo").is_integer()); - REQUIRE(n.at("foo").get_value() == -456); + REQUIRE(n.at("foo").get_value_ref() == -456); }; fkyaml::node unordered_map_node(unordered_map_node_val); @@ -487,9 +487,9 @@ TEST_CASE("Node_CtorWithCompatibleType") { REQUIRE(n.is_mapping()); REQUIRE(n.size() == 2); REQUIRE(n.at("test").is_integer()); - REQUIRE(n.at("test").get_value() == 123); + REQUIRE(n.at("test").get_value_ref() == 123); REQUIRE(n.at("foo").is_integer()); - REQUIRE(n.at("foo").get_value() == -456); + REQUIRE(n.at("foo").get_value_ref() == -456); }; fkyaml::node unordered_multimap_node(unordered_multimap_node_val); @@ -504,48 +504,59 @@ TEST_CASE("Node_CtorWithCompatibleType") { REQUIRE(node.is_null()); } - SECTION("bool") { + SECTION("boolean") { fkyaml::node node(true); REQUIRE(node.is_boolean()); - REQUIRE(node.get_value() == true); + REQUIRE(node.get_value_ref() == true); } -} -TEST_CASE("Node_BooleanCtor") { -} + SECTION("integer") { + fkyaml::node node(23467); + REQUIRE(node.is_integer()); + REQUIRE(node.get_value_ref() == 23467); + } -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); -} + SECTION("floating point") { + fkyaml::node node(3.14); + REQUIRE(node.is_float_number()); + REQUIRE(node.get_value_ref() == 3.14); + } -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); -} + // string-like types -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::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"; From 33e2cdffc3e28e04fd0444ab0861bdd893999c6a Mon Sep 17 00:00:00 2001 From: fktn Date: Wed, 15 Jan 2025 00:16:56 +0900 Subject: [PATCH 08/12] fixed typos in doxygen comments --- include/fkYAML/detail/conversions/to_node.hpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/include/fkYAML/detail/conversions/to_node.hpp b/include/fkYAML/detail/conversions/to_node.hpp index b92914b7..3c9f961c 100644 --- a/include/fkYAML/detail/conversions/to_node.hpp +++ b/include/fkYAML/detail/conversions/to_node.hpp @@ -107,7 +107,7 @@ inline void to_node(BasicNodeType& n, T&& s) noexcept { /// @brief to_node function for compatible sequence types. /// @note This overload is enabled when -/// * both begin()/end() functions are callable +/// * 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. @@ -145,15 +145,14 @@ inline void to_node(BasicNodeType& n, T&& m) noexcept { external_node_constructor::mapping(n, std::forward(m)); } -/// @brief to_node function for compatible sequence types. +/// @brief to_node function for compatible mapping types. /// @note This overload is enabled when -/// * both begin()/end() functions are callable -/// * CompatSeqType doesn't have `mapped_type` (mapping-like type) -/// * BasicNodeType::string_type cannot be constructed from a CompatSeqType object (string-like type) +/// * 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 CompatSeqType A container type. +/// @tparam CompatMapType A container type. /// @param n A basic_node object. -/// @param s A container object. +/// @param m A container object. template < typename BasicNodeType, typename CompatMapType, enable_if_t< From d6540e9d27267e6f0cc89a103b7b219509e7d9b2 Mon Sep 17 00:00:00 2001 From: fktn Date: Wed, 15 Jan 2025 01:18:07 +0900 Subject: [PATCH 09/12] add builtin to_node() impls for std::pair & std::tuple --- include/fkYAML/detail/conversions/to_node.hpp | 33 +++++++++++++++ tests/unit_test/test_node_class.cpp | 42 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/include/fkYAML/detail/conversions/to_node.hpp b/include/fkYAML/detail/conversions/to_node.hpp index 3c9f961c..b0b95ddd 100644 --- a/include/fkYAML/detail/conversions/to_node.hpp +++ b/include/fkYAML/detail/conversions/to_node.hpp @@ -130,6 +130,39 @@ inline void to_node(BasicNodeType& n, CompatSeqType&& s) { 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. /// @tparam BasicNodeType A basic_node template instance type. /// @tparam T A mapping node value type. diff --git a/tests/unit_test/test_node_class.cpp b/tests/unit_test/test_node_class.cpp index 55a98f8c..315abde1 100644 --- a/tests/unit_test/test_node_class.cpp +++ b/tests/unit_test/test_node_class.cpp @@ -401,6 +401,48 @@ TEST_CASE("Node_CtorWithCompatibleType") { 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") { From 505ebce31cd59fa75334a6059a7f2a1d63f8b095 Mon Sep 17 00:00:00 2001 From: fktn Date: Wed, 15 Jan 2025 01:18:19 +0900 Subject: [PATCH 10/12] run amalgamation --- single_include/fkYAML/node.hpp | 84 ++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 15 deletions(-) diff --git a/single_include/fkYAML/node.hpp b/single_include/fkYAML/node.hpp index c9d0f886..5a74d960 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. @@ -12261,7 +12257,7 @@ inline void to_node(BasicNodeType& n, T&& s) noexcept { /// @brief to_node function for compatible sequence types. /// @note This overload is enabled when -/// * both begin()/end() functions are callable +/// * 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. @@ -12275,7 +12271,7 @@ template < is_basic_node, negation>>, negation>>, detect::has_begin_end, - negation>, + negation, detect::has_mapped_type>>, negation>>::value, int> = 0> inline void to_node(BasicNodeType& n, CompatSeqType&& s) { @@ -12284,6 +12280,39 @@ inline void to_node(BasicNodeType& n, CompatSeqType&& s) { 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. /// @tparam BasicNodeType A basic_node template instance type. /// @tparam T A mapping node value type. @@ -12299,6 +12328,31 @@ inline void to_node(BasicNodeType& n, T&& m) noexcept { 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 : 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 From 1b24ba4ca54c16faba4a9e5a3af549532ec497fa Mon Sep 17 00:00:00 2001 From: fktn Date: Wed, 15 Jan 2025 02:20:20 +0900 Subject: [PATCH 11/12] fixed compile errors with older gcc/clang versions --- .github/workflows/ubuntu.yml | 4 ++++ include/fkYAML/detail/conversions/to_node.hpp | 3 ++- single_include/fkYAML/node.hpp | 3 ++- tests/unit_test/test_node_class.cpp | 6 +++--- 4 files changed, 11 insertions(+), 5 deletions(-) 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/include/fkYAML/detail/conversions/to_node.hpp b/include/fkYAML/detail/conversions/to_node.hpp index b0b95ddd..dc258948 100644 --- a/include/fkYAML/detail/conversions/to_node.hpp +++ b/include/fkYAML/detail/conversions/to_node.hpp @@ -124,6 +124,7 @@ template < 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; @@ -198,7 +199,7 @@ template < inline void to_node(BasicNodeType& n, CompatMapType&& m) { external_node_constructor::mapping(n); auto& map = n.template get_value_ref(); - for (const auto& pair : m) { + for (const auto& pair : std::forward(m)) { map.emplace(pair.first, pair.second); } } diff --git a/single_include/fkYAML/node.hpp b/single_include/fkYAML/node.hpp index 5a74d960..9ecbbb96 100644 --- a/single_include/fkYAML/node.hpp +++ b/single_include/fkYAML/node.hpp @@ -12274,6 +12274,7 @@ template < 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; @@ -12348,7 +12349,7 @@ template < inline void to_node(BasicNodeType& n, CompatMapType&& m) { external_node_constructor::mapping(n); auto& map = n.template get_value_ref(); - for (const auto& pair : m) { + for (const auto& pair : std::forward(m)) { map.emplace(pair.first, pair.second); } } diff --git a/tests/unit_test/test_node_class.cpp b/tests/unit_test/test_node_class.cpp index 315abde1..f39aaa6e 100644 --- a/tests/unit_test/test_node_class.cpp +++ b/tests/unit_test/test_node_class.cpp @@ -223,7 +223,7 @@ TEST_CASE("Node_CtorWithCompatibleType") { SECTION("std::array") { std::array array_node_val {{fkyaml::node(true), fkyaml::node(false)}}; - std::array array_bool_val {true, false}; + std::array array_bool_val {{true, false}}; auto validate = [](const fkyaml::node& n) { REQUIRE(n.is_sequence()); @@ -422,8 +422,8 @@ TEST_CASE("Node_CtorWithCompatibleType") { } SECTION("std::tuple") { - std::tuple tuple_node_val = {"test", 123, true}; - std::tuple tuple_val = {"test", 123, true}; + 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()); From 0967aee22bc4a92d96bddc1e38fd2acae37897f7 Mon Sep 17 00:00:00 2001 From: fktn Date: Wed, 15 Jan 2025 02:38:50 +0900 Subject: [PATCH 12/12] updated docs & examples --- docs/docs/api/basic_node/constructor.md | 8 ++++++ examples/apis/basic_node/constructor_6.cpp | 27 ++++++++++++++++--- examples/apis/basic_node/constructor_6.output | 15 +++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) 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