-
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 arguments #169
Conversation
Wow, really impressive stuff! On a cursory glance, the implementation seems very reasonable, but I'd like to look into it in more details before merging. Does the name map handling add a lot of overhead? |
I don't have any benchmark so I can't tell the numbers, but hopefully it won't add much overhead. What it does is simple: The args are distinguished into normal args and named args by |
I very much like the feature, but building maps of names arguments adds quite a bit to the compiled code size (bloat-test from format-benchmark) even when not using them: Without named arguments: 34912 bytes Could you move map construction from the individual formatting functions to enum Type {
// ...
CSTRING, STRING, WSTRING, POINTER, CUSTOM,
NAMED_ARG
}; The real type will have to go to Then the name map can be constructed dynamically in Hope this makes sense. |
Here's the result I got with MSVC2015 (x64): Without named arguments: 98,304 bytes Not a big improvement :/ |
I meant moving not just map sorting but all map management to template <typename... Args> \
void func(arg_type arg0, const Args & ... args) { \
typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \
const int count = fmt::internal::NamedArgsCounter<Args...>::value; \
typename fmt::internal::BasicArgMap<Char>::value_type mapArray[count + 1]; \
fmt::internal::add_named_args<0>(mapArray, args...); \
fmt::internal::BasicArgMap<Char> map(mapArray, count); \
func(arg0, fmt::internal::make_arg_list<Char>(array, &map, fmt::internal::strip_name(args)...)); \
} becomes template <typename... Args> \
void func(arg_type arg0, const Args & ... args) { \
typename fmt::internal::ArgArray<sizeof...(Args)>::Type array; \
func(arg0, fmt::internal::make_arg_list<Char>(array, args...)); \
} as it used to be. Then the compiled code size shouldn't increase (for user code). It won't be possible to fully allocate map on stack, but I think it's not critical. If desired this can be optimized by allocating some map elements on stack. |
There are some problems:
Note that the only "map management" thing left in the code was So...I don't think you can magically add names without paying something here. |
Good points. Let try to answer and clarify my suggestion a bit:
This can be addressed by making struct NamedArg {
StringRef name;
Arg arg;
};
NamedArg arg(StringRef name, T const& value) {
NamedArg named_arg;
named_arg.name = name;
named_arg.arg = MakeValue<char>(value);
named_arg.arg.type = make_type(value);
return named_arg;
} Then the type information will be captured by the
Names can be stored in temporary
You are right, the storage strategy depends on
Yes, but only if there are named arguments, so there will be no or little overhead when they are not used.
This is a bit more complicated than it seems. I haven't looked into it in details, but even adding a member to If it sounds too complicated or unclear, I can try implementing the idea with additional |
Hmm, this seems recursive, |
Yes, but it's not a problem as fmt::format("{}", fmt::arg("x", fmt::arg("y", ...))); which can be rejected at compile time. |
Conflicts: format.cc test/format-test.cc
} | ||
EXPECT_THROW_MSG(format("{0:{1}}", 0, (INT_MAX + 1ul)), | ||
FormatError, "number is too big"); | ||
|
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.
Why are the tests above removed?
Thanks for making the changes, the compiled code size is back to 34912 which is great! Two minor comments:
Otherwise this looks good and I'm happy to merge this in. |
And one more thing: could you squash the WIP commits to keep the history clean? Thanks! |
|
Answers to this SO question might help.
I guess conversion is the only way with |
Close this for a new PR. |
Allows code like below: