I've never been satisfied with any of the possible sprintf()
-like functions in C++:
sprintf()
is a C function and it's very unsafe and dangerous to use (undefined behavior all over the place)boost::format
is too slow and awkward to use (think of the%
syntax)iostream
is extremely awkward to use
So I came up with my own. Those are my requirements:
- Inspired by C#'s format function
- Must never fail at run time, never crash and never have undefined behavior, no matter what you call it with. Should fail at compile time whenever possible instead.
- Must be expandable (if you create a class
Person
, then you can pass aPerson
object to the function) - Must be fast
- The format string must be easy to localize
- The format string must not have placeholders whose syntax depends on the argument type (ie, no
%s
vs%d
) - Must have a fairly "normal" syntax (no % like boost::format)
I'm interested in reviews on the satisfaction of the requirements above. I'm especially interested in reviews from the end-user perspective and less about the internals of the code (i.e., the code that is hidden from the end user).
A few examples:
// Returns a string containing "Hello my name is Andreas and I'm 22 years old".
Format("Hello my name is {0} and I'm {1} years old.", "Andreas", 22);
// Returns "Hex: 0xA"
Format("Hex: {0:x}", 10);
// Fails to compile: Person is not a built in type and doesn't have any function to convert to string
struct Person {};
Person p;
Format("Person: {0}", p);
// "Person: Andreas Bonini" [Note: if it was {0:abc}, then Person::ToString("abc") would have been called]
struct Person {
Person(string first, string last) : First(first), Last(last) {}
string ToString(const string &options) const { return Format("{0} {1}", First, Last); }
string First, Last;
};
Person p("Andreas", "Bonini");
Format("Person: {0}", p);
These are my unit tests:
TEST(Format)
{
CHECK_EQUAL(Format("Testing {0} test {0}{0} test", 123), "Testing 123 test 123123 test");
CHECK_EQUAL(Format("{0}", (const char *)NULL), "{null}");
CHECK_EQUAL(Format("Test double {0} - {1}", 0, 1), "Test double 0 - 1");
CHECK_EQUAL(Format("Test options: {0:x}", 0xABC), "Test options: 0xABC");
CHECK_EQUAL(Format("Test malformed: {0:x", 0xABC), "Test malformed: {0:x");
CHECK_EQUAL(Format("Check stupid: {0:bmkldmbkglmbgk902 r iko4om wkl lfs s,gf, gfsdg fsd ! @ G}", "stupid"), "Check stupid: stupid");
CHECK_EQUAL(Format("Check missing: {1}", 0), "Check missing: {Argument 1}");
CHECK_EQUAL(Format("Master format test {0} {1} {2 {2:2} {3} {4:e} {5:x} {0}", 0, 1234.55566f, 1.11111f, "lolz", "a'b", 0xABCDEFABCDEFULL),
"Master format test 0 1234.56 {2 1.11 lolz 'a\\'b' 0xABCDEFABCDEF 0");
CHECK_EQUAL(Format("{0:x}", 0xFFFFFFFF), "0xFFFFFFFF");
}
Note that due to C++ limitations (lack of variadic templates) the code isn't exactly pretty. Also the code is part of a bigger library so it won't compile of its own.
- Format.h
- Format.cpp
- FormatNoVariadicTemplates.h (ugly part)