From b679ebba04cf094c19f9224ab34b665e6160b3f4 Mon Sep 17 00:00:00 2001 From: Spiros Tsalikis Date: Wed, 23 Mar 2022 22:54:46 -0400 Subject: [PATCH] dynamic_format_arg_store: Add copy constructor To implement this copy constructor we need the following copy constructors too: 1) dynamic_arg_list 2) basic_format_arg 1) value 3) named_arg_info --- include/fmt/args.h | 47 ++++++++++++++++++++++++++++++++++++ include/fmt/core.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++ test/args-test.cc | 37 +++++++++++++++++++--------- 3 files changed, 133 insertions(+), 11 deletions(-) diff --git a/include/fmt/args.h b/include/fmt/args.h index a3966d14..662de329 100644 --- a/include/fmt/args.h +++ b/include/fmt/args.h @@ -33,23 +33,61 @@ class dynamic_arg_list { // unit for placing vtable. So storage_node_base is made a fake template. template struct node { virtual ~node() = default; + virtual std::unique_ptr> clone() const = 0; std::unique_ptr> next; }; template struct typed_node : node<> { T value; + FMT_CONSTEXPR typed_node(const typed_node& node) : value(node.value) {} + template FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} template FMT_CONSTEXPR typed_node(const basic_string_view& arg) : value(arg.data(), arg.size()) {} + + std::unique_ptr> clone() const override { + return std::unique_ptr>(new typed_node(*this)); + } }; std::unique_ptr> head_; public: + constexpr dynamic_arg_list() = default; + + dynamic_arg_list(const dynamic_arg_list& arg_list) { + if (arg_list.head_) { + head_ = arg_list.head_->clone(); + auto temp = head_.get(); + auto arg_list_temp = arg_list.head_.get(); + while (arg_list_temp->next) { + temp->next = arg_list_temp->next->clone(); + temp = temp->next.get(); + arg_list_temp = arg_list_temp->next.get(); + } + } + } + + dynamic_arg_list& operator=(const dynamic_arg_list& arg_list) { + if (this != &arg_list) { + if (arg_list.head_) { + head_ = arg_list.head_->clone(); + auto temp = head_.get(); + auto arg_list_temp = arg_list.head_.get(); + while (arg_list_temp->next) { + temp->next = arg_list_temp->next->clone(); + temp = temp->next.get(); + arg_list_temp = arg_list_temp->next.get(); + } + } + } + return *this; + } + template const T& push(const Arg& arg) { auto new_node = std::unique_ptr>(new typed_node(arg)); auto& value = new_node->value; @@ -145,6 +183,15 @@ class dynamic_format_arg_store public: constexpr dynamic_format_arg_store() = default; + dynamic_format_arg_store(const dynamic_format_arg_store& store) + : +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + basic_format_args(), +#endif + data_(store.data_), + named_info_(store.named_info_), + dynamic_args_(store.dynamic_args_) { + } /** \rst Adds an argument into the dynamic store for later passing to a formatting diff --git a/include/fmt/core.h b/include/fmt/core.h index 106fd6fe..1117498e 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1204,6 +1204,63 @@ template class value { named_arg_value named_args; }; + FMT_INLINE value(const value& v, const type& t) { + switch (t) { + case type::none_type: + no_value = monostate(); + break; + case type::int_type: + int_value = v.int_value; + break; + case type::uint_type: + uint_value = v.uint_value; + break; + case type::long_long_type: + long_long_value = v.long_long_value; + break; + case type::ulong_long_type: + ulong_long_value = v.ulong_long_value; + break; + case type::int128_type: + int128_value = v.int128_value; + break; + case type::uint128_type: + uint128_value = v.uint128_value; + break; + case type::bool_type: + bool_value = v.bool_value; + break; + case type::char_type: + char_value = v.char_value; + break; + case type::float_type: + float_value = v.float_value; + break; + case type::double_type: + double_value = v.double_value; + break; + case type::long_double_type: + long_double_value = v.long_double_value; + break; + case type::cstring_type: + case type::string_type: + string.data = v.string.data; + string.size = v.string.size; + break; + case type::pointer_type: + pointer = v.pointer; + break; + case type::custom_type: + custom.value = v.custom.value; + custom.format = v.custom.format; + break; + default: + named_args.data = v.named_args.data; + named_args.size = v.named_args.size; + break; + } + } + constexpr FMT_INLINE value() : no_value() {} constexpr FMT_INLINE value(int val) : int_value(val) {} constexpr FMT_INLINE value(unsigned val) : uint_value(val) {} @@ -1548,6 +1605,9 @@ template class basic_format_arg { constexpr basic_format_arg() : type_(detail::type::none_type) {} + basic_format_arg(const basic_format_arg& arg) + : value_(arg.value_, arg.type_), type_(arg.type_) {} + constexpr explicit operator bool() const noexcept { return type_ != detail::type::none_type; } diff --git a/test/args-test.cc b/test/args-test.cc index aa01fa4e..d7482bee 100644 --- a/test/args-test.cc +++ b/test/args-test.cc @@ -12,7 +12,7 @@ #include "gtest/gtest.h" TEST(args_test, basic) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.push_back(42); store.push_back("abc1"); store.push_back(1.5f); @@ -21,7 +21,7 @@ TEST(args_test, basic) { TEST(args_test, strings_and_refs) { // Unfortunately the tests are compiled with old ABI so strings use COW. - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); char str[] = "1234567890"; store.push_back(str); store.push_back(std::cref(str)); @@ -50,7 +50,7 @@ template <> struct formatter { FMT_END_NAMESPACE TEST(args_test, custom_format) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); auto c = custom_type(); store.push_back(c); ++c.i; @@ -79,7 +79,7 @@ template <> struct formatter { FMT_END_NAMESPACE TEST(args_test, to_string_and_formatter) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); auto s = to_stringable(); store.push_back(s); store.push_back(std::cref(s)); @@ -87,13 +87,13 @@ TEST(args_test, to_string_and_formatter) { } TEST(args_test, named_int) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.push_back(fmt::arg("a1", 42)); EXPECT_EQ("42", fmt::vformat("{a1}", store)); } TEST(args_test, named_strings) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); char str[] = "1234567890"; store.push_back(fmt::arg("a1", str)); store.push_back(fmt::arg("a2", std::cref(str))); @@ -102,7 +102,7 @@ TEST(args_test, named_strings) { } TEST(args_test, named_arg_by_ref) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); char band[] = "Rolling Stones"; store.push_back(fmt::arg("band", std::cref(band))); band[9] = 'c'; // Changing band affects the output. @@ -110,7 +110,7 @@ TEST(args_test, named_arg_by_ref) { } TEST(args_test, named_custom_format) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); auto c = custom_type(); store.push_back(fmt::arg("c1", c)); ++c.i; @@ -123,7 +123,7 @@ TEST(args_test, named_custom_format) { } TEST(args_test, clear) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.push_back(42); auto result = fmt::vformat("{}", store); @@ -140,7 +140,7 @@ TEST(args_test, clear) { } TEST(args_test, reserve) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.reserve(2, 1); store.push_back(1.5f); store.push_back(fmt::arg("a1", 42)); @@ -165,7 +165,7 @@ template <> struct formatter { FMT_END_NAMESPACE TEST(args_test, throw_on_copy) { - fmt::dynamic_format_arg_store store; + auto store = fmt::dynamic_format_arg_store(); store.push_back(std::string("foo")); try { store.push_back(copy_throwable()); @@ -184,3 +184,18 @@ TEST(args_test, move_constructor) { store.reset(); EXPECT_EQ(fmt::vformat("{} {} {a1}", moved_store), "42 foo foo"); } + +TEST(args_test, copy_constructor) { + using store_type = fmt::dynamic_format_arg_store; + auto store = std::unique_ptr(new store_type()); + store->push_back(fmt::arg("test1", "value1")); + store->push_back(fmt::arg("test2", "value2")); + store->push_back(fmt::arg("test3", "value3")); + + auto store2 = std::unique_ptr(new store_type(*store)); + store.reset(); + store2->push_back(fmt::arg("test4", "value4")); + + auto result = fmt::vformat("{test1} {test2} {test3} {test4}", *store2); + EXPECT_EQ(result, "value1 value2 value3 value4"); +}