mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 10:47:35 +02:00
Add support for wide strings.
This commit is contained in:
@ -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.
|
||||||
|
|
||||||
|
122
format.h
122
format.h
@ -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;
|
|||||||
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 {
|
|||||||
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(
|
|||||||
|
|
||||||
// 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(
|
|||||||
// 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 {
|
|||||||
// 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 {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
|
|
||||||
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> {
|
|||||||
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> {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
// 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 {
|
|||||||
// 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 {
|
|||||||
|
|
||||||
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
|||||||
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() {
|
|||||||
|
|
||||||
// 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) {
|
|||||||
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}:%")
|
||||||
|
Reference in New Issue
Block a user