mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-31 03:07:36 +02:00
Disallow writing a wide string to a char stream and vice versa, because there is no good way to implement these function without knowing the encodings and what std::ostream doesn't make any sense.
This commit is contained in:
53
format.cc
53
format.cc
@ -73,41 +73,34 @@ inline int IsInf(double x) { return !_finite(x); }
|
|||||||
#define FMT_SNPRINTF sprintf_s
|
#define FMT_SNPRINTF sprintf_s
|
||||||
|
|
||||||
#endif // _MSC_VER
|
#endif // _MSC_VER
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename T>
|
||||||
struct CharTraits;
|
int fmt::internal::CharTraits<char>::FormatFloat(
|
||||||
|
char *buffer, std::size_t size, const char *format,
|
||||||
template <>
|
unsigned width, int precision, T value) {
|
||||||
struct CharTraits<char> {
|
if (width == 0) {
|
||||||
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 ?
|
return precision < 0 ?
|
||||||
FMT_SNPRINTF(buffer, size, format, width, value) :
|
FMT_SNPRINTF(buffer, size, format, value) :
|
||||||
FMT_SNPRINTF(buffer, size, format, width, precision, 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 <>
|
template <typename T>
|
||||||
struct CharTraits<wchar_t> {
|
int fmt::internal::CharTraits<wchar_t>::FormatFloat(
|
||||||
template <typename T>
|
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||||
static int FormatFloat(wchar_t *buffer, std::size_t size,
|
unsigned width, int precision, T value) {
|
||||||
const wchar_t *format, unsigned width, int precision, T value) {
|
if (width == 0) {
|
||||||
if (width == 0) {
|
|
||||||
return precision < 0 ?
|
|
||||||
swprintf(buffer, size, format, value) :
|
|
||||||
swprintf(buffer, size, format, precision, value);
|
|
||||||
}
|
|
||||||
return precision < 0 ?
|
return precision < 0 ?
|
||||||
swprintf(buffer, size, format, width, value) :
|
swprintf(buffer, size, format, value) :
|
||||||
swprintf(buffer, size, format, width, precision, value);
|
swprintf(buffer, size, format, precision, value);
|
||||||
}
|
}
|
||||||
};
|
return precision < 0 ?
|
||||||
|
swprintf(buffer, size, format, width, value) :
|
||||||
|
swprintf(buffer, size, format, width, precision, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char fmt::internal::DIGITS[] =
|
const char fmt::internal::DIGITS[] =
|
||||||
@ -310,7 +303,7 @@ void fmt::BasicWriter<Char>::FormatDouble(
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
std::size_t size = buffer_.capacity() - offset;
|
std::size_t size = buffer_.capacity() - offset;
|
||||||
Char *start = &buffer_[offset];
|
Char *start = &buffer_[offset];
|
||||||
int n = CharTraits<Char>::FormatFloat(
|
int n = internal::CharTraits<Char>::FormatFloat(
|
||||||
start, size, format, width_for_sprintf, precision, value);
|
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) {
|
||||||
|
33
format.h
33
format.h
@ -148,6 +148,27 @@ void Array<T, SIZE>::append(const T *begin, const T *end) {
|
|||||||
size_ += num_elements;
|
size_ += num_elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct CharTraits;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct CharTraits<char> {
|
||||||
|
typedef wchar_t UnsupportedType;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static int FormatFloat(char *buffer, std::size_t size,
|
||||||
|
const char *format, unsigned width, int precision, T value);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct CharTraits<wchar_t> {
|
||||||
|
typedef char UnsupportedType;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static int FormatFloat(wchar_t *buffer, std::size_t size,
|
||||||
|
const wchar_t *format, unsigned width, int precision, T value);
|
||||||
|
};
|
||||||
|
|
||||||
// Information about an integer type.
|
// Information about an integer type.
|
||||||
// IntTraits is not specialized for integer types smaller than int,
|
// IntTraits is not specialized for integer types smaller than int,
|
||||||
// since these are promoted to int.
|
// since these are promoted to int.
|
||||||
@ -501,6 +522,12 @@ class BasicWriter {
|
|||||||
CharPtr FormatString(const StringChar *s,
|
CharPtr FormatString(const StringChar *s,
|
||||||
std::size_t size, const FormatSpec &spec);
|
std::size_t size, const FormatSpec &spec);
|
||||||
|
|
||||||
|
// This method is private to disallow writing a wide string to a
|
||||||
|
// char stream and vice versa. If you want to print a wide string
|
||||||
|
// as a pointer as std::ostream does, cast it to const void*.
|
||||||
|
// Do not implement!
|
||||||
|
void operator<<(const typename internal::CharTraits<Char>::UnsupportedType *);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
Returns the number of characters written to the output buffer.
|
Returns the number of characters written to the output buffer.
|
||||||
@ -596,13 +623,13 @@ class BasicWriter {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicWriter &operator<<(Char value) {
|
BasicWriter &operator<<(char value) {
|
||||||
*GrowBuffer(1) = value;
|
*GrowBuffer(1) = value;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicWriter &operator<<(const fmt::StringRef value) {
|
BasicWriter &operator<<(const fmt::BasicStringRef<Char> value) {
|
||||||
const char *str = value.c_str();
|
const Char *str = value.c_str();
|
||||||
std::size_t size = value.size();
|
std::size_t size = value.size();
|
||||||
std::copy(str, str + size, GrowBuffer(size));
|
std::copy(str, str + size, GrowBuffer(size));
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -89,22 +89,50 @@ using fmt::pad;
|
|||||||
FORMAT_TEST_THROW_(statement, expected_exception, expected_message, \
|
FORMAT_TEST_THROW_(statement, expected_exception, expected_message, \
|
||||||
GTEST_NONFATAL_FAILURE_)
|
GTEST_NONFATAL_FAILURE_)
|
||||||
|
|
||||||
struct WriteChecker {
|
// Checks if writing value to BasicWriter<Char> produces the same result
|
||||||
|
// as writing it to std::basic_ostringstream<Char>.
|
||||||
|
template <typename Char, typename T>
|
||||||
|
static ::testing::AssertionResult CheckWrite(const T &value, const char *type) {
|
||||||
|
std::basic_ostringstream<Char> os;
|
||||||
|
os << value;
|
||||||
|
std::basic_string<Char> expected = os.str();
|
||||||
|
std::basic_string<Char> actual = str(BasicWriter<Char>() << value);
|
||||||
|
if (expected == actual)
|
||||||
|
return ::testing::AssertionSuccess();
|
||||||
|
return ::testing::AssertionFailure()
|
||||||
|
<< "Value of: str(Writer<" << type << ">() << value)\n"
|
||||||
|
<< " Actual: " << actual << "\n"
|
||||||
|
<< "Expected: " << expected << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AnyWriteChecker {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
::testing::AssertionResult operator()(const char *, const T &value) const {
|
::testing::AssertionResult operator()(const char *, const T &value) const {
|
||||||
std::ostringstream os;
|
::testing::AssertionResult result = CheckWrite<char>(value, "char");
|
||||||
os << value;
|
return result ? CheckWrite<wchar_t>(value, "wchar_t") : result;
|
||||||
std::string expected = os.str(), actual = str(Writer() << value);
|
|
||||||
if (expected == actual)
|
|
||||||
return ::testing::AssertionSuccess();
|
|
||||||
return ::testing::AssertionFailure()
|
|
||||||
<< "Value of: str(Writer() << value)\n"
|
|
||||||
<< " Actual: " << actual << "\n"
|
|
||||||
<< "Expected: " << expected << "\n";
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CHECK_WRITE(value) ASSERT_PRED_FORMAT1(WriteChecker(), value)
|
struct CharWriteChecker {
|
||||||
|
template <typename T>
|
||||||
|
::testing::AssertionResult operator()(const char *, const T &value) const {
|
||||||
|
return CheckWrite<char>(value, "char");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WCharWriteChecker {
|
||||||
|
template <typename T>
|
||||||
|
::testing::AssertionResult operator()(const char *, const T &value) const {
|
||||||
|
return CheckWrite<wchar_t>(value, "char");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if writing value to BasicWriter produces the same result
|
||||||
|
// as writing it to std::ostringstream both for char and wchar_t.
|
||||||
|
#define CHECK_WRITE(value) ASSERT_PRED_FORMAT1(AnyWriteChecker(), value)
|
||||||
|
|
||||||
|
#define CHECK_WRITE_CHAR(value) ASSERT_PRED_FORMAT1(CharWriteChecker(), value)
|
||||||
|
#define CHECK_WRITE_WCHAR(value) ASSERT_PRED_FORMAT1(WCharWriteChecker(), value)
|
||||||
|
|
||||||
// Increment a number in a string.
|
// Increment a number in a string.
|
||||||
void Increment(char *s) {
|
void Increment(char *s) {
|
||||||
@ -279,8 +307,20 @@ TEST(WriterTest, WriteDouble) {
|
|||||||
CHECK_WRITE(4.2l);
|
CHECK_WRITE(4.2l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(WriterTest, WriteChar) {
|
||||||
|
CHECK_WRITE('a');
|
||||||
|
}
|
||||||
|
|
||||||
TEST(WriterTest, WriteString) {
|
TEST(WriterTest, WriteString) {
|
||||||
CHECK_WRITE("abc");
|
CHECK_WRITE_CHAR("abc");
|
||||||
|
// The following line shouldn't compile:
|
||||||
|
//fmt::Writer() << L"abc";
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(WriterTest, WriteWideString) {
|
||||||
|
CHECK_WRITE_WCHAR(L"abc");
|
||||||
|
// The following line shouldn't compile:
|
||||||
|
//fmt::WWriter() << "abc";
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, oct) {
|
TEST(WriterTest, oct) {
|
||||||
|
Reference in New Issue
Block a user