Add support for types other than int in oct, hex, hexu & pad. Document the API.

This commit is contained in:
Victor Zverovich 2013-01-13 07:14:54 -08:00
parent d53cc2bc12
commit aba9e15021
3 changed files with 98 additions and 52 deletions

View File

@ -13,7 +13,8 @@ String Formatting
.. doxygenclass:: format::TempFormatter .. doxygenclass:: format::TempFormatter
:members: :members:
.. doxygenstruct:: format::NoAction .. doxygenclass:: format::NoAction
:members:
.. doxygenclass:: format::StringRef .. doxygenclass:: format::StringRef
:members: :members:

109
format.h
View File

@ -117,23 +117,36 @@ void Array<T, SIZE>::append(const T *begin, const T *end) {
} }
// Information about an integer type. // Information about an integer type.
// IntTraits is not specialized for integer types smaller than int,
// since these are promoted to int.
template <typename T> template <typename T>
struct IntTraits { struct IntTraits {};
template <typename T, typename UnsignedT>
struct SignedIntTraits {
typedef T Type;
typedef UnsignedT UnsignedType;
static bool IsNegative(T value) { return value < 0; }
};
template <typename T>
struct UnsignedIntTraits {
typedef T Type;
typedef T UnsignedType; typedef T UnsignedType;
static bool IsNegative(T) { return false; } static bool IsNegative(T) { return false; }
}; };
template <> template <>
struct IntTraits<int> { struct IntTraits<int> : SignedIntTraits<int, unsigned> {};
typedef unsigned UnsignedType;
static bool IsNegative(int value) { return value < 0; }
};
template <> template <>
struct IntTraits<long> { struct IntTraits<unsigned> : UnsignedIntTraits<unsigned> {};
typedef unsigned long UnsignedType;
static bool IsNegative(long value) { return value < 0; } template <>
}; struct IntTraits<long> : SignedIntTraits<long, unsigned long> {};
template <>
struct IntTraits<unsigned long> : UnsignedIntTraits<unsigned long> {};
class ArgInserter; class ArgInserter;
} }
@ -157,13 +170,31 @@ class StringRef {
mutable std::size_t size_; mutable std::size_t size_;
public: public:
/**
Constructs a string reference object from a C string and a size.
If `size` is zero, which is the default, the size is computed with
`strlen`.
*/
StringRef(const char *s, std::size_t size = 0) : data_(s), size_(size) {} StringRef(const char *s, std::size_t size = 0) : data_(s), size_(size) {}
/**
Constructs a string reference from an `std::string` object.
*/
StringRef(const std::string &s) : data_(s.c_str()), size_(s.size()) {} StringRef(const std::string &s) : data_(s.c_str()), size_(s.size()) {}
/**
Converts a string reference to an `std::string` object.
*/
operator std::string() const { return std::string(data_, size()); } operator std::string() const { return std::string(data_, size()); }
/**
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.
*/
std::size_t size() const { std::size_t size() const {
if (size_ == 0) size_ = std::strlen(data_); if (size_ == 0) size_ = std::strlen(data_);
return size_; return size_;
@ -256,28 +287,39 @@ class IntFormatter : public SpecT {
T value() const { return value_; } T value() const { return value_; }
}; };
inline IntFormatter<int, TypeSpec<'o'> > oct(int value) { // internal::IntTraits<T>::Type is used instead of T to avoid instantiating
return IntFormatter<int, TypeSpec<'o'> >(value, TypeSpec<'o'>()); // the function for types smaller than int similarly to enable_if.
template <typename T>
inline IntFormatter<
typename internal::IntTraits<T>::Type, TypeSpec<'o'> > oct(T value) {
return IntFormatter<T, TypeSpec<'o'> >(value, TypeSpec<'o'>());
} }
inline IntFormatter<int, TypeSpec<'x'> > hex(int value) { template <typename T>
return IntFormatter<int, TypeSpec<'x'> >(value, TypeSpec<'x'>()); inline IntFormatter<
typename internal::IntTraits<T>::Type, TypeSpec<'x'> > hex(T value) {
return IntFormatter<T, TypeSpec<'x'> >(value, TypeSpec<'x'>());
} }
inline IntFormatter<int, TypeSpec<'X'> > hexu(int value) { template <typename T>
return IntFormatter<int, TypeSpec<'X'> >(value, TypeSpec<'X'>()); inline IntFormatter<
typename internal::IntTraits<T>::Type, TypeSpec<'X'> > hexu(T value) {
return IntFormatter<T, TypeSpec<'X'> >(value, TypeSpec<'X'>());
} }
template <char TYPE> template <typename T, char TYPE>
inline IntFormatter<int, AlignTypeSpec<TYPE> > pad( inline IntFormatter<
IntFormatter<int, TypeSpec<TYPE> > f, unsigned width, char fill = ' ') { typename internal::IntTraits<T>::Type, AlignTypeSpec<TYPE> > pad(
return IntFormatter<int, AlignTypeSpec<TYPE> >( IntFormatter<T, TypeSpec<TYPE> > f, unsigned width, char fill = ' ') {
return IntFormatter<T, AlignTypeSpec<TYPE> >(
f.value(), AlignTypeSpec<TYPE>(width, fill)); f.value(), AlignTypeSpec<TYPE>(width, fill));
} }
inline IntFormatter<int, AlignTypeSpec<0> > pad( template <typename T>
int value, unsigned width, char fill = ' ') { inline IntFormatter<
return IntFormatter<int, AlignTypeSpec<0> >( typename internal::IntTraits<T>::Type, AlignTypeSpec<0> > pad(
T value, unsigned width, char fill = ' ') {
return IntFormatter<T, AlignTypeSpec<0> >(
value, AlignTypeSpec<0>(width, fill)); value, AlignTypeSpec<0>(width, fill));
} }
@ -336,25 +378,19 @@ class BasicFormatter {
public: public:
/** /**
\rst
Returns the number of characters written to the output buffer. Returns the number of characters written to the output buffer.
\endrst
*/ */
std::size_t size() const { return buffer_.size(); } std::size_t size() const { return buffer_.size(); }
/** /**
\rst
Returns a pointer to the output buffer content. No terminating null Returns a pointer to the output buffer content. No terminating null
character is appended. character is appended.
\endrst
*/ */
const char *data() const { return &buffer_[0]; } const char *data() const { return &buffer_[0]; }
/** /**
\rst
Returns a pointer to the output buffer content with terminating null Returns a pointer to the output buffer content with terminating null
character appended. character appended.
\endrst
*/ */
const char *c_str() const { const char *c_str() const {
std::size_t size = buffer_.size(); std::size_t size = buffer_.size();
@ -364,9 +400,7 @@ class BasicFormatter {
} }
/** /**
\rst Returns the content of the output buffer as an `std::string`.
Returns the content of the output buffer as an ``std::string``.
\endrst
*/ */
std::string str() const { return std::string(&buffer_[0], buffer_.size()); } std::string str() const { return std::string(&buffer_[0], buffer_.size()); }
@ -628,18 +662,14 @@ class Formatter : public BasicFormatter {
public: public:
/** /**
\rst
Constructs a formatter with an empty output buffer. Constructs a formatter with an empty output buffer.
\endrst
*/ */
Formatter() : format_(0) {} Formatter() : format_(0) {}
/** /**
\rst
Formats a string appending the output to the internal buffer. Formats a string appending the output to the internal buffer.
Arguments are accepted through the returned ``ArgInserter`` object Arguments are accepted through the returned `ArgInserter` object
using inserter operator ``<<``. using inserter operator `<<`.
\endrst
*/ */
internal::ArgInserter operator()(StringRef format); internal::ArgInserter operator()(StringRef format);
}; };
@ -764,17 +794,16 @@ inline internal::ArgInserter Formatter::operator()(StringRef format) {
/** /**
A formatting action that does nothing. A formatting action that does nothing.
*/ */
struct NoAction { class NoAction {
public:
/** Does nothing. */ /** Does nothing. */
void operator()(const Formatter &) const {} void operator()(const Formatter &) const {}
}; };
/** /**
\rst
A formatter with an action performed when formatting is complete. A formatter with an action performed when formatting is complete.
Objects of this class normally exist only as temporaries returned Objects of this class normally exist only as temporaries returned
by one of the formatting functions which explains the name. by one of the formatting functions which explains the name.
\endrst
*/ */
template <typename Action = NoAction> template <typename Action = NoAction>
class TempFormatter : public internal::ArgInserter { class TempFormatter : public internal::ArgInserter {

View File

@ -1065,21 +1065,29 @@ TEST(TempFormatterTest, Examples) {
} }
TEST(StrTest, oct) { TEST(StrTest, oct) {
BasicFormatter f; EXPECT_EQ("12", (BasicFormatter() << oct(012)).str());
f << oct(042); EXPECT_EQ("34", (BasicFormatter() << oct(034)).str());
EXPECT_EQ("42", f.str()); EXPECT_EQ("56", (BasicFormatter() << oct(056)).str());
EXPECT_EQ("70", (BasicFormatter() << oct(070)).str());
} }
TEST(StrTest, hex) { TEST(StrTest, hex) {
BasicFormatter f; fmt::IntFormatter<int, fmt::TypeSpec<'x'> > (*phex)(int value) = hex;
f << hex(0xbeef); phex(42);
EXPECT_EQ("beef", f.str()); // This shouldn't compile:
//fmt::IntFormatter<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex;
EXPECT_EQ("cafe", (BasicFormatter() << hex(0xcafe)).str());
EXPECT_EQ("babe", (BasicFormatter() << hex(0xbabeu)).str());
EXPECT_EQ("dead", (BasicFormatter() << hex(0xdeadl)).str());
EXPECT_EQ("beef", (BasicFormatter() << hex(0xbeeful)).str());
} }
TEST(StrTest, hexu) { TEST(StrTest, hexu) {
BasicFormatter f; EXPECT_EQ("CAFE", (BasicFormatter() << hexu(0xcafe)).str());
f << hexu(0xbabe); EXPECT_EQ("BABE", (BasicFormatter() << hexu(0xbabeu)).str());
EXPECT_EQ("BABE", f.str()); EXPECT_EQ("DEAD", (BasicFormatter() << hexu(0xdeadl)).str());
EXPECT_EQ("BEEF", (BasicFormatter() << hexu(0xbeeful)).str());
} }
class ISO8601DateFormatter { class ISO8601DateFormatter {
@ -1098,9 +1106,17 @@ public:
ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); } ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); }
TEST(StrTest, pad) { TEST(StrTest, pad) {
EXPECT_EQ(" cafe", (BasicFormatter() << pad(hex(0xcafe), 8)).str());
EXPECT_EQ(" babe", (BasicFormatter() << pad(hex(0xbabeu), 8)).str());
EXPECT_EQ(" dead", (BasicFormatter() << pad(hex(0xdeadl), 8)).str());
EXPECT_EQ(" beef", (BasicFormatter() << pad(hex(0xbeeful), 8)).str());
EXPECT_EQ(" 11", (BasicFormatter() << pad(11, 7)).str());
EXPECT_EQ(" 22", (BasicFormatter() << pad(22u, 7)).str());
EXPECT_EQ(" 33", (BasicFormatter() << pad(33l, 7)).str());
EXPECT_EQ(" 44", (BasicFormatter() << pad(44lu, 7)).str());
BasicFormatter f; BasicFormatter f;
f << pad(hex(0xbeef), 8);
EXPECT_EQ(" beef", f.str());
f.Clear(); f.Clear();
f << pad(42, 5, '0'); f << pad(42, 5, '0');
EXPECT_EQ("00042", f.str()); EXPECT_EQ("00042", f.str());