This commit is contained in:
gabime 2016-07-22 20:19:26 +03:00
parent eff486dbae
commit 43a4048b92
7 changed files with 3292 additions and 2580 deletions

View File

@ -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

View File

@ -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_; Buffer<Char> &buffer_;
Char *start_; Char *start_;
public: public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) { FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0])
this->setp(start_, start_ + buffer_.capacity()); {
} 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));
} }
/** /**

View File

@ -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> template <typename T>
int visit_any_int(T value) { int visit_any_int(T value)
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) {
FMT_THROW(FormatError("number is too big")); if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
return static_cast<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: public:
ArgConverter(internal::Arg &arg, wchar_t type) ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {} : arg_(arg), type_(type) {}
void visit_bool(bool value) { void visit_bool(bool value)
if (type_ != 's') {
visit_any_int(value); if (type_ != 's')
} visit_any_int(value);
}
template <typename U>
void visit_any_int(U value) { template <typename U>
bool is_signed = type_ == 'd' || type_ == 'i'; void visit_any_int(U value)
using internal::Arg; {
typedef typename internal::Conditional< bool is_signed = type_ == 'd' || type_ == 'i';
is_same<T, void>::value, U, T>::type TargetType; using internal::Arg;
if (sizeof(TargetType) <= sizeof(int)) { typedef typename internal::Conditional<
// Extra casts are used to silence warnings. is_same<T, void>::value, U, T>::type TargetType;
if (is_signed) { if (sizeof(TargetType) <= sizeof(int))
arg_.type = Arg::INT; {
arg_.int_value = static_cast<int>(static_cast<TargetType>(value)); // Extra casts are used to silence warnings.
} else { if (is_signed)
arg_.type = Arg::UINT; {
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned; arg_.type = Arg::INT;
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value)); arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
} }
} else { else
if (is_signed) { {
arg_.type = Arg::LONG_LONG; arg_.type = Arg::UINT;
// glibc's printf doesn't sign extend arguments of smaller types: typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
// std::printf("%lld", -42); // prints "4294967254" arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
// but we don't have to do the same because it's a UB. }
arg_.long_long_value = static_cast<LongLong>(value); }
} else { else
arg_.type = Arg::ULONG_LONG; {
arg_.ulong_long_value = if (is_signed)
static_cast<typename internal::MakeUnsigned<U>::Type>(value); {
} arg_.type = Arg::LONG_LONG;
// 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: public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() { void report_unhandled_arg()
FMT_THROW(FormatError("width is not integer")); {
} FMT_THROW(FormatError("width is not integer"));
}
template <typename T>
unsigned visit_any_int(T value) { template <typename T>
typedef typename internal::IntTraits<T>::MainType UnsignedType; unsigned visit_any_int(T value)
UnsignedType width = static_cast<UnsignedType>(value); {
if (internal::is_negative(value)) { typedef typename internal::IntTraits<T>::MainType UnsignedType;
spec_.align_ = ALIGN_LEFT; UnsignedType width = static_cast<UnsignedType>(value);
width = 0 - width; 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:
/**
\rst
Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &writer, FormatSpec &spec)
: internal::ArgFormatterBase<Impl, Char>(writer, spec) {}
/** Formats an argument of type ``bool``. */
void visit_bool(bool value) {
FormatSpec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
this->write(value);
}
/** Formats a character. */
void visit_char(int value) {
const FormatSpec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (fmt_spec.width_ > 1) {
Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT) {
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. */ typedef internal::ArgFormatterBase<Impl, Char> Base;
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. */ public:
void visit_pointer(const void *value) { /**
if (value) \rst
return Base::visit_pointer(value); Constructs an argument formatter object.
this->spec().type_ = 0; *writer* is a reference to the output writer and *spec* contains format
write_null_pointer(); specifier information for standard argument types.
} \endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &writer, FormatSpec &spec)
: internal::ArgFormatterBase<Impl, Char>(writer, spec) {}
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of type ``bool``. */
void visit_custom(internal::Arg::CustomValue c) { void visit_bool(bool value)
BasicFormatter<Char> formatter(ArgList(), this->writer()); {
const Char format_str[] = {'}', 0}; FormatSpec &fmt_spec = this->spec();
const Char *format = format_str; if (fmt_spec.type_ != 's')
c.format(&formatter, c.value, &format); return this->visit_any_int(value);
} fmt_spec.type_ = 0;
this->write(value);
}
/** Formats a character. */
void visit_char(int value)
{
const FormatSpec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (fmt_spec.width_ > 1)
{
Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT)
{
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);
}
}; };
/** 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 // Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument. // to the maximum unsigned value, the next argument.
internal::Arg get_arg( internal::Arg get_arg(
const Char *s, const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)()); unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index. // Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec); unsigned parse_header(const Char *&s, FormatSpec &spec);
public: public:
/** /**
\rst \rst
Constructs a ``PrintfFormatter`` object. References to the arguments and Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have the writer are stored in the formatter object so make sure they have
appropriate lifetimes. appropriate lifetimes.
\endrst \endrst
*/ */
explicit PrintfFormatter(const ArgList &args, BasicWriter<Char> &w) explicit PrintfFormatter(const ArgList &args, BasicWriter<Char> &w)
: FormatterBase(args), writer_(w) {} : FormatterBase(args), writer_(w) {}
/** Formats stored arguments and writes the output to the writer. */ /** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicCStringRef<Char> format_str); 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; else if (*s == '*')
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s)); {
} ++s;
return arg_index; spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
} }
template <typename Char, typename AF> template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) { void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
const Char *start = format_str.c_str(); {
const Char *s = start; const Char *start = format_str.c_str();
while (*s) { const Char *s = start;
Char c = *s++; while (*s)
if (c != '%') continue; {
if (*s == c) { Char c = *s++;
write(writer_, start, s); if (c != '%') continue;
start = ++s; if (*s == c)
continue; {
write(writer_, start, s);
start = ++s;
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 - 1); write(writer_, start, 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;
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);
} }
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