Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft]Add copy constructor for dynamic_format_arg_store #2832

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions include/fmt/args.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,61 @@ class dynamic_arg_list {
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
virtual std::unique_ptr<node<>> clone() const = 0;
std::unique_ptr<node<>> next;
};

template <typename T> struct typed_node : node<> {
T value;

FMT_CONSTEXPR typed_node(const typed_node<T>& node) : value(node.value) {}

template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}

template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}

std::unique_ptr<node<>> clone() const override {
return std::unique_ptr<typed_node<T>>(new typed_node<T>(*this));
}
};

std::unique_ptr<node<>> 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 <typename T, typename Arg> const T& push(const Arg& arg) {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
Expand Down Expand Up @@ -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<Context>& store)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(),
#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
Expand Down
60 changes: 60 additions & 0 deletions include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1204,6 +1204,63 @@ template <typename Context> class value {
named_arg_value<char_type> named_args;
};

FMT_INLINE value(const value<Context>& 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) {}
Expand Down Expand Up @@ -1548,6 +1605,9 @@ template <typename Context> class basic_format_arg {

constexpr basic_format_arg() : type_(detail::type::none_type) {}

basic_format_arg(const basic_format_arg<Context>& arg)
: value_(arg.value_, arg.type_), type_(arg.type_) {}

constexpr explicit operator bool() const noexcept {
return type_ != detail::type::none_type;
}
Expand Down
37 changes: 26 additions & 11 deletions test/args-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "gtest/gtest.h"

TEST(args_test, basic) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
store.push_back(42);
store.push_back("abc1");
store.push_back(1.5f);
Expand All @@ -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<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
char str[] = "1234567890";
store.push_back(str);
store.push_back(std::cref(str));
Expand Down Expand Up @@ -50,7 +50,7 @@ template <> struct formatter<custom_type> {
FMT_END_NAMESPACE

TEST(args_test, custom_format) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
auto c = custom_type();
store.push_back(c);
++c.i;
Expand Down Expand Up @@ -79,21 +79,21 @@ template <> struct formatter<to_stringable> {
FMT_END_NAMESPACE

TEST(args_test, to_string_and_formatter) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
auto s = to_stringable();
store.push_back(s);
store.push_back(std::cref(s));
fmt::vformat("", store);
}

TEST(args_test, named_int) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
store.push_back(fmt::arg("a1", 42));
EXPECT_EQ("42", fmt::vformat("{a1}", store));
}

TEST(args_test, named_strings) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
char str[] = "1234567890";
store.push_back(fmt::arg("a1", str));
store.push_back(fmt::arg("a2", std::cref(str)));
Expand All @@ -102,15 +102,15 @@ TEST(args_test, named_strings) {
}

TEST(args_test, named_arg_by_ref) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
char band[] = "Rolling Stones";
store.push_back(fmt::arg("band", std::cref(band)));
band[9] = 'c'; // Changing band affects the output.
EXPECT_EQ(fmt::vformat("{band}", store), "Rolling Scones");
}

TEST(args_test, named_custom_format) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
auto c = custom_type();
store.push_back(fmt::arg("c1", c));
++c.i;
Expand All @@ -123,7 +123,7 @@ TEST(args_test, named_custom_format) {
}

TEST(args_test, clear) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
store.push_back(42);

auto result = fmt::vformat("{}", store);
Expand All @@ -140,7 +140,7 @@ TEST(args_test, clear) {
}

TEST(args_test, reserve) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
store.reserve(2, 1);
store.push_back(1.5f);
store.push_back(fmt::arg("a1", 42));
Expand All @@ -165,7 +165,7 @@ template <> struct formatter<copy_throwable> {
FMT_END_NAMESPACE

TEST(args_test, throw_on_copy) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
auto store = fmt::dynamic_format_arg_store<fmt::format_context>();
store.push_back(std::string("foo"));
try {
store.push_back(copy_throwable());
Expand All @@ -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<fmt::format_context>;
auto store = std::unique_ptr<store_type>(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<store_type>(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");
}