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

User-defined literals for format and named arguments #204

Merged
merged 2 commits into from
Oct 7, 2015

Conversation

dean0x7d
Copy link
Contributor

This is a bit of fun with C++11 user-defined literals. This brings C++ Format a little closer to Python's str.format for literals:

# Python
"{0}{1}{0}".format("abra", "cad")
// C++11 with user-defined literals
"{0}{1}{0}"_format("abra", "cad");

The literal _format just passes the arguments to fmt::format and is equivalent to fmt::format("{0}{1}{0}", "abra", "cad");

Literals can be used with named arguments as well:

# Python
"Hello, {name}! Your number is {number}".format(name="World", number=1);
// C++11 with user-defined literals
"Hello, {name}! Your number is {number}"_format("name"_a="World", "number"_a=1);

The _a literal just constructs a fmt::arg under the hood.

Compiler support for UDLs is required. Minimum Clang 3.1, GCC 4.7 or VS2015.
To use the literals using namespace fmt::literals; will import just _format and _a. But using namespace fmt; is also a possibility.

@vitaut
Copy link
Contributor

vitaut commented Sep 28, 2015

Thanks for the contribution! This looks like a cool even if a bit unconventional use of user-defined literals. I'd like to experiment with this a bit more before merging in, hopefully in the beginning of the next week when I return from the trip I'm currently in.

vitaut added a commit that referenced this pull request Oct 7, 2015
User-defined literals for format and named arguments
@vitaut vitaut merged commit 3b9765f into fmtlib:master Oct 7, 2015
@vitaut
Copy link
Contributor

vitaut commented Oct 7, 2015

@dean0x7d, I get some warnings and errors on GCC 4.8.4 after merging this:

cppformat/test/format-test.cc:1615:427: warning: invalid suffix on literal; C++11 requires a space between literal and identifier [-Wliteral-suffix]
   EXPECT_EQ(format("{first}{second}{first}{third}",
                                                                                                                                                                                                                                                                                                                                                                                                                                           ^
...
cppformat/test/format-test.cc: In member function ‘virtual void LiteralsTest_Format_Test::TestBody()’:
/home/viz/Downloads/3/cppformat/test/format-test.cc:1610:237: error: expected ‘)’ before ‘{’ token
   EXPECT_EQ(format("{}c{}", "ab", 1), "{}c{}"_format("ab", 1));
                                                                                                                                                                                                                                             ^
...

Do you know what might be causing these?

@vitaut
Copy link
Contributor

vitaut commented Oct 7, 2015

I've temporarily moved the changes to the udl branch until the above issue is resolved.

@dean0x7d
Copy link
Contributor Author

dean0x7d commented Oct 7, 2015

This is really strange. I tested this originally with GCC 4.8.4, Clang 3.7 and VS2015. I just tried again with GCC 4.8.4 and I can't reproduce the error. Everything is working fine.

Is it possible that it's some system/library specific preprocessor definition messing with _format?

@vitaut
Copy link
Contributor

vitaut commented Oct 7, 2015

Interestingly, the error only occurs in tests. I can compile and run a small standalone example successfully:

#include "format.h"
using namespace fmt::literals;
int main() {
  fmt::print("The answer is {answer}.", "answer"_a=42);
}

And _format is not defined there - I've just checked with an #ifdef and #error.

@vitaut
Copy link
Contributor

vitaut commented Oct 7, 2015

Looks like a preprocessor problem, because changing the test to

  std::string str = "{}c{}"_format("ab", 1);
  EXPECT_EQ(format("{}c{}", "ab", 1), str);

compiles.

@dean0x7d
Copy link
Contributor Author

dean0x7d commented Oct 7, 2015

The gtest macros pass their arguments both as code and as strings using the macro stringize operator (#). It's possible that's where it trips over the literal syntax. But that's still pretty weird. I haven't been able to reproduce the error either in tests or standalone. I tried with GCC 4.8.4 and 5.2.0.

@vitaut
Copy link
Contributor

vitaut commented Oct 8, 2015

Yeah, looks like a stringification issue. The following code

#define stringify(x) #x
stringify("{}c{}"_format("ab", 1));

is preprocessed to

""{}c{}"_format(\"ab\", 1)";

Note that the double quotes in "{}c{}" are not escaped.

Here's a relevant bug report: https://gcc.gnu.org/ml/gcc-bugs/2015-04/msg02027.html

I guess a simple solution is to assign the result of "..."_format(...) to a string variable and pass it to EXPECT_EQ as I did in my previous comment.

@dean0x7d
Copy link
Contributor Author

dean0x7d commented Oct 9, 2015

I made the required changes to the tests. I haven't been able to reproduce the stringification bug on my end, so I can't actually test the workaround but I think this should be fine.

Is there any way to add the relevant commit to this pull request or should I open a new one?

@vitaut
Copy link
Contributor

vitaut commented Oct 9, 2015

Normally the PR is updated automatically when you push to correspondent branch, but I guess not for merged PRs. So I just pulled the commit from your branch. As expected the tests compile correctly now (why the issue only reproduces on my machine is a mystery). Thanks!

@vitaut
Copy link
Contributor

vitaut commented Oct 9, 2015

@dean0x7d BTW, could you by any chance add some docs describing the new functionality?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants