Move ostream support to ostream.{h,cc}

This commit is contained in:
Victor Zverovich 2016-05-06 07:37:20 -07:00
parent 041725e9d0
commit 90730e706b
12 changed files with 423 additions and 306 deletions

View File

@ -1,12 +1,12 @@
# Define the fmt library, its includes and the needed defines.
# format.cc is added to FMT_HEADERS for the header-only configuration.
set(FMT_HEADERS format.h format.cc time.h)
set(FMT_HEADERS format.h format.cc ostream.h ostream.cc time.h)
if (HAVE_OPEN)
set(FMT_HEADERS ${FMT_HEADERS} posix.h)
set(FMT_SOURCES ${FMT_SOURCES} posix.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS})
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} ../ChangeLog.rst)
option(FMT_CPPFORMAT "Build cppformat library for backward compatibility." OFF)
if (FMT_CPPFORMAT)

View File

@ -60,12 +60,6 @@ using fmt::internal::Arg;
# define FMT_CATCH(x) if (false)
#endif
#ifdef FMT_HEADER_ONLY
# define FMT_FUNC inline
#else
# define FMT_FUNC
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
@ -361,21 +355,6 @@ class CharConverter : public ArgVisitor<CharConverter, void> {
arg_.int_value = static_cast<char>(value);
}
};
// Write the content of w to os.
void write(std::ostream &os, Writer &w) {
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
size -= n;
} while (size != 0);
}
} // namespace
namespace internal {
@ -896,13 +875,6 @@ FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void fmt::print(std::ostream &os, CStringRef format_str,
ArgList args) {
MemoryWriter w;
w.write(format_str, args);
write(os, w);
}
FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
@ -918,13 +890,6 @@ FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) {
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
FMT_FUNC int fmt::fprintf(std::ostream &os, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
write(os, w);
return static_cast<int>(w.size());
}
#ifndef FMT_HEADER_ONLY
template struct fmt::internal::BasicData<void>;

View File

@ -40,14 +40,6 @@
#include <vector>
#include <utility>
#ifndef FMT_USE_IOSTREAMS
# define FMT_USE_IOSTREAMS 1
#endif
#if FMT_USE_IOSTREAMS
# include <ostream>
#endif
#ifdef _SECURE_SCL
# define FMT_SECURE_SCL _SECURE_SCL
#else
@ -387,10 +379,6 @@ template <typename CharType,
typename ArgFormatter = fmt::ArgFormatter<CharType> >
class BasicFormatter;
template <typename Char, typename ArgFormatter, typename T>
void format(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value);
/**
\rst
A string reference. It can be constructed from a C string or ``std::string``.
@ -1057,33 +1045,16 @@ struct WCharHelper<T, wchar_t> {
typedef char Yes[1];
typedef char No[2];
// These are non-members to workaround an overload resolution bug in bcc32.
Yes &convert(fmt::ULongLong);
Yes &convert(std::ostream &);
No &convert(...);
template <typename T>
T &get();
struct DummyStream : std::ostream {
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
};
No &operator<<(std::ostream &, int);
// These are non-members to workaround an overload resolution bug in bcc32.
Yes &convert(fmt::ULongLong);
No &convert(...);
template<typename T, bool ENABLE_CONVERSION>
struct ConvertToIntImpl {
enum { value = false };
};
template<typename T>
struct ConvertToIntImpl<T, true> {
// Convert to int only if T doesn't have an overloaded operator<<.
enum {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
enum { value = ENABLE_CONVERSION };
};
template<typename T, bool ENABLE_CONVERSION>
@ -2140,38 +2111,6 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
(t12.type << 48) | (t13.type << 52) | (t14.type << 56);
}
#endif
template <class Char>
class FormatBuf : public std::basic_streambuf<Char> {
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
Char *start_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) {
this->setp(start_, start_ + buffer_.capacity());
}
int_type overflow(int_type ch = traits_type::eof()) {
if (!traits_type::eq_int_type(ch, traits_type::eof())) {
size_t size = this->size();
buffer_.resize(size);
buffer_.reserve(size * 2);
start_ = &buffer_[0];
start_[size] = traits_type::to_char_type(ch);
this->setp(start_+ size + 1, start_ + size * 2);
}
return ch;
}
size_t size() const {
return to_unsigned(this->pptr() - start_);
}
};
} // namespace internal
# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n
@ -3100,21 +3039,6 @@ class BasicArrayWriter : public BasicWriter<Char> {
typedef BasicArrayWriter<char> ArrayWriter;
typedef BasicArrayWriter<wchar_t> WArrayWriter;
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output << value;
BasicStringRef<Char> str(&buffer[0], format_buf.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
}
// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_system_error(int error_code,
@ -3544,32 +3468,6 @@ FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
FMT_VARIADIC(int, printf, CStringRef)
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
#if FMT_USE_IOSTREAMS
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
#endif
namespace internal {
template <typename Char>
inline bool is_name_start(Char c) {
@ -3924,7 +3822,10 @@ operator"" _a(const wchar_t *s, std::size_t) { return {s}; }
#endif
#ifdef FMT_HEADER_ONLY
# define FMT_FUNC inline
# include "format.cc"
#else
# define FMT_FUNC
#endif
#endif // FMT_FORMAT_H_

61
fmt/ostream.cc Normal file
View File

@ -0,0 +1,61 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ostream.h"
namespace fmt {
namespace {
// Write the content of w to os.
void write(std::ostream &os, Writer &w) {
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
size -= n;
} while (size != 0);
}
}
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
write(os, w);
}
FMT_FUNC int fprintf(std::ostream &os, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
write(os, w);
return static_cast<int>(w.size());
}
} // namespace fmt

133
fmt/ostream.h Normal file
View File

@ -0,0 +1,133 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include "format.h"
#include <ostream>
namespace fmt {
namespace internal {
template <class Char>
class FormatBuf : public std::basic_streambuf<Char> {
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
Char *start_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) {
this->setp(start_, start_ + buffer_.capacity());
}
int_type overflow(int_type ch = traits_type::eof()) {
if (!traits_type::eq_int_type(ch, traits_type::eof())) {
size_t size = this->size();
buffer_.resize(size);
buffer_.reserve(size * 2);
start_ = &buffer_[0];
start_[size] = traits_type::to_char_type(ch);
this->setp(start_+ size + 1, start_ + size * 2);
}
return ch;
}
size_t size() const {
return to_unsigned(this->pptr() - start_);
}
};
Yes &convert(std::ostream &);
struct DummyStream : std::ostream {
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
};
No &operator<<(std::ostream &, int);
template<typename T>
struct ConvertToIntImpl<T, true> {
// Convert to int only if T doesn't have an overloaded operator<<.
enum {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output << value;
BasicStringRef<Char> str(&buffer[0], format_buf.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#endif
#endif // FMT_OSTREAM_H_

View File

@ -75,6 +75,7 @@ add_fmt_test(assert-test)
add_fmt_test(gtest-extra-test)
add_fmt_test(format-test)
add_fmt_test(format-impl-test)
add_fmt_test(ostream-test)
add_fmt_test(printf-test)
add_fmt_test(util-test mock-allocator.h)
add_fmt_test(macro-test)

View File

@ -123,55 +123,3 @@ TEST(FormatTest, FormatErrorCode) {
EXPECT_EQ(msg, w.str());
}
}
TEST(FormatTest, WriteToOStream) {
std::ostringstream os;
fmt::MemoryWriter w;
w << "foo";
fmt::write(os, w);
EXPECT_EQ("foo", os.str());
}
TEST(FormatTest, WriteToOStreamMaxSize) {
std::size_t max_size = std::numeric_limits<std::size_t>::max();
std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max();
if (max_size <= fmt::internal::to_unsigned(max_streamsize))
return;
class TestWriter : public fmt::BasicWriter<char> {
private:
struct TestBuffer : fmt::Buffer<char> {
explicit TestBuffer(std::size_t size) { size_ = size; }
void grow(std::size_t) {}
} buffer_;
public:
explicit TestWriter(std::size_t size)
: fmt::BasicWriter<char>(buffer_), buffer_(size) {}
} w(max_size);
struct MockStreamBuf : std::streambuf {
MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n));
std::streamsize xsputn(const char *s, std::streamsize n) {
const void *v = s;
return xsputn(v, n);
}
} buffer;
struct TestOStream : std::ostream {
explicit TestOStream(MockStreamBuf &buffer) : std::ostream(&buffer) {}
} os(buffer);
testing::InSequence sequence;
const char *data = 0;
std::size_t size = max_size;
do {
typedef fmt::internal::MakeUnsigned<std::streamsize>::Type UStreamSize;
UStreamSize n = std::min<UStreamSize>(
size, fmt::internal::to_unsigned(max_streamsize));
EXPECT_CALL(buffer, xsputn(data, static_cast<std::streamsize>(n)))
.WillOnce(testing::Return(max_streamsize));
data += n;
size -= static_cast<std::size_t>(n);
} while (size != 0);
fmt::write(os, w);
}

View File

@ -31,10 +31,7 @@
#include <clocale>
#include <cmath>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <memory>
#include <sstream>
#include <stdint.h>
#if FMT_USE_TYPE_TRAITS
@ -386,30 +383,10 @@ TEST(WriterTest, hexu) {
EXPECT_EQ("DEADBEEF", (MemoryWriter() << hexu(0xdeadbeefull)).str());
}
class Date {
int year_, month_, day_;
public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
int year() const { return year_; }
int month() const { return month_; }
int day() const { return day_; }
friend std::ostream &operator<<(std::ostream &os, const Date &d) {
os << d.year_ << '-' << d.month_ << '-' << d.day_;
return os;
}
friend std::wostream &operator<<(std::wostream &os, const Date &d) {
os << d.year_ << L'-' << d.month_ << L'-' << d.day_;
return os;
}
template <typename Char>
friend BasicWriter<Char> &operator<<(BasicWriter<Char> &f, const Date &d) {
return f << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
template <typename Char>
BasicWriter<Char> &operator<<(BasicWriter<Char> &f, const Date &d) {
return f << d.year() << '-' << d.month() << '-' << d.day();
}
class ISO8601DateFormatter {
const Date *date_;
@ -665,7 +642,6 @@ TEST(FormatterTest, LeftAlign) {
EXPECT_EQ("c ", format("{0:<5}", 'c'));
EXPECT_EQ("abc ", format("{0:<5}", "abc"));
EXPECT_EQ("0xface ", format("{0:<8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
}
TEST(FormatterTest, RightAlign) {
@ -683,7 +659,6 @@ TEST(FormatterTest, RightAlign) {
EXPECT_EQ(" c", format("{0:>5}", 'c'));
EXPECT_EQ(" abc", format("{0:>5}", "abc"));
EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
}
TEST(FormatterTest, NumericAlign) {
@ -709,8 +684,6 @@ TEST(FormatterTest, NumericAlign) {
FormatError, "format specifier '=' requires numeric argument");
EXPECT_THROW_MSG(format("{0:=8}", reinterpret_cast<void*>(0xface)),
FormatError, "format specifier '=' requires numeric argument");
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")),
FormatError, "format specifier '=' requires numeric argument");
}
TEST(FormatterTest, CenterAlign) {
@ -728,7 +701,6 @@ TEST(FormatterTest, CenterAlign) {
EXPECT_EQ(" c ", format("{0:^5}", 'c'));
EXPECT_EQ(" abc ", format("{0:^6}", "abc"));
EXPECT_EQ(" 0xface ", format("{0:^8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
}
TEST(FormatterTest, Fill) {
@ -748,7 +720,6 @@ TEST(FormatterTest, Fill) {
EXPECT_EQ("c****", format("{0:*<5}", 'c'));
EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
}
TEST(FormatterTest, PlusSign) {
@ -773,8 +744,6 @@ TEST(FormatterTest, PlusSign) {
FormatError, "format specifier '+' requires numeric argument");
EXPECT_THROW_MSG(format("{0:+}", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier '+' requires numeric argument");
EXPECT_THROW_MSG(format("{0:+}", TestString()),
FormatError, "format specifier '+' requires numeric argument");
}
TEST(FormatterTest, MinusSign) {
@ -799,8 +768,6 @@ TEST(FormatterTest, MinusSign) {
FormatError, "format specifier '-' requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier '-' requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", TestString()),
FormatError, "format specifier '-' requires numeric argument");
}
TEST(FormatterTest, SpaceSign) {
@ -825,8 +792,6 @@ TEST(FormatterTest, SpaceSign) {
FormatError, "format specifier ' ' requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier ' ' requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", TestString()),
FormatError, "format specifier ' ' requires numeric argument");
}
TEST(FormatterTest, HashFlag) {
@ -872,8 +837,6 @@ TEST(FormatterTest, HashFlag) {
FormatError, "format specifier '#' requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier '#' requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", TestString()),
FormatError, "format specifier '#' requires numeric argument");
}
TEST(FormatterTest, ZeroFlag) {
@ -894,8 +857,6 @@ TEST(FormatterTest, ZeroFlag) {
FormatError, "format specifier '0' requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", reinterpret_cast<void*>(0x42)),
FormatError, "format specifier '0' requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", TestString()),
FormatError, "format specifier '0' requires numeric argument");
}
TEST(FormatterTest, Width) {
@ -923,7 +884,6 @@ TEST(FormatterTest, Width) {
EXPECT_EQ(" 0xcafe", format("{0:10}", reinterpret_cast<void*>(0xcafe)));
EXPECT_EQ("x ", format("{0:11}", 'x'));
EXPECT_EQ("str ", format("{0:12}", "str"));
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
}
TEST(FormatterTest, RuntimeWidth) {
@ -982,7 +942,6 @@ TEST(FormatterTest, RuntimeWidth) {
format("{0:{1}}", reinterpret_cast<void*>(0xcafe), 10));
EXPECT_EQ("x ", format("{0:{1}}", 'x', 11));
EXPECT_EQ("str ", format("{0:{1}}", "str", 12));
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
}
TEST(FormatterTest, Precision) {
@ -1042,7 +1001,6 @@ TEST(FormatterTest, Precision) {
FormatError, "precision not allowed in pointer format specifier");
EXPECT_EQ("st", format("{0:.2}", "str"));
EXPECT_EQ("te", format("{0:.2}", TestString("test")));
}
TEST(FormatterTest, RuntimePrecision) {
@ -1126,7 +1084,6 @@ TEST(FormatterTest, RuntimePrecision) {
FormatError, "precision not allowed in pointer format specifier");
EXPECT_EQ("st", format("{0:.{1}}", "str", 2));
EXPECT_EQ("te", format("{0:.{1}}", TestString("test"), 2));
}
template <typename T>
@ -1393,14 +1350,14 @@ TEST(FormatterTest, FormatCStringRef) {
EXPECT_EQ("test", format("{0}", CStringRef("test")));
}
TEST(FormatterTest, FormatUsingIOStreams) {
EXPECT_EQ("a string", format("{0}", TestString("a string")));
std::string s = format("The date is {0}", Date(2012, 12, 9));
EXPECT_EQ("The date is 2012-12-9", s);
void format(fmt::BasicFormatter<char> &f, const char *, const Date &d) {
f.writer() << d.year() << '-' << d.month() << '-' << d.day();
}
TEST(FormatterTest, FormatCustom) {
Date date(2012, 12, 9);
check_unknown_types(date, "s", "string");
EXPECT_EQ(L"The date is 2012-12-9",
format(L"The date is {0}", Date(2012, 12, 9)));
EXPECT_THROW_MSG(fmt::format("{:s}", date), FormatError,
"unmatched '}' in format string");
}
class Answer {};
@ -1563,9 +1520,6 @@ TEST(FormatTest, Print) {
EXPECT_WRITE(stderr,
fmt::print(stderr, "Don't {}!", "panic"), "Don't panic!");
#endif
std::ostringstream os;
fmt::print(os, "Don't {}!", "panic");
EXPECT_EQ("Don't panic!", os.str());
}
#if FMT_USE_FILE_DESCRIPTORS
@ -1660,27 +1614,12 @@ TEST(LiteralsTest, NamedArg) {
}
#endif // FMT_USE_USER_DEFINED_LITERALS
enum TestEnum {};
std::ostream &operator<<(std::ostream &os, TestEnum) {
return os << "TestEnum";
}
enum TestEnum2 { A };
enum TestEnum { A };
TEST(FormatTest, Enum) {
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
EXPECT_EQ("0", fmt::format("{}", A));
}
struct EmptyTest {};
std::ostream &operator<<(std::ostream &os, EmptyTest) {
return os << "";
}
TEST(FormatTest, EmptyCustomOutput) {
EXPECT_EQ("", fmt::format("{}", EmptyTest()));
}
class MockArgFormatter :
public fmt::internal::ArgFormatterBase<MockArgFormatter, char> {
public:
@ -1705,19 +1644,3 @@ FMT_VARIADIC(void, custom_format, const char *)
TEST(FormatTest, CustomArgFormatter) {
custom_format("{}", 42);
}
struct TestArgFormatter : fmt::BasicArgFormatter<TestArgFormatter, char> {
TestArgFormatter(fmt::BasicFormatter<char, TestArgFormatter> &f,
fmt::FormatSpec &s, const char *fmt)
: fmt::BasicArgFormatter<TestArgFormatter, char>(f, s, fmt) {}
};
TEST(ArgFormatterTest, CustomArg) {
fmt::MemoryWriter writer;
typedef fmt::BasicFormatter<char, TestArgFormatter> Formatter;
Formatter formatter(fmt::ArgList(), writer);
fmt::FormatSpec spec;
TestArgFormatter af(formatter, spec, "}");
af.visit(fmt::internal::MakeArg<Formatter>(TestEnum()));
EXPECT_EQ("TestEnum", writer.str());
}

192
test/ostream-test.cc Normal file
View File

@ -0,0 +1,192 @@
/*
std::ostream support tests
Copyright (c) 2012-2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmt/ostream.cc"
#include <sstream>
#include "gmock/gmock.h"
#include "gtest-extra.h"
#include "util.h"
using fmt::format;
using fmt::FormatError;
template <typename Char>
std::basic_ostream<Char> &operator<<(
std::basic_ostream<Char> &os, const BasicTestString<Char> &s) {
os << s.value();
return os;
}
std::ostream &operator<<(std::ostream &os, const Date &d) {
os << d.year() << '-' << d.month() << '-' << d.day();
return os;
}
std::wostream &operator<<(std::wostream &os, const Date &d) {
os << d.year() << L'-' << d.month() << L'-' << d.day();
return os;
}
enum TestEnum {};
std::ostream &operator<<(std::ostream &os, TestEnum) {
return os << "TestEnum";
}
enum TestEnum2 {A};
TEST(OStreamTest, Enum) {
EXPECT_FALSE(fmt::internal::ConvertToInt<TestEnum>::value);
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
EXPECT_EQ("0", fmt::format("{}", A));
}
struct TestArgFormatter : fmt::BasicArgFormatter<TestArgFormatter, char> {
TestArgFormatter(fmt::BasicFormatter<char, TestArgFormatter> &f,
fmt::FormatSpec &s, const char *fmt)
: fmt::BasicArgFormatter<TestArgFormatter, char>(f, s, fmt) {}
};
TEST(OStreamTest, CustomArg) {
fmt::MemoryWriter writer;
typedef fmt::BasicFormatter<char, TestArgFormatter> Formatter;
Formatter formatter(fmt::ArgList(), writer);
fmt::FormatSpec spec;
TestArgFormatter af(formatter, spec, "}");
af.visit(fmt::internal::MakeArg<Formatter>(TestEnum()));
EXPECT_EQ("TestEnum", writer.str());
}
TEST(OStreamTest, Format) {
EXPECT_EQ("a string", format("{0}", TestString("a string")));
std::string s = format("The date is {0}", Date(2012, 12, 9));
EXPECT_EQ("The date is 2012-12-9", s);
Date date(2012, 12, 9);
EXPECT_EQ(L"The date is 2012-12-9",
format(L"The date is {0}", Date(2012, 12, 9)));
}
TEST(OStreamTest, FormatSpecs) {
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")),
FormatError, "format specifier '=' requires numeric argument");
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
EXPECT_THROW_MSG(format("{0:+}", TestString()),
FormatError, "format specifier '+' requires numeric argument");
EXPECT_THROW_MSG(format("{0:-}", TestString()),
FormatError, "format specifier '-' requires numeric argument");
EXPECT_THROW_MSG(format("{0: }", TestString()),
FormatError, "format specifier ' ' requires numeric argument");
EXPECT_THROW_MSG(format("{0:#}", TestString()),
FormatError, "format specifier '#' requires numeric argument");
EXPECT_THROW_MSG(format("{0:05}", TestString()),
FormatError, "format specifier '0' requires numeric argument");
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
EXPECT_EQ("te", format("{0:.2}", TestString("test")));
EXPECT_EQ("te", format("{0:.{1}}", TestString("test"), 2));
}
struct EmptyTest {};
std::ostream &operator<<(std::ostream &os, EmptyTest) {
return os << "";
}
TEST(OStreamTest, EmptyCustomOutput) {
EXPECT_EQ("", fmt::format("{}", EmptyTest()));
}
TEST(OStreamTest, Print) {
std::ostringstream os;
fmt::print(os, "Don't {}!", "panic");
EXPECT_EQ("Don't panic!", os.str());
}
TEST(OStreamTest, PrintfCustom) {
EXPECT_EQ("abc", fmt::sprintf("%s", TestString("abc")));
}
TEST(OStreamTest, FPrintf) {
std::ostringstream os;
int ret = fmt::fprintf(os, "Don't %s!", "panic");
EXPECT_EQ("Don't panic!", os.str());
EXPECT_EQ(12, ret);
}
TEST(OStreamTest, WriteToOStream) {
std::ostringstream os;
fmt::MemoryWriter w;
w << "foo";
fmt::write(os, w);
EXPECT_EQ("foo", os.str());
}
TEST(OStreamTest, WriteToOStreamMaxSize) {
std::size_t max_size = std::numeric_limits<std::size_t>::max();
std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max();
if (max_size <= fmt::internal::to_unsigned(max_streamsize))
return;
class TestWriter : public fmt::BasicWriter<char> {
private:
struct TestBuffer : fmt::Buffer<char> {
explicit TestBuffer(std::size_t size) { size_ = size; }
void grow(std::size_t) {}
} buffer_;
public:
explicit TestWriter(std::size_t size)
: fmt::BasicWriter<char>(buffer_), buffer_(size) {}
} w(max_size);
struct MockStreamBuf : std::streambuf {
MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n));
std::streamsize xsputn(const char *s, std::streamsize n) {
const void *v = s;
return xsputn(v, n);
}
} buffer;
struct TestOStream : std::ostream {
explicit TestOStream(MockStreamBuf &buffer) : std::ostream(&buffer) {}
} os(buffer);
testing::InSequence sequence;
const char *data = 0;
std::size_t size = max_size;
do {
typedef fmt::internal::MakeUnsigned<std::streamsize>::Type UStreamSize;
UStreamSize n = std::min<UStreamSize>(
size, fmt::internal::to_unsigned(max_streamsize));
EXPECT_CALL(buffer, xsputn(data, static_cast<std::streamsize>(n)))
.WillOnce(testing::Return(max_streamsize));
data += n;
size -= static_cast<std::size_t>(n);
} while (size != 0);
fmt::write(os, w);
}

View File

@ -447,10 +447,6 @@ TEST(PrintfTest, Pointer) {
EXPECT_PRINTF("(nil)", "%p", null_str);
}
TEST(PrintfTest, Custom) {
EXPECT_PRINTF("abc", "%s", TestString("abc"));
}
TEST(PrintfTest, Location) {
// TODO: test %n
}
@ -479,12 +475,5 @@ TEST(PrintfTest, PrintfError) {
#endif
TEST(PrintfTest, WideString) {
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", TestWString(L"abc")));
}
TEST(PrintfTest, Iostream) {
std::ostringstream os;
int ret = fmt::fprintf(os, "Don't %s!", "panic");
EXPECT_EQ("Don't panic!", os.str());
EXPECT_EQ(12, ret);
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
}

View File

@ -62,9 +62,10 @@ using testing::StrictMock;
namespace {
struct Test {};
template <typename Char>
std::basic_ostream<Char> &operator<<(std::basic_ostream<Char> &os, Test) {
return os << "test";
void format(fmt::BasicFormatter<Char> &f, const Char *, Test) {
f.writer() << "test";
}
template <typename Char, typename T>
@ -914,14 +915,11 @@ TEST(UtilTest, ReportWindowsError) {
#endif // _WIN32
enum TestEnum2 {};
enum TestEnum3 {};
std::ostream &operator<<(std::ostream &, TestEnum3);
TEST(UtilTest, ConvertToInt) {
EXPECT_TRUE(fmt::internal::ConvertToInt<char>::enable_conversion);
EXPECT_FALSE(fmt::internal::ConvertToInt<const char *>::enable_conversion);
EXPECT_TRUE(fmt::internal::ConvertToInt<TestEnum2>::value);
EXPECT_FALSE(fmt::internal::ConvertToInt<TestEnum3>::value);
}
#if FMT_USE_ENUM_BASE

View File

@ -78,11 +78,7 @@ class BasicTestString {
public:
explicit BasicTestString(const Char *value = EMPTY) : value_(value) {}
friend std::basic_ostream<Char> &operator<<(
std::basic_ostream<Char> &os, const BasicTestString &s) {
os << s.value_;
return os;
}
const std::basic_string<Char> &value() const { return value_; }
};
template <typename Char>
@ -90,3 +86,13 @@ const Char BasicTestString<Char>::EMPTY[] = {0};
typedef BasicTestString<char> TestString;
typedef BasicTestString<wchar_t> TestWString;
class Date {
int year_, month_, day_;
public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
int year() const { return year_; }
int month() const { return month_; }
int day() const { return day_; }
};