-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Support named args in dynamic_format_arg_store (#1655). #1663
Merged
Merged
Changes from all commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
7a1a67f
Dynamic arguments storage. Implementation of enhancement from issue
vsolontsov-ll 833bfe3
Finxing build issues. (Not complete)
vsolontsov-ll c3c6d0c
Fixed a lifetime issue in test and minor build fixes.
vsolontsov-ll 448fb74
MSVC2015 fix
vsolontsov-ll 5930040
Clang-format applied.
vsolontsov-ll 7433fd5
Merge branch 'master' of https://github.com/fmtlib/fmt.git
vsolontsov-ll f0984c4
Addressed minor issues, removed named_arg support.
vsolontsov-ll 107f9a8
Custom list -- first cut.
vsolontsov-ll 4351334
Update dyn-args.h
vsolontsov-ll 90219a7
Build fixes
vsolontsov-ll bee2b5f
Preliminary cleanup.
vsolontsov-ll c2c5c5c
Merge branch 'master' of https://github.com/fmtlib/fmt.git
vsolontsov-ll 6e047c2
minor (unused alias in test)
vsolontsov-ll 70bc839
merge dyn-args.h to core.h
vsolontsov-ll def562c
minor fixes
vsolontsov-ll 49a46f8
Removed conditional compilation as there's already the
vsolontsov-ll 863aa63
trying to fix MSVC
vsolontsov-ll 473ba57
Fixed comments
vsolontsov-ll ae512cd
Fixes after review
vsolontsov-ll baae799
Merge remote-tracking branch 'zverovich/master'
vsolontsov-ll c902f34
Support named args in dynamic_format_arg_store (#1655).
vsolontsov-ll efbcb27
fix build: exceptions and cast...
vsolontsov-ll e46c0a4
Fix namespace issue around exceptions marcos for MSVC
vsolontsov-ll 6199e2a
Named arguments. Better wupport for std::reference_wrapper.
vsolontsov-ll ced27f1
Fix review comments
vsolontsov-ll d2a2c52
Fix gcc 4.8 build
vsolontsov-ll 5f6f93b
Minor fixes.
vsolontsov-ll 00b6883
Review fixes.
vsolontsov-ll File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -629,6 +629,7 @@ using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>; | |
|
||
template <typename Context> class basic_format_arg; | ||
template <typename Context> class basic_format_args; | ||
template <typename Context> class dynamic_format_arg_store; | ||
|
||
// A formatter for objects of type T. | ||
template <typename T, typename Char = char, typename Enable = void> | ||
|
@@ -1131,6 +1132,7 @@ template <typename Context> class basic_format_arg { | |
|
||
friend class basic_format_args<Context>; | ||
friend class internal::arg_map<Context>; | ||
friend class dynamic_format_arg_store<Context>; | ||
|
||
using char_type = typename Context::char_type; | ||
|
||
|
@@ -1269,10 +1271,14 @@ inline basic_format_arg<Context> make_arg(const T& value) { | |
} | ||
|
||
template <typename T> struct is_reference_wrapper : std::false_type {}; | ||
|
||
template <typename T> | ||
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {}; | ||
|
||
template <typename T> const T& unwrap(const T& v) { return v; } | ||
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) { | ||
return static_cast<const T&>(v); | ||
} | ||
|
||
class dynamic_arg_list { | ||
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for | ||
// templates it doesn't complain about inability to deduce single translation | ||
|
@@ -1419,6 +1425,50 @@ inline format_arg_store<Context, Args...> make_format_args( | |
return {args...}; | ||
} | ||
|
||
namespace internal { | ||
template <typename Char> struct named_arg_base { | ||
const Char* name; | ||
|
||
// Serialized value<context>. | ||
mutable char data[sizeof(basic_format_arg<buffer_context<Char>>)]; | ||
|
||
named_arg_base(const Char* nm) : name(nm) {} | ||
|
||
template <typename Context> basic_format_arg<Context> deserialize() const { | ||
basic_format_arg<Context> arg; | ||
std::memcpy(&arg, data, sizeof(basic_format_arg<Context>)); | ||
return arg; | ||
} | ||
}; | ||
|
||
struct view {}; | ||
|
||
template <typename T, typename Char> | ||
struct named_arg : view, named_arg_base<Char> { | ||
const T& value; | ||
|
||
named_arg(const Char* name, const T& val) | ||
: named_arg_base<Char>(name), value(val) {} | ||
}; | ||
|
||
} // namespace internal | ||
|
||
/** | ||
\rst | ||
Returns a named argument to be used in a formatting function. It should only | ||
be used in a call to a formatting function. | ||
|
||
**Example**:: | ||
|
||
fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); | ||
\endrst | ||
*/ | ||
template <typename Char, typename T> | ||
inline internal::named_arg<T, Char> arg(const Char* name, const T& arg) { | ||
static_assert(!internal::is_named_arg<T>(), "nested named arguments"); | ||
return {name, arg}; | ||
} | ||
|
||
/** | ||
\rst | ||
A dynamic version of `fmt::format_arg_store<>`. | ||
|
@@ -1449,8 +1499,7 @@ class dynamic_format_arg_store | |
std::is_same<T, internal::std_string_view<char_type>>::value || | ||
(mapped_type != internal::type::cstring_type && | ||
mapped_type != internal::type::string_type && | ||
mapped_type != internal::type::custom_type && | ||
mapped_type != internal::type::named_arg_type)) | ||
mapped_type != internal::type::custom_type)) | ||
}; | ||
}; | ||
|
||
|
@@ -1460,6 +1509,7 @@ class dynamic_format_arg_store | |
|
||
// Storage of basic_format_arg must be contiguous. | ||
std::vector<basic_format_arg<Context>> data_; | ||
std::vector<internal::named_arg_info<char_type>> named_info_; | ||
|
||
// Storage of arguments not fitting into basic_format_arg must grow | ||
// without relocation because items in data_ refer to it. | ||
|
@@ -1468,13 +1518,38 @@ class dynamic_format_arg_store | |
friend class basic_format_args<Context>; | ||
|
||
unsigned long long get_types() const { | ||
return internal::is_unpacked_bit | data_.size(); | ||
return internal::is_unpacked_bit | data_.size() | | ||
(named_info_.empty() ? 0ULL | ||
: static_cast<unsigned long long>( | ||
internal::has_named_args_bit)); | ||
} | ||
|
||
const basic_format_arg<Context>* data() const { | ||
return named_info_.empty() ? data_.data() : data_.data() + 1; | ||
} | ||
|
||
template <typename T> void emplace_arg(const T& arg) { | ||
data_.emplace_back(internal::make_arg<Context>(arg)); | ||
} | ||
|
||
template <typename T> | ||
void emplace_arg(const internal::named_arg<T, char_type>& arg) { | ||
if (named_info_.empty()) { | ||
constexpr const internal::named_arg_info<char_type>* zero_ptr{nullptr}; | ||
data_.insert(data_.begin(), {zero_ptr, 0}); | ||
} | ||
data_.emplace_back( | ||
internal::make_arg<Context>(internal::unwrap(arg.value))); | ||
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) { | ||
data->pop_back(); | ||
}; | ||
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)> | ||
guard{&data_, pop_one}; | ||
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)}); | ||
data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; | ||
guard.release(); | ||
} | ||
|
||
public: | ||
/** | ||
\rst | ||
|
@@ -1500,19 +1575,54 @@ class dynamic_format_arg_store | |
if (internal::const_check(need_copy<T>::value)) | ||
emplace_arg(dynamic_args_.push<stored_type<T>>(arg)); | ||
else | ||
emplace_arg(arg); | ||
emplace_arg(internal::unwrap(arg)); | ||
} | ||
|
||
/** | ||
\rst | ||
Adds a reference to the argument into the dynamic store for later passing to | ||
a formating function. | ||
a formating function. Supports named arguments wrapped in | ||
std::reference_wrapper (via std::ref()/std::cref()). | ||
|
||
**Example**:: | ||
fmt::dynamic_format_arg_store<fmt::format_context> store; | ||
char str[] = "1234567890"; | ||
store.push_back(std::cref(str)); | ||
int a1_val{42}; | ||
auto a1 = fmt::arg("a1_", a1_val); | ||
store.push_back(std::cref(a1)); | ||
|
||
// Changing str affects the output but only for string and custom types. | ||
str[0] = 'X'; | ||
|
||
std::string result = fmt::vformat("{} and {a1_}"); | ||
assert(result == "X234567890 and 42"); | ||
\endrst | ||
*/ | ||
template <typename T> void push_back(std::reference_wrapper<T> arg) { | ||
static_assert( | ||
need_copy<T>::value, | ||
internal::is_named_arg<typename std::remove_cv<T>::type>::value || | ||
need_copy<T>::value, | ||
"objects of built-in types and string views are always copied"); | ||
emplace_arg(arg.get()); | ||
} | ||
|
||
/** | ||
Adds named argument into the dynamic store for later passing to a formating | ||
function. std::reference_wrapper is supported to avoid copying of the | ||
argument. | ||
*/ | ||
template <typename T> | ||
void push_back(const internal::named_arg<T, char_type>& arg) { | ||
const char_type* arg_name = | ||
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str(); | ||
if (internal::const_check(need_copy<T>::value)) { | ||
emplace_arg( | ||
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value))); | ||
} else { | ||
emplace_arg(fmt::arg(arg_name, arg.value)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: please wrap the else condition in a compound statement ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
} | ||
} | ||
}; | ||
|
||
/** | ||
|
@@ -1597,7 +1707,7 @@ template <typename Context> class basic_format_args { | |
\endrst | ||
*/ | ||
FMT_INLINE basic_format_args(const dynamic_format_arg_store<Context>& store) | ||
: basic_format_args(store.get_types(), store.data_.data()) {} | ||
: basic_format_args(store.get_types(), store.data()) {} | ||
|
||
/** | ||
\rst | ||
|
@@ -1659,31 +1769,6 @@ template <typename Container> | |
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>> | ||
: is_contiguous<Container> {}; | ||
|
||
template <typename Char> struct named_arg_base { | ||
const Char* name; | ||
|
||
// Serialized value<context>. | ||
mutable char data[sizeof(basic_format_arg<buffer_context<Char>>)]; | ||
|
||
named_arg_base(const Char* nm) : name(nm) {} | ||
|
||
template <typename Context> basic_format_arg<Context> deserialize() const { | ||
basic_format_arg<Context> arg; | ||
std::memcpy(&arg, data, sizeof(basic_format_arg<Context>)); | ||
return arg; | ||
} | ||
}; | ||
|
||
struct view {}; | ||
|
||
template <typename T, typename Char> | ||
struct named_arg : view, named_arg_base<Char> { | ||
const T& value; | ||
|
||
named_arg(const Char* name, const T& val) | ||
: named_arg_base<Char>(name), value(val) {} | ||
}; | ||
|
||
// Reports a compile-time error if S is not a valid format string. | ||
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)> | ||
FMT_INLINE void check_format_string(const S&) { | ||
|
@@ -1727,22 +1812,6 @@ inline void vprint_mojibake(std::FILE*, string_view, format_args) {} | |
#endif | ||
} // namespace internal | ||
|
||
/** | ||
\rst | ||
Returns a named argument to be used in a formatting function. It should only | ||
be used in a call to a formatting function. | ||
|
||
**Example**:: | ||
|
||
fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); | ||
\endrst | ||
*/ | ||
template <typename Char, typename T> | ||
inline internal::named_arg<T, Char> arg(const Char* name, const T& arg) { | ||
static_assert(!internal::is_named_arg<T>(), "nested named arguments"); | ||
return {name, arg}; | ||
} | ||
|
||
/** Formats a string and writes the output to ``out``. */ | ||
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with | ||
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes,
basic_format_arg
is constructed fromdynamic_format_arg_store::emplace_arg()
and needs to access private ctor. Also there's a need to updatenamed_args
with new size and pointer to array ofnamed_arg_info
after potential relocation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done