Commit 877abaf3 authored by Victor Zverovich's avatar Victor Zverovich

Parameterize integer formatting method on format spec type. Add Sprint/iomanip...

Parameterize integer formatting method on format spec type. Add Sprint/iomanip style formatting methods (oct, hex, hexu, pad).
parent 6d116e95
This diff is collapsed.
......@@ -28,6 +28,8 @@
#ifndef FORMAT_H_
#define FORMAT_H_
#include <stdint.h>
#include <cstddef>
#include <cstdio>
#include <cstring>
......@@ -114,6 +116,25 @@ void Array<T, SIZE>::append(const T *begin, const T *end) {
size_ += num_elements;
}
// Information about an integer type.
template <typename T>
struct IntTraits {
typedef T UnsignedType;
static bool IsNegative(T) { return false; }
};
template <>
struct IntTraits<int> {
typedef unsigned UnsignedType;
static bool IsNegative(int value) { return value < 0; }
};
template <>
struct IntTraits<long> {
typedef unsigned long UnsignedType;
static bool IsNegative(long value) { return value < 0; }
};
class ArgInserter;
}
......@@ -159,19 +180,129 @@ enum Alignment {
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
};
struct FormatSpec {
Alignment align;
unsigned flags;
unsigned width;
char type;
char fill;
// Flags.
enum { SIGN_FLAG = 1, PLUS_FLAG = 2, HASH_FLAG = 4 };
struct Spec {};
template <char TYPE>
struct TypeSpec : Spec {
Alignment align() const { return ALIGN_DEFAULT; }
unsigned width() const { return 0; }
bool sign_flag() const { return false; }
bool plus_flag() const { return false; }
bool hash_flag() const { return false; }
char type() const { return TYPE; }
char fill() const { return ' '; }
};
struct WidthSpec {
unsigned width_;
char fill_;
WidthSpec(unsigned width, char fill) : width_(width), fill_(fill) {}
unsigned width() const { return width_; }
char fill() const { return fill_; }
};
struct AlignSpec : WidthSpec {
Alignment align_;
AlignSpec(unsigned width, char fill)
: WidthSpec(width, fill), align_(ALIGN_DEFAULT) {}
Alignment align() const { return align_; }
};
template <char TYPE>
struct AlignTypeSpec : AlignSpec {
AlignTypeSpec(unsigned width, char fill) : AlignSpec(width, fill) {}
bool sign_flag() const { return false; }
bool plus_flag() const { return false; }
bool hash_flag() const { return false; }
char type() const { return TYPE; }
};
struct FormatSpec : AlignSpec {
unsigned flags_;
char type_;
FormatSpec(unsigned width = 0, char type = 0, char fill = ' ')
: align(ALIGN_DEFAULT), flags(0), width(width), type(type), fill(fill) {}
: AlignSpec(width, fill), flags_(0), type_(type) {}
Alignment align() const { return align_; }
bool sign_flag() const { return (flags_ & SIGN_FLAG) != 0; }
bool plus_flag() const { return (flags_ & PLUS_FLAG) != 0; }
bool hash_flag() const { return (flags_ & HASH_FLAG) != 0; }
char type() const { return type_; }
};
template <typename T, typename Spec>
class IntFormatter : public Spec {
private:
T value_;
public:
IntFormatter(T value, const Spec &spec = Spec())
: Spec(spec), value_(value) {}
T value() const { return value_; }
};
inline IntFormatter<int, TypeSpec<'o'> > oct(int value) {
return IntFormatter<int, TypeSpec<'o'> >(value, TypeSpec<'o'>());
}
inline IntFormatter<int, TypeSpec<'x'> > hex(int value) {
return IntFormatter<int, TypeSpec<'x'> >(value, TypeSpec<'x'>());
}
inline IntFormatter<int, TypeSpec<'X'> > hexu(int value) {
return IntFormatter<int, TypeSpec<'X'> >(value, TypeSpec<'X'>());
}
template <char TYPE>
inline IntFormatter<int, AlignTypeSpec<TYPE> > pad(
IntFormatter<int, TypeSpec<TYPE> > f, unsigned width, char fill = ' ') {
return IntFormatter<int, AlignTypeSpec<TYPE> >(
f.value(), AlignTypeSpec<TYPE>(width, fill));
}
inline IntFormatter<int, AlignTypeSpec<0> > pad(
int value, unsigned width, char fill = ' ') {
return IntFormatter<int, AlignTypeSpec<0> >(
value, AlignTypeSpec<0>(width, fill));
}
class BasicFormatter {
private:
static unsigned CountDigits(uint64_t n) {
unsigned count = 1;
for (;;) {
// Integer division is slow so do it for a group of four digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
if (n < 10) return count;
if (n < 100) return count + 1;
if (n < 1000) return count + 2;
if (n < 10000) return count + 3;
n /= 10000u;
count += 4;
}
}
static void FormatDecimal(char *buffer, uint64_t value, unsigned num_digits);
protected:
static void ReportUnknownType(char code, const char *type);
enum { INLINE_BUFFER_SIZE = 500 };
mutable internal::Array<char, INLINE_BUFFER_SIZE> buffer_; // Output buffer.
......@@ -183,11 +314,19 @@ class BasicFormatter {
return &buffer_[size];
}
char *PrepareFilledBuffer(unsigned size, const FormatSpec &spec, char sign);
char *PrepareFilledBuffer(unsigned size, const Spec &, char sign) {
char *p = GrowBuffer(size);
*p = sign;
return p + size - 1;
}
char *PrepareFilledBuffer(unsigned size, const AlignSpec &spec, char sign);
// Formats an integer.
template <typename T>
void FormatInt(T value, const FormatSpec &spec);
void FormatInt(T value, const FormatSpec &spec) {
*this << IntFormatter<T, FormatSpec>(value, spec);
}
// Formats a floating point number (double or long double).
template <typename T>
......@@ -231,23 +370,103 @@ class BasicFormatter {
*/
std::string str() const { return std::string(&buffer_[0], buffer_.size()); }
void operator<<(int value);
BasicFormatter &operator<<(int value) {
return *this << IntFormatter<int, TypeSpec<0> >(value, TypeSpec<0>());
}
BasicFormatter &operator<<(unsigned value) {
return *this << IntFormatter<unsigned, TypeSpec<0> >(value, TypeSpec<0>());
}
void operator<<(char value) {
BasicFormatter &operator<<(char value) {
*GrowBuffer(1) = value;
return *this;
}
void operator<<(const char *value) {
BasicFormatter &operator<<(const char *value) {
std::size_t size = std::strlen(value);
std::strncpy(GrowBuffer(size), value, size);
return *this;
}
BasicFormatter &Write(int value, const FormatSpec &spec) {
FormatInt(value, spec);
return *this;
template <typename T, typename Spec>
BasicFormatter &operator<<(const IntFormatter<T, Spec> &f);
void Write(const std::string &s, const FormatSpec &spec) {
FormatString(s.data(), s.size(), spec);
}
void Clear() {
buffer_.clear();
}
};
template <typename T, typename Spec>
BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
T value = f.value();
unsigned size = 0;
char sign = 0;
typedef typename internal::IntTraits<T>::UnsignedType UnsignedType;
UnsignedType abs_value = value;
if (internal::IntTraits<T>::IsNegative(value)) {
sign = '-';
++size;
abs_value = 0 - abs_value;
} else if (f.sign_flag()) {
sign = f.plus_flag() ? '+' : ' ';
++size;
}
switch (f.type()) {
case 0: case 'd': {
unsigned num_digits = BasicFormatter::CountDigits(abs_value);
char *p = PrepareFilledBuffer(size + num_digits, f, sign)
- num_digits + 1;
BasicFormatter::FormatDecimal(p, abs_value, num_digits);
break;
}
case 'x': case 'X': {
UnsignedType n = abs_value;
bool print_prefix = f.hash_flag();
if (print_prefix) size += 2;
do {
++size;
} while ((n >>= 4) != 0);
char *p = PrepareFilledBuffer(size, f, sign);
n = abs_value;
const char *digits = f.type() == 'x' ?
"0123456789abcdef" : "0123456789ABCDEF";
do {
*p-- = digits[n & 0xf];
} while ((n >>= 4) != 0);
if (print_prefix) {
*p-- = f.type();
*p = '0';
}
break;
}
case 'o': {
UnsignedType n = abs_value;
bool print_prefix = f.hash_flag();
if (print_prefix) ++size;
do {
++size;
} while ((n >>= 3) != 0);
char *p = PrepareFilledBuffer(size, f, sign);
n = abs_value;
do {
*p-- = '0' + (n & 7);
} while ((n >>= 3) != 0);
if (print_prefix)
*p = '0';
break;
}
default:
BasicFormatter::ReportUnknownType(f.type(), "integer");
break;
}
return *this;
}
/**
\rst
The :cpp:class:`format::Formatter` class provides string formatting
......@@ -382,7 +601,6 @@ class Formatter : public BasicFormatter {
int next_arg_index_;
friend class internal::ArgInserter;
friend class ArgFormatter;
void Add(const Arg &arg) {
args_.push_back(&arg);
......@@ -522,34 +740,18 @@ const char *c_str(ArgInserter::Proxy p);
using format::internal::str;
using format::internal::c_str;
// ArgFormatter provides access to the format buffer within custom
// Format functions. It is not desirable to pass Formatter to these
// functions because Formatter::operator() is not reentrant and
// therefore can't be used for argument formatting.
class ArgFormatter {
private:
Formatter &formatter_;
public:
explicit ArgFormatter(Formatter &f) : formatter_(f) {}
void Write(const std::string &s, const FormatSpec &spec) {
formatter_.FormatString(s.data(), s.size(), spec);
}
};
// The default formatting function.
template <typename T>
void Format(ArgFormatter &af, const FormatSpec &spec, const T &value) {
void Format(BasicFormatter &f, const FormatSpec &spec, const T &value) {
std::ostringstream os;
os << value;
af.Write(os.str(), spec);
f.Write(os.str(), spec);
}
template <typename T>
void Formatter::FormatCustomArg(const void *arg, const FormatSpec &spec) {
ArgFormatter af(*this);
Format(af, spec, *static_cast<const T*>(arg));
BasicFormatter &f = *this;
Format(f, spec, *static_cast<const T*>(arg));
}
inline internal::ArgInserter Formatter::operator()(StringRef format) {
......
......@@ -45,10 +45,15 @@ using std::size_t;
using std::sprintf;
using fmt::internal::Array;
using fmt::BasicFormatter;
using fmt::Formatter;
using fmt::Format;
using fmt::FormatError;
using fmt::StringRef;
using fmt::hex;
using fmt::hexu;
using fmt::oct;
using fmt::pad;
#define FORMAT_TEST_THROW_(statement, expected_exception, message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
......@@ -844,21 +849,6 @@ TEST(FormatterTest, FormatString) {
EXPECT_EQ("test", str(Format("{0}") << std::string("test")));
}
TEST(ArgFormatterTest, Write) {
Formatter formatter;
fmt::ArgFormatter format(formatter);
fmt::FormatSpec spec;
spec.width = 2;
format.Write("12", spec);
EXPECT_EQ("12", formatter.str());
spec.width = 4;
format.Write("34", spec);
EXPECT_EQ("1234 ", formatter.str());
spec.width = 0;
format.Write("56", spec);
EXPECT_EQ("1234 56", formatter.str());
}
class Date {
int year_, month_, day_;
public:
......@@ -880,8 +870,8 @@ TEST(FormatterTest, FormatUsingIOStreams) {
class Answer {};
void Format(fmt::ArgFormatter &af, const fmt::FormatSpec &spec, Answer) {
af.Write("42", spec);
void Format(fmt::BasicFormatter &f, const fmt::FormatSpec &spec, Answer) {
f.Write("42", spec);
}
TEST(FormatterTest, CustomFormat) {
......@@ -1063,6 +1053,33 @@ TEST(TempFormatterTest, Examples) {
ReportError("File not found: {0}") << path;
}
TEST(StrTest, oct) {
BasicFormatter f;
f << oct(042);
EXPECT_EQ("42", f.str());
}
TEST(StrTest, hex) {
BasicFormatter f;
f << hex(0xbeef);
EXPECT_EQ("beef", f.str());
}
TEST(StrTest, hexu) {
BasicFormatter f;
f << hexu(0xbabe);
EXPECT_EQ("BABE", f.str());
}
TEST(StrTest, pad) {
BasicFormatter f;
f << pad(hex(0xbeef), 8);
EXPECT_EQ(" beef", f.str());
f.Clear();
f << pad(42, 5, '0');
EXPECT_EQ("00042", f.str());
}
template <typename T>
std::string str(const T &value) {
return fmt::str(fmt::Format("{0}") << value);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment