pull/254/head
gabime 9 years ago
parent eff486dbae
commit 43a4048b92

@ -18,107 +18,112 @@ void user_defined_example();
namespace spd = spdlog; namespace spd = spdlog;
int main(int, char*[]) int main(int, char*[])
{ {
try { try
// Multithreaded color console {
auto console = spd::stdout_logger_mt("console", true); // Multithreaded color console
console->info("Welcome to spdlog!"); auto console = spd::stdout_logger_mt("console", true);
console->error("An info message example {}..", 1); console->info("Welcome to spdlog!");
console->error("An info message example {}..", 1);
// Formatting examples // Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12); console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456); console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported"); console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned"); console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned"); console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered"); console->info("{:^30}", "centered");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Runtime log levels // Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!"); console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed.."); console->debug("This message shold be displayed..");
// Create basic file logger (not rotated) // Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt"); auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
my_logger->info("Some log message"); my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files // Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3); auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i); rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// Create a daily logger - a new file is created every day on 2:30am // Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30); auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
daily_logger->info(123.44); daily_logger->info(123.44);
// Customize msg format for all messages // Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format"); rotating_logger->info("This is another message with custom format");
// Compile time debug or trace macros. // Compile time debug or trace macros.
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON // Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
// Asynchronous logging is very fast.. // Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example(); async_example();
// syslog example. linux/osx only.. // syslog example. linux/osx only..
syslog_example(); syslog_example();
// log user-defined types example.. // log user-defined types example..
user_defined_example(); user_defined_example();
// Release and close all loggers // Release and close all loggers
spdlog::drop_all(); spdlog::drop_all();
} }
catch (const spd::spdlog_ex& ex) { catch (const spd::spdlog_ex& ex)
std::cout << "Log failed: " << ex.what() << std::endl; {
return EXIT_FAILURE; std::cout << "Log failed: " << ex.what() << std::endl;
} return EXIT_FAILURE;
return EXIT_SUCCESS; }
return EXIT_SUCCESS;
} }
void async_example() void async_example()
{ {
size_t q_size = 4096; //queue size must be power of 2 size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size); spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i); async_file->info("Async message #{}", i);
} }
//syslog example (linux/osx only) //syslog example (linux/osx only)
void syslog_example() void syslog_example()
{ {
#if defined (__linux__) || defined(__APPLE__) #if defined (__linux__) || defined(__APPLE__)
std::string ident = "spdlog-example"; std::string ident = "spdlog-example";
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID); auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!"); syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!");
#endif #endif
} }
// user defined types logging by implementing operator<< // user defined types logging by implementing operator<<
struct my_type struct my_type
{ {
int i; int i;
template<typename OStream> template<typename OStream>
friend OStream& operator<<(OStream& os, const my_type &c) {return os << "[my_type i="<<c.i << "]";} friend OStream& operator<<(OStream& os, const my_type &c)
{
return os << "[my_type i="<<c.i << "]";
}
}; };
#include <spdlog/fmt/ostr.h> // must be included #include <spdlog/fmt/ostr.h> // must be included
void user_defined_example() void user_defined_example()
{ {
spd::get("console")->info("user defined type: {}", my_type{ 14 }); spd::get("console")->info("user defined type: {}", my_type { 14 });
} }

File diff suppressed because it is too large Load Diff

