diff --git a/README.rst b/README.rst
index a55318f1..714c5cbd 100644
--- a/README.rst
+++ b/README.rst
@@ -34,6 +34,7 @@ Features
for older compilers.
* Clean warning-free codebase even on high warning levels
(-Wall -Wextra -pedantic).
+* Support for wide strings.
See the `documentation `__ for more details.
diff --git a/format.h b/format.h
index c4900217..d32ea51b 100644
--- a/format.h
+++ b/format.h
@@ -105,6 +105,41 @@ inline int IsInf(double x) { return !_finite(x); }
#endif // _MSC_VER
+template
+struct CharTraits;
+
+template <>
+struct CharTraits {
+ template
+ static int FormatFloat(char *buffer, std::size_t size,
+ const char *format, unsigned width, int precision, T value) {
+ if (width == 0) {
+ return precision < 0 ?
+ FMT_SNPRINTF(buffer, size, format, value) :
+ FMT_SNPRINTF(buffer, size, format, precision, value);
+ }
+ return precision < 0 ?
+ FMT_SNPRINTF(buffer, size, format, width, value) :
+ FMT_SNPRINTF(buffer, size, format, width, precision, value);
+ }
+};
+
+template <>
+struct CharTraits {
+ template
+ static int FormatFloat(wchar_t *buffer, std::size_t size,
+ const wchar_t *format, unsigned width, int precision, T value) {
+ if (width == 0) {
+ return precision < 0 ?
+ swprintf(buffer, size, format, value) :
+ swprintf(buffer, size, format, precision, value);
+ }
+ return precision < 0 ?
+ swprintf(buffer, size, format, width, value) :
+ swprintf(buffer, size, format, width, precision, value);
+ }
+};
+
// A simple array for POD types with the first SIZE elements stored in
// the object itself. It supports a subset of std::vector's operations.
template
@@ -244,9 +279,10 @@ class FormatterProxy;
Format(Format("{{}}")) << 42;
\endrst
*/
-class StringRef {
+template
+class BasicStringRef {
private:
- const char *data_;
+ const Char *data_;
mutable std::size_t size_;
public:
@@ -255,32 +291,38 @@ class StringRef {
If `size` is zero, which is the default, the size is computed with
`strlen`.
*/
- StringRef(const char *s, std::size_t size = 0) : data_(s), size_(size) {}
+ BasicStringRef(const Char *s, std::size_t size = 0) : data_(s), size_(size) {}
/**
Constructs a string reference from an `std::string` object.
*/
- StringRef(const std::string &s) : data_(s.c_str()), size_(s.size()) {}
+ BasicStringRef(const std::basic_string &s)
+ : data_(s.c_str()), size_(s.size()) {}
/**
Converts a string reference to an `std::string` object.
*/
- operator std::string() const { return std::string(data_, size()); }
+ operator std::basic_string() const {
+ return std::basic_string(data_, size());
+ }
/**
Returns the pointer to a C string.
*/
- const char *c_str() const { return data_; }
+ const Char *c_str() const { return data_; }
/**
Returns the string size.
*/
std::size_t size() const {
- if (size_ == 0) size_ = std::strlen(data_);
+ if (size_ == 0) size_ = std::char_traits::length(data_);
return size_;
}
};
+typedef BasicStringRef StringRef;
+typedef BasicStringRef WStringRef;
+
class FormatError : public std::runtime_error {
public:
explicit FormatError(const std::string &message)
@@ -729,8 +771,8 @@ void BasicWriter::FormatDouble(
// Build format string.
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
- char format[MAX_FORMAT_SIZE];
- char *format_ptr = format;
+ Char format[MAX_FORMAT_SIZE];
+ Char *format_ptr = format;
*format_ptr++ = '%';
unsigned width_for_sprintf = width;
if (spec.hash_flag())
@@ -755,18 +797,9 @@ void BasicWriter::FormatDouble(
// Format using snprintf.
for (;;) {
std::size_t size = buffer_.capacity() - offset;
- int n = 0;
Char *start = &buffer_[offset];
- if (width_for_sprintf == 0) {
- n = precision < 0 ?
- FMT_SNPRINTF(start, size, format, value) :
- FMT_SNPRINTF(start, size, format, precision, value);
- } else {
- n = precision < 0 ?
- FMT_SNPRINTF(start, size, format, width_for_sprintf, value) :
- FMT_SNPRINTF(start, size, format, width_for_sprintf,
- precision, value);
- }
+ int n = internal::CharTraits::FormatFloat(
+ start, size, format, width_for_sprintf, precision, value);
if (n >= 0 && offset + n < buffer_.capacity()) {
if (sign) {
if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
@@ -953,7 +986,8 @@ class BasicFormatter {
// This method is private to disallow formatting of wide characters.
// If you want to output a wide character cast it to integer type.
// Do not implement!
- Arg(wchar_t value);
+ // TODO
+ //Arg(wchar_t value);
public:
Type type;
@@ -1048,14 +1082,14 @@ class BasicFormatter {
args_.push_back(&arg);
}
- void ReportError(const char *s, StringRef message) const;
+ void ReportError(const Char *s, StringRef message) const;
- unsigned ParseUInt(const char *&s) const;
+ unsigned ParseUInt(const Char *&s) const;
// Parses argument index and returns an argument with this index.
- const Arg &ParseArgIndex(const char *&s);
+ const Arg &ParseArgIndex(const Char *&s);
- void CheckSign(const char *&s, const Arg &arg);
+ void CheckSign(const Char *&s, const Arg &arg);
void DoFormat();
@@ -1122,14 +1156,8 @@ inline std::basic_string str(const BasicWriter &f) {
template
inline const Char *c_str(const BasicWriter &f) { return f.c_str(); }
-std::string str(internal::FormatterProxy p);
-const char *c_str(internal::FormatterProxy p);
-
namespace internal {
-using fmt::str;
-using fmt::c_str;
-
template
class FormatterProxy {
private:
@@ -1160,6 +1188,14 @@ inline const char *c_str(internal::FormatterProxy p) {
return p.Format()->c_str();
}
+inline std::wstring str(internal::FormatterProxy p) {
+ return p.Format()->str();
+}
+
+inline const wchar_t *c_str(internal::FormatterProxy p) {
+ return p.Format()->c_str();
+}
+
/**
A formatting action that does nothing.
*/
@@ -1181,7 +1217,7 @@ class NoAction {
struct PrintError {
void operator()(const fmt::Writer &w) const {
- std::cerr << "Error: " << w.str() << std::endl;
+ fmt::Print("Error: {}\n") << w.str();
}
};
@@ -1204,10 +1240,10 @@ class Formatter : private Action, public BasicFormatter {
Formatter& operator=(const Formatter &);
struct Proxy {
- const char *format;
+ const Char *format;
Action action;
- Proxy(const char *fmt, Action a) : format(fmt), action(a) {}
+ Proxy(const Char *fmt, Action a) : format(fmt), action(a) {}
};
public:
@@ -1220,7 +1256,7 @@ class Formatter : private Action, public BasicFormatter {
examples of action classes.
\endrst
*/
- explicit Formatter(StringRef format, Action a = Action())
+ explicit Formatter(BasicStringRef format, Action a = Action())
: Action(a), BasicFormatter(writer_, format.c_str()),
inactive_(false) {
}
@@ -1277,6 +1313,10 @@ inline Formatter<> Format(StringRef format) {
return Formatter<>(format);
}
+inline Formatter Format(WStringRef format) {
+ return Formatter(format);
+}
+
/** A formatting action that writes formatted output to stdout. */
class Write {
public:
@@ -1297,7 +1337,7 @@ inline Formatter Print(StringRef format) {
// FormatError reporting unmatched '{'. The idea is that unmatched '{'
// should override other errors.
template
-void BasicFormatter::ReportError(const char *s, StringRef message) const {
+void BasicFormatter::ReportError(const Char *s, StringRef message) const {
for (int num_open_braces = num_open_braces_; *s; ++s) {
if (*s == '{') {
++num_open_braces;
@@ -1312,7 +1352,7 @@ void BasicFormatter::ReportError(const char *s, StringRef message) const {
// Parses an unsigned integer advancing s to the end of the parsed input.
// This function assumes that the first character of s is a digit.
template
-unsigned BasicFormatter::ParseUInt(const char *&s) const {
+unsigned BasicFormatter::ParseUInt(const Char *&s) const {
assert('0' <= *s && *s <= '9');
unsigned value = 0;
do {
@@ -1326,7 +1366,7 @@ unsigned BasicFormatter::ParseUInt(const char *&s) const {
template
inline const typename BasicFormatter::Arg
- &BasicFormatter::ParseArgIndex(const char *&s) {
+ &BasicFormatter::ParseArgIndex(const Char *&s) {
unsigned arg_index = 0;
if (*s < '0' || *s > '9') {
if (*s != '}' && *s != ':')
@@ -1350,7 +1390,7 @@ inline const typename BasicFormatter::Arg
}
template
-void BasicFormatter::CheckSign(const char *&s, const Arg &arg) {
+void BasicFormatter::CheckSign(const Char *&s, const Arg &arg) {
if (arg.type > LAST_NUMERIC_TYPE) {
ReportError(s,
Format("format specifier '{0}' requires numeric argument") << *s);
@@ -1369,7 +1409,7 @@ void BasicFormatter::DoFormat() {
next_arg_index_ = 0;
const Char *s = start;
typedef internal::Array::INLINE_BUFFER_SIZE> Buffer;
- Writer &writer = *writer_;
+ BasicWriter &writer = *writer_;
while (*s) {
char c = *s++;
if (c != '{' && c != '}') continue;
@@ -1392,7 +1432,7 @@ void BasicFormatter::DoFormat() {
// Parse fill and alignment.
if (char c = *s) {
- const char *p = s + 1;
+ const Char *p = s + 1;
spec.align_ = ALIGN_DEFAULT;
do {
switch (*p) {
diff --git a/format_test.cc b/format_test.cc
index 6c28e1dd..2c82be97 100644
--- a/format_test.cc
+++ b/format_test.cc
@@ -1028,6 +1028,10 @@ TEST(FormatterTest, CustomFormat) {
EXPECT_EQ("42", str(Format("{0}") << Answer()));
}
+TEST(FormatterTest, WideFormatString) {
+ EXPECT_EQ(L"42", str(Format(L"{}") << 42));
+}
+
TEST(FormatterTest, FormatStringFromSpeedTest) {
EXPECT_EQ("1.2340000000:0042:+3.13:str:0x3e8:X:%",
str(Format("{0:0.10f}:{1:04}:{2:+g}:{3}:{4}:{5}:%")