Fix a crash when exception is throws in nested Format. Check automatically assigned argument index. Add double output method to BasicWriter.

This commit is contained in:
Victor Zverovich 2013-02-27 13:17:09 -08:00
parent dcb82310eb
commit 1e58ebf1b7
2 changed files with 129 additions and 104 deletions

View File

@ -485,6 +485,11 @@ class BasicWriter {
return *this << IntFormatter<unsigned, TypeSpec<0> >(value, TypeSpec<0>()); return *this << IntFormatter<unsigned, TypeSpec<0> >(value, TypeSpec<0>());
} }
BasicWriter &operator<<(double value) {
FormatDouble(value, FormatSpec(), -1);
return *this;
}
BasicWriter &operator<<(Char value) { BasicWriter &operator<<(Char value) {
*GrowBuffer(1) = value; *GrowBuffer(1) = value;
return *this; return *this;
@ -957,6 +962,7 @@ class BasicFormatter : public BasicWriter<Char> {
// so it will be alive in the Arg's destructor where Format is called. // so it will be alive in the Arg's destructor where Format is called.
// Note that the string object will not necessarily be alive when // Note that the string object will not necessarily be alive when
// the destructor of ArgInserter is called. // the destructor of ArgInserter is called.
if (formatter)
formatter->CompleteFormatting(); formatter->CompleteFormatting();
} }
}; };
@ -1291,9 +1297,9 @@ inline const typename BasicFormatter<Char>::Arg
} }
next_arg_index_ = -1; next_arg_index_ = -1;
arg_index = ParseUInt(s); arg_index = ParseUInt(s);
}
if (arg_index >= args_.size()) if (arg_index >= args_.size())
ReportError(s, "argument index is out of range in format"); ReportError(s, "argument index is out of range in format");
}
return *args_[arg_index]; return *args_[arg_index];
} }

View File