@ -10,78 +10,89 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
// Commented out by spdlog to use header only // Commented out by spdlog to use header only
// #include "fmt/format.h" // #include "fmt/format.h"
#include <ostream> #include <ostream>
namespace fmt { namespace fmt
{
namespace internal { namespace internal
{
template <class Char> template <class Char>
class FormatBuf : public std::basic_streambuf<Char> { class FormatBuf : public std::basic_streambuf<Char>
private: {
typedef typename std::basic_streambuf<Char>::int_type int_type; private:
typedef typename std::basic_streambuf<Char>::traits_type traits_type; 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_; Buffer<Char> &buffer_;
Char *start_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) { public:
this->setp(start_, start_ + buffer_.capacity()); 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 buf_size = size();
buffer_.resize(buf_size);
buffer_.reserve(buf_size * 2);
start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
} }
return ch;
}
size_t size() const { int_type overflow(int_type ch = traits_type::eof())
return to_unsigned(this->pptr() - start_); {
} if (!traits_type::eq_int_type(ch, traits_type::eof()))
{
size_t buf_size = size();
buffer_.resize(buf_size);
buffer_.reserve(buf_size * 2);
start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
}
return ch;
}
size_t size() const
{
return to_unsigned(this->pptr() - start_);
}
}; };
Yes &convert(std::ostream &); Yes &convert(std::ostream &);
struct DummyStream : std::ostream { struct DummyStream : std::ostream
DummyStream(); // Suppress a bogus warning in MSVC. {
// Hide all operator<< overloads from std::ostream. DummyStream(); // Suppress a bogus warning in MSVC.
void operator<<(Null<>); // Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
}; };
No &operator<<(std::ostream &, int); No &operator<<(std::ostream &, int);
template<typename T> template<typename T>
struct ConvertToIntImpl<T, true> { struct ConvertToIntImpl<T, true>
// Convert to int only if T doesn't have an overloaded operator<<. {
enum { // Convert to int only if T doesn't have an overloaded operator<<.
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No) enum
}; {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
}; };
} // namespace internal } // namespace internal
// Formats a value. // Formats a value.
template <typename Char, typename ArgFormatter, typename T> template <typename Char, typename ArgFormatter, typename T>
void format(BasicFormatter<Char, ArgFormatter> &f, void format(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value) { const Char *&format_str, const T &value)
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer); internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
output << value; output << value;
BasicStringRef<Char> str(&buffer[0], format_buf.size()); BasicStringRef<Char> str(&buffer[0], format_buf.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg; typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str)); format_str = f.format(format_str, MakeArg(str));
} }
/** /**

@ -15,60 +15,81 @@
#include "fmt/format.h" #include "fmt/format.h"
namespace fmt { namespace fmt
namespace internal { {
namespace internal
{
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> template <bool IsSigned>
struct IntChecker { struct IntChecker
template <typename T> {
static bool fits_in_int(T value) { template <typename T>
unsigned max = std::numeric_limits<int>::max(); static bool fits_in_int(T value)
return value <= max; {
} unsigned max = std::numeric_limits<int>::max();
static bool fits_in_int(bool) { return true; } return value <= max;
}
static bool fits_in_int(bool)
{
return true;
}
}; };
template <> template <>
struct IntChecker<true> { struct IntChecker<true>
template <typename T> {
static bool fits_in_int(T value) { template <typename T>
return value >= std::numeric_limits<int>::min() && static bool fits_in_int(T value)
value <= std::numeric_limits<int>::max(); {
} return value >= std::numeric_limits<int>::min() &&
static bool fits_in_int(int) { return true; } value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int)
{
return true;
}
}; };
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> { class PrecisionHandler : public ArgVisitor<PrecisionHandler, int>
public: {
void report_unhandled_arg() { public:
FMT_THROW(FormatError("precision is not integer")); void report_unhandled_arg()
} {
FMT_THROW(FormatError("precision is not integer"));
template <typename T> }
int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) template <typename T>
FMT_THROW(FormatError("number is too big")); int visit_any_int(T value)
return static_cast<int>(value); {
} if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
}; };
// IsZeroInt::visit(arg) returns true iff arg is a zero integer. // IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> { class IsZeroInt : public ArgVisitor<IsZeroInt, bool>
public: {
template <typename T> public:
bool visit_any_int(T value) { return value == 0; } template <typename T>
bool visit_any_int(T value)
{
return value == 0;
}
}; };
template <typename T, typename U> template <typename T, typename U>
struct is_same { struct is_same
enum { value = 0 }; {
enum { value = 0 };
}; };
template <typename T> template <typename T>
struct is_same<T, T> { struct is_same<T, T>
enum { value = 1 }; {
enum { value = 1 };
}; };
// An argument visitor that converts an integer argument to T for printf, // An argument visitor that converts an integer argument to T for printf,
@ -76,99 +97,117 @@ struct is_same<T, T> {
// corresponding signed or unsigned type depending on the type specifier: // corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned) // 'd' and 'i' - signed, other - unsigned)
template <typename T = void> template <typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> { class ArgConverter : public ArgVisitor<ArgConverter<T>, void>
private: {
internal::Arg &arg_; private:
wchar_t type_; internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(internal::Arg &arg, wchar_t type) public:
: arg_(arg), type_(type) {} ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
void visit_bool(bool value) {
if (type_ != 's') void visit_bool(bool value)
visit_any_int(value); {
} if (type_ != 's')
visit_any_int(value);
template <typename U> }
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i'; template <typename U>
using internal::Arg; void visit_any_int(U value)
typedef typename internal::Conditional< {
bool is_signed = type_ == 'd' || type_ == 'i';
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType; is_same<T, void>::value, U, T>::type TargetType;
if (sizeof(TargetType) <= sizeof(int)) { if (sizeof(TargetType) <= sizeof(int))
// Extra casts are used to silence warnings. {
if (is_signed) { // Extra casts are used to silence warnings.
arg_.type = Arg::INT; if (is_signed)
arg_.int_value = static_cast<int>(static_cast<TargetType>(value)); {
} else { arg_.type = Arg::INT;
arg_.type = Arg::UINT; arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned; }
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value)); else
} {
} else { arg_.type = Arg::UINT;
if (is_signed) { typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
arg_.type = Arg::LONG_LONG; arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
// glibc's printf doesn't sign extend arguments of smaller types: }
// std::printf("%lld", -42); // prints "4294967254" }
// but we don't have to do the same because it's a UB. else
arg_.long_long_value = static_cast<LongLong>(value); {
} else { if (is_signed)
arg_.type = Arg::ULONG_LONG; {
arg_.ulong_long_value = arg_.type = Arg::LONG_LONG;
static_cast<typename internal::MakeUnsigned<U>::Type>(value); // glibc's printf doesn't sign extend arguments of smaller types:
} // std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_.long_long_value = static_cast<LongLong>(value);
}
else
{
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
}
}
} }
}
}; };
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void> { class CharConverter : public ArgVisitor<CharConverter, void>
private: {
internal::Arg &arg_; private:
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public: public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {} explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
template <typename T> template <typename T>
void visit_any_int(T value) { void visit_any_int(T value)
arg_.type = internal::Arg::CHAR; {
arg_.int_value = static_cast<char>(value); arg_.type = internal::Arg::CHAR;
} arg_.int_value = static_cast<char>(value);
}
}; };
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> { class WidthHandler : public ArgVisitor<WidthHandler, unsigned>
private: {
FormatSpec &spec_; private:
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() {
FMT_THROW(FormatError("width is not integer")); void report_unhandled_arg()
} {
FMT_THROW(FormatError("width is not integer"));
template <typename T> }
unsigned visit_any_int(T value) {
typedef typename internal::IntTraits<T>::MainType UnsignedType; template <typename T>
UnsignedType width = static_cast<UnsignedType>(value); unsigned visit_any_int(T value)
if (internal::is_negative(value)) { {
spec_.align_ = ALIGN_LEFT; typedef typename internal::IntTraits<T>::MainType UnsignedType;
width = 0 - width; UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value))
{
spec_.align_ = ALIGN_LEFT;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
} }
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
}; };
} // namespace internal } // namespace internal
@ -190,302 +229,343 @@ class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
\endrst \endrst
*/ */
template <typename Impl, typename Char> template <typename Impl, typename Char>
class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char> { class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char>
private: {
void write_null_pointer() { private:
this->spec().type_ = 0; void write_null_pointer()
this->write("(nil)"); {
} this->spec().type_ = 0;
this->write("(nil)");
typedef internal::ArgFormatterBase<Impl, Char> Base; }
public: typedef internal::ArgFormatterBase<Impl, Char> Base;
/**
\rst public:
Constructs an argument formatter object. /**
*writer* is a reference to the output writer and *spec* contains format \rst
specifier information for standard argument types. Constructs an argument formatter object.
\endrst *writer* is a reference to the output writer and *spec* contains format
*/ specifier information for standard argument types.
BasicPrintfArgFormatter(BasicWriter<Char> &writer, FormatSpec &spec) \endrst
: internal::ArgFormatterBase<Impl, Char>(writer, spec) {} */
BasicPrintfArgFormatter(BasicWriter<Char> &writer, FormatSpec &spec)
/** Formats an argument of type ``bool``. */ : internal::ArgFormatterBase<Impl, Char>(writer, spec) {}
void visit_bool(bool value) {
FormatSpec &fmt_spec = this->spec(); /** Formats an argument of type ``bool``. */
if (fmt_spec.type_ != 's') void visit_bool(bool value)
return this->visit_any_int(value); {
fmt_spec.type_ = 0; FormatSpec &fmt_spec = this->spec();
this->write(value); if (fmt_spec.type_ != 's')
} return this->visit_any_int(value);
fmt_spec.type_ = 0;
/** Formats a character. */ this->write(value);
void visit_char(int value) { }
const FormatSpec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer(); /** Formats a character. */
if (fmt_spec.type_ && fmt_spec.type_ != 'c') void visit_char(int value)
w.write_int(value, fmt_spec); {
typedef typename BasicWriter<Char>::CharPtr CharPtr; const FormatSpec &fmt_spec = this->spec();
CharPtr out = CharPtr(); BasicWriter<Char> &w = this->writer();
if (fmt_spec.width_ > 1) { if (fmt_spec.type_ && fmt_spec.type_ != 'c')
Char fill = ' '; w.write_int(value, fmt_spec);
out = w.grow_buffer(fmt_spec.width_); typedef typename BasicWriter<Char>::CharPtr CharPtr;
if (fmt_spec.align_ != ALIGN_LEFT) { CharPtr out = CharPtr();
std::fill_n(out, fmt_spec.width_ - 1, fill); if (fmt_spec.width_ > 1)
out += fmt_spec.width_ - 1; {
} else { Char fill = ' ';
std::fill_n(out + 1, fmt_spec.width_ - 1, fill); out = w.grow_buffer(fmt_spec.width_);
} if (fmt_spec.align_ != ALIGN_LEFT)
} else { {
out = w.grow_buffer(1); std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1;
}
else
{
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
}
}
else
{
out = w.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
/** Formats a null-terminated C string. */
void visit_cstring(const char *value)
{
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
/** Formats a pointer. */
void visit_pointer(const void *value)
{
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c)
{
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
} }
*out = static_cast<Char>(value);
}
/** Formats a null-terminated C string. */
void visit_cstring(const char *value) {
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
/** Formats a pointer. */
void visit_pointer(const void *value) {
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c) {
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
}; };
/** The default printf argument formatter. */ /** The default printf argument formatter. */
template <typename Char> template <typename Char>
class PrintfArgFormatter class PrintfArgFormatter
: public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char> { : public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>
public: {
/** Constructs an argument formatter object. */ public:
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s) /** Constructs an argument formatter object. */
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s) {} PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s) {}
}; };
/** This template formats data and writes the output to a writer. */ /** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> > template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase { class PrintfFormatter : private internal::FormatterBase
private: {
BasicWriter<Char> &writer_; private:
BasicWriter<Char> &writer_;
void parse_flags(FormatSpec &spec, const Char *&s);
void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument. // Returns the argument with specified index or, if arg_index is equal
internal::Arg get_arg( // to the maximum unsigned value, the next argument.
const Char *s, internal::Arg get_arg(
unsigned arg_index = (std::numeric_limits<unsigned>::max)()); const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec); // Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
/** public:
\rst /**
Constructs a ``PrintfFormatter`` object. References to the arguments and \rst
the writer are stored in the formatter object so make sure they have Constructs a ``PrintfFormatter`` object. References to the arguments and
appropriate lifetimes. the writer are stored in the formatter object so make sure they have
\endrst appropriate lifetimes.
*/ \endrst
explicit PrintfFormatter(const ArgList &args, BasicWriter<Char> &w) */
: FormatterBase(args), writer_(w) {} explicit PrintfFormatter(const ArgList &args, BasicWriter<Char> &w)
: FormatterBase(args), writer_(w) {}
/** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicCStringRef<Char> format_str); /** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicCStringRef<Char> format_str);
}; };
template <typename Char, typename AF> template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) { void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s)
for (;;) { {
switch (*s++) { for (;;)
case '-': {
spec.align_ = ALIGN_LEFT; switch (*s++)
break; {
case '+': case '-':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG; spec.align_ = ALIGN_LEFT;
break; break;
case '0': case '+':
spec.fill_ = '0'; spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break; break;
case ' ': case '0':
spec.flags_ |= SIGN_FLAG; spec.fill_ = '0';
break; break;
case '#': case ' ':
spec.flags_ |= HASH_FLAG; spec.flags_ |= SIGN_FLAG;
break; break;
default: case '#':
--s; spec.flags_ |= HASH_FLAG;
return; break;
default:
--s;
return;
}
} }
}
} }
template <typename Char, typename AF> template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s, internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index) { unsigned arg_index)
(void)s; {
const char *error = 0; (void)s;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ? const char *error = 0;
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
if (error) next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
FMT_THROW(FormatError(!*s ? "invalid format string" : error)); if (error)
return arg; FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
} }
template <typename Char, typename AF> template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header( unsigned PrintfFormatter<Char, AF>::parse_header(
const Char *&s, FormatSpec &spec) { const Char *&s, FormatSpec &spec)
unsigned arg_index = std::numeric_limits<unsigned>::max(); {
Char c = *s; unsigned arg_index = std::numeric_limits<unsigned>::max();
if (c >= '0' && c <= '9') { Char c = *s;
// Parse an argument index (if followed by '$') or a width possibly if (c >= '0' && c <= '9')
// preceded with '0' flag(s). {
unsigned value = internal::parse_nonnegative_int(s); // Parse an argument index (if followed by '$') or a width possibly
if (*s == '$') { // value is an argument index // preceded with '0' flag(s).
++s; unsigned value = internal::parse_nonnegative_int(s);
arg_index = value; if (*s == '$') // value is an argument index
} else { {
if (c == '0') ++s;
spec.fill_ = '0'; arg_index = value;
if (value != 0) { }
// Nonzero value means that we parsed width and don't need to else
// parse it or flags again, so return now. {
spec.width_ = value; if (c == '0')
return arg_index; spec.fill_ = '0';
} if (value != 0)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
} }
} parse_flags(spec, s);
parse_flags(spec, s); // Parse width.
// Parse width. if (*s >= '0' && *s <= '9')
if (*s >= '0' && *s <= '9') { {
spec.width_ = internal::parse_nonnegative_int(s); spec.width_ = internal::parse_nonnegative_int(s);
} else if (*s == '*') {
++s;
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) {
const Char *start = format_str.c_str();
const Char *s = start;
while (*s) {
Char c = *s++;
if (c != '%') continue;
if (*s == c) {
write(writer_, start, s);
start = ++s;
continue;
} }
write(writer_, start, s - 1); else if (*s == '*')
{
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.') {
++s;
if ('0' <= *s && *s <= '9') {
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
} else if (*s == '*') {
++s; ++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s)); spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
}
using internal::Arg;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::ArgConverter;
switch (*s++) {
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
} }
return arg_index;
}
// Parse type. template <typename Char, typename AF>
if (!*s) void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
FMT_THROW(FormatError("invalid format string")); {
spec.type_ = static_cast<char>(*s++); const Char *start = format_str.c_str();
if (arg.type <= Arg::LAST_INTEGER_TYPE) { const Char *s = start;
// Normalize type. while (*s)
switch (spec.type_) { {
case 'i': case 'u': Char c = *s++;
spec.type_ = 'd'; if (c != '%') continue;
break; if (*s == c)
case 'c': {
// TODO: handle wchar_t write(writer_, start, s);
internal::CharConverter(arg).visit(arg); start = ++s;
break; continue;
} }
write(writer_, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.')
{
++s;
if ('0' <= *s && *s <= '9')
{
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
}
else if (*s == '*')
{
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
}
}
using internal::Arg;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0')
{
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::ArgConverter;
switch (*s++)
{
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (arg.type <= Arg::LAST_INTEGER_TYPE)
{
// Normalize type.
switch (spec.type_)
{
case 'i':
case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
internal::CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
AF(writer_, spec).visit(arg);
} }
write(writer_, start, s);
start = s;
// Format argument.
AF(writer_, spec).visit(arg);
}
write(writer_, start, s);
} }
template <typename Char> template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args) { void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
PrintfFormatter<Char>(args, w).format(format); {
PrintfFormatter<Char>(args, w).format(format);
} }
/** /**
@ -497,17 +577,19 @@ void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args) {
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\endrst \endrst
*/ */
inline std::string sprintf(CStringRef format, ArgList args) { inline std::string sprintf(CStringRef format, ArgList args)
MemoryWriter w; {
printf(w, format, args); MemoryWriter w;
return w.str(); printf(w, format, args);
return w.str();
} }
FMT_VARIADIC(std::string, sprintf, CStringRef) FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args) { inline std::wstring sprintf(WCStringRef format, ArgList args)
WMemoryWriter w; {
printf(w, format, args); WMemoryWriter w;
return w.str(); printf(w, format, args);
return w.str();
} }
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
@ -532,8 +614,9 @@ FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
inline int printf(CStringRef format, ArgList args) { inline int printf(CStringRef format, ArgList args)
return fprintf(stdout, format, args); {
return fprintf(stdout, format, args);
} }
FMT_VARIADIC(int, printf, CStringRef) FMT_VARIADIC(int, printf, CStringRef)
} // namespace fmt } // namespace fmt

Loading…
Cancel
Save