Commit bc6c1c17 authored by Victor Zverovich's avatar Victor Zverovich

Add support for wide strings.

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