@ -45,7 +45,7 @@ using fmt::Formatter;
using fmt::Format; using fmt::Format;
using fmt::FormatError; using fmt::FormatError;
using fmt::StringRef; using fmt::StringRef;
using fmt::hexu; using fmt::Writer;
using fmt::pad; using fmt::pad;
#define FORMAT_TEST_THROW_(statement, expected_exception, message, fail) \ #define FORMAT_TEST_THROW_(statement, expected_exception, message, fail) \
@ -195,6 +195,117 @@ TEST(ArrayTest, Append) {
EXPECT_EQ(15u, array.capacity()); EXPECT_EQ(15u, array.capacity());
} }
TEST(WriterTest, WriteInt) {
EXPECT_EQ("42", str(Writer() << 42));
EXPECT_EQ("-42", str(Writer() << -42));
}
TEST(WriterTest, oct) {
using fmt::oct;
EXPECT_EQ("12", str(Writer() << oct(static_cast<short>(012))));
EXPECT_EQ("12", str(Writer() << oct(012)));
EXPECT_EQ("34", str(Writer() << oct(034u)));
EXPECT_EQ("56", str(Writer() << oct(056l)));
EXPECT_EQ("70", str(Writer() << oct(070ul)));
}
TEST(WriterTest, hex) {
using fmt::hex;
fmt::IntFormatter<int, fmt::TypeSpec<'x'> > (*phex)(int value) = hex;
phex(42);
// This shouldn't compile:
//fmt::IntFormatter<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex;
EXPECT_EQ("cafe", str(Writer() << hex(0xcafe)));
EXPECT_EQ("babe", str(Writer() << hex(0xbabeu)));
EXPECT_EQ("dead", str(Writer() << hex(0xdeadl)));
EXPECT_EQ("beef", str(Writer() << hex(0xbeeful)));
}
TEST(WriterTest, hexu) {
using fmt::hexu;
EXPECT_EQ("CAFE", str(Writer() << hexu(0xcafe)));
EXPECT_EQ("BABE", str(Writer() << hexu(0xbabeu)));
EXPECT_EQ("DEAD", str(Writer() << hexu(0xdeadl)));
EXPECT_EQ("BEEF", str(Writer() << hexu(0xbeeful)));
}
TEST(WriterTest, WriteDouble) {
EXPECT_EQ("4.2", str(Writer() << 4.2));
EXPECT_EQ("-4.2", str(Writer() << -4.2));
}
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;
}
template <typename Char>
friend BasicWriter<Char> &operator<<(BasicWriter<Char> &f, const Date &d) {
return f << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
class ISO8601DateFormatter {
const Date *date_;
public:
ISO8601DateFormatter(const Date &d) : date_(&d) {}
template <typename Char>
friend BasicWriter<Char> &operator<<(
BasicWriter<Char> &w, const ISO8601DateFormatter &d) {
return w << pad(d.date_->year(), 4, '0') << '-'
<< pad(d.date_->month(), 2, '0') << '-' << pad(d.date_->day(), 2, '0');
}
};
ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); }
TEST(WriterTest, pad) {
using fmt::hex;
EXPECT_EQ(" cafe", str(Writer() << pad(hex(0xcafe), 8)));
EXPECT_EQ(" babe", str(Writer() << pad(hex(0xbabeu), 8)));
EXPECT_EQ(" dead", str(Writer() << pad(hex(0xdeadl), 8)));
EXPECT_EQ(" beef", str(Writer() << pad(hex(0xbeeful), 8)));
EXPECT_EQ(" 11", str(Writer() << pad(11, 7)));
EXPECT_EQ(" 22", str(Writer() << pad(22u, 7)));
EXPECT_EQ(" 33", str(Writer() << pad(33l, 7)));
EXPECT_EQ(" 44", str(Writer() << pad(44lu, 7)));
BasicWriter<char> f;
f.Clear();
f << pad(42, 5, '0');
EXPECT_EQ("00042", f.str());
f.Clear();
f << Date(2012, 12, 9);
EXPECT_EQ("2012-12-9", f.str());
f.Clear();
f << iso8601(Date(2012, 1, 9));
EXPECT_EQ("2012-01-09", f.str());
}
TEST(WriterTest, NoConflictWithIOManip) {
using namespace std;
using namespace fmt;
EXPECT_EQ("cafe", str(Writer() << hex(0xcafe)));
EXPECT_EQ("12", str(Writer() << oct(012)));
}
TEST(WriterTest, WWriter) {
EXPECT_EQ(L"cafe", str(fmt::WWriter() << fmt::hex(0xcafe)));
}
TEST(FormatterTest, Escape) { TEST(FormatterTest, Escape) {
EXPECT_EQ("{", str(Format("{{"))); EXPECT_EQ("{", str(Format("{{")));
EXPECT_EQ("before {", str(Format("before {{"))); EXPECT_EQ("before {", str(Format("before {{")));
@ -266,6 +377,8 @@ TEST(FormatterTest, AutoArgIndex) {
FormatError, "cannot switch from manual to automatic argument indexing"); FormatError, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(Format("{:.{0}}") << 1.2345 << 2, EXPECT_THROW_MSG(Format("{:.{0}}") << 1.2345 << 2,
FormatError, "cannot switch from automatic to manual argument indexing"); FormatError, "cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(Format("{}"), FormatError,
"argument index is out of range in format");
} }
TEST(FormatterTest, EmptySpecs) { TEST(FormatterTest, EmptySpecs) {
@ -842,26 +955,6 @@ TEST(FormatterTest, FormatString) {
EXPECT_EQ("test", str(Format("{0}") << std::string("test"))); EXPECT_EQ("test", str(Format("{0}") << std::string("test")));
} }
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;
}
template <typename Char>
friend BasicWriter<Char> &operator<<(BasicWriter<Char> &f, const Date &d) {
return f << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};
TEST(FormatterTest, FormatUsingIOStreams) { TEST(FormatterTest, FormatUsingIOStreams) {
EXPECT_EQ("a string", str(Format("{0}") << TestString("a string"))); EXPECT_EQ("a string", str(Format("{0}") << TestString("a string")));
std::string s = str(fmt::Format("The date is {0}") << Date(2012, 12, 9)); std::string s = str(fmt::Format("The date is {0}") << Date(2012, 12, 9));
@ -948,6 +1041,12 @@ TEST(FormatterTest, StrNamespace) {
fmt::c_str(Format("")); fmt::c_str(Format(""));
} }
TEST(FormatterTest, ExceptionInNestedFormat) {
// Exception in nested format may cause Arg's destructor be called before
// the argument has been attached to a Formatter object.
EXPECT_THROW(Format(Format("{}")) << 42;, FormatError);
}
TEST(StringRefTest, Ctor) { TEST(StringRefTest, Ctor) {
EXPECT_STREQ("abc", StringRef("abc").c_str()); EXPECT_STREQ("abc", StringRef("abc").c_str());
EXPECT_EQ(3u, StringRef("abc").size()); EXPECT_EQ(3u, StringRef("abc").size());
@ -1061,86 +1160,6 @@ TEST(TempFormatterTest, Examples) {
ReportError("File not found: {0}") << path; ReportError("File not found: {0}") << path;
} }
TEST(StrTest, oct) {
using fmt::oct;
EXPECT_EQ("12", str(BasicWriter<char>() << oct(static_cast<short>(012))));
EXPECT_EQ("12", str(BasicWriter<char>() << oct(012)));
EXPECT_EQ("34", str(BasicWriter<char>() << oct(034u)));
EXPECT_EQ("56", str(BasicWriter<char>() << oct(056l)));
EXPECT_EQ("70", str(BasicWriter<char>() << oct(070ul)));
}
TEST(StrTest, hex) {
using fmt::hex;
fmt::IntFormatter<int, fmt::TypeSpec<'x'> > (*phex)(int value) = hex;
phex(42);
// This shouldn't compile:
//fmt::IntFormatter<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex;
EXPECT_EQ("cafe", str(BasicWriter<char>() << hex(0xcafe)));
EXPECT_EQ("babe", str(BasicWriter<char>() << hex(0xbabeu)));
EXPECT_EQ("dead", str(BasicWriter<char>() << hex(0xdeadl)));
EXPECT_EQ("beef", str(BasicWriter<char>() << hex(0xbeeful)));
}
TEST(StrTest, hexu) {
EXPECT_EQ("CAFE", str(BasicWriter<char>() << hexu(0xcafe)));
EXPECT_EQ("BABE", str(BasicWriter<char>() << hexu(0xbabeu)));
EXPECT_EQ("DEAD", str(BasicWriter<char>() << hexu(0xdeadl)));
EXPECT_EQ("BEEF", str(BasicWriter<char>() << hexu(0xbeeful)));
}
class ISO8601DateFormatter {
const Date *date_;
public:
ISO8601DateFormatter(const Date &d) : date_(&d) {}
template <typename Char>
friend BasicWriter<Char> &operator<<(
BasicWriter<Char> &f, const ISO8601DateFormatter &d) {
return f << pad(d.date_->year(), 4, '0') << '-'
<< pad(d.date_->month(), 2, '0') << '-' << pad(d.date_->day(), 2, '0');
}
};
ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); }
TEST(StrTest, pad) {
using fmt::hex;
EXPECT_EQ(" cafe", str(BasicWriter<char>() << pad(hex(0xcafe), 8)));
EXPECT_EQ(" babe", str(BasicWriter<char>() << pad(hex(0xbabeu), 8)));
EXPECT_EQ(" dead", str(BasicWriter<char>() << pad(hex(0xdeadl), 8)));
EXPECT_EQ(" beef", str(BasicWriter<char>() << pad(hex(0xbeeful), 8)));
EXPECT_EQ(" 11", str(BasicWriter<char>() << pad(11, 7)));
EXPECT_EQ(" 22", str(BasicWriter<char>() << pad(22u, 7)));
EXPECT_EQ(" 33", str(BasicWriter<char>() << pad(33l, 7)));
EXPECT_EQ(" 44", str(BasicWriter<char>() << pad(44lu, 7)));
BasicWriter<char> f;
f.Clear();
f << pad(42, 5, '0');
EXPECT_EQ("00042", f.str());
f.Clear();
f << Date(2012, 12, 9);
EXPECT_EQ("2012-12-9", f.str());
f.Clear();
f << iso8601(Date(2012, 1, 9));
EXPECT_EQ("2012-01-09", f.str());
}
TEST(StrTest, NoConflictWithIOManip) {
using namespace std;
using namespace fmt;
EXPECT_EQ("cafe", str(BasicWriter<char>() << hex(0xcafe)));
EXPECT_EQ("12", str(BasicWriter<char>() << oct(012)));
}
TEST(StrTest, BasicWriterWChar) {
EXPECT_EQ(L"cafe", str(BasicWriter<wchar_t>() << fmt::hex(0xcafe)));
}
template <typename T> template <typename T>
std::string str(const T &value) { std::string str(const T &value) {
return fmt::str(fmt::Format("{0}") << value); return fmt::str(fmt::Format("{0}") << value);