Compare commits

...

15 Commits

Author SHA1 Message Date
gabime
f85a08622e version 0.13.0 2017-03-28 02:09:01 +03:00
gabime
0c276beaaf astyle 2017-03-28 02:08:18 +03:00
gabime
5a8cecdfb6 fix unused warning message 2017-03-28 02:05:59 +03:00
gabime
397d4866b3 Fixed issue #396 and added some tests to catch it 2017-03-28 01:54:33 +03:00
Gabi Melman
27df6eb4ca Merge pull request #403 from Falconne/master
Disambiguate fmt logging methods that are using variadic templates.
2017-03-28 00:27:29 +03:00
Anuradha Dissanayake
ad1c18704d Disambiguate fmt logging methods that are using variadic templates.
As variadic template arguments can be zero length, we need to specify that at least one fmt argument is provided, to distinguish these methods from the existing trivial method that takes no fmt arguments.

Without this, static analysers such as ReSharper flag the logging calls as errors.
2017-03-27 08:58:03 +13:00
Gabi Melman
029e6ed40f Merge pull request #399 from devanshdalal/patch-1
Update README.md
2017-03-22 08:41:27 +00:00
Devansh D
a55615c984 Update README.md 2017-03-22 13:52:56 +05:30
Gabi Melman
e8da69ebe1 Merge pull request #394 from zamaudio/spdlog-updatefmt-3.0.1
fmt: update bundled fmt to 3.0.1 (7fa8f8f)
2017-03-20 14:41:06 +02:00
Damien Zammit
8192c13379 fmt: update bundled fmt to 3.0.1 (7fa8f8f)
Signed-off-by: Damien Zammit <damien@zamaudio.com>
2017-03-20 15:25:10 +11:00
Gabi Melman
270c08b275 Merge pull request #377 from tekezo/master
Use double-braces in std::array initialization.
2017-03-02 20:18:55 +02:00
Takayama Fumihiko
a4714a6571 use double-braces in std::array initialization 2017-03-03 01:37:53 +09:00
Gabi Melman
5585299b03 Merge pull request #375 from horar/master
Don't hardcode '.txt.' log file name suffix (resolve #333)
2017-02-28 09:19:07 +02:00
Ľubomír Carik
fd8df5b820 Don't hardcode '.txt.' log file name suffix (resolve #333)
Signed-off-by: Ľubomír Carik <Lubomir.Carik@gmail.com>
2017-02-28 00:59:23 +01:00
Gabi Melman
d7a8db8f63 Update README.md 2017-02-21 12:13:04 +02:00
22 changed files with 2436 additions and 2052 deletions

View File

@@ -4,7 +4,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Install
#### Just copy or clone the headers:
#### Just copy the headers:
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
@@ -24,7 +24,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* Mac OSX (clang 3.5+)
* Android
##Features
## Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use.
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.

View File

@@ -39,7 +39,7 @@ int main(int, char*[])
// 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");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
@@ -106,7 +106,7 @@ void async_example()
{
size_t q_size = 4096; //queue size must be power of 2
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");
for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i);

View File

@@ -63,6 +63,11 @@ public:
//Wait for the queue to be empty, and flush synchronously
//Warning: this can potentialy last forever as we wait it to complete
void flush() override;
// Error handler
virtual void set_error_handler(log_err_handler) override;
virtual log_err_handler error_handler() override;
protected:
void _sink_it(details::log_msg& msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override;

View File

@@ -66,7 +66,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
msg_type(std::move(other.msg_type))
{}
async_msg(async_msg_type m_type) :msg_type(m_type)
async_msg(async_msg_type m_type):msg_type(m_type)
{}
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
@@ -85,7 +85,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
async_msg& operator=(const async_msg& other) = delete;
// construct from log_msg
async_msg(const details::log_msg& m) :
async_msg(const details::log_msg& m):
level(m.level),
time(m.time),
thread_id(m.thread_id),
@@ -135,6 +135,7 @@ public:
void flush(bool wait_for_q);
void set_error_handler(spdlog::log_err_handler err_handler);
private:
formatter_ptr _formatter;
@@ -221,7 +222,8 @@ inline spdlog::details::async_log_helper::~async_log_helper()
_worker_thread.join();
}
catch (...) // don't crash in destructor
{}
{
}
}
@@ -250,19 +252,21 @@ inline void spdlog::details::async_log_helper::push_msg(details::async_log_helpe
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
{
push_msg(async_msg(async_msg_type::flush));
if(wait_for_q)
if (wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed
}
inline void spdlog::details::async_log_helper::worker_loop()
{
try
{
if (_worker_warmup_cb) _worker_warmup_cb();
auto last_pop = details::os::now();
auto last_flush = last_pop;
while(process_next_msg(last_pop, last_flush));
if (_worker_teardown_cb) _worker_teardown_cb();
auto active = true;
while (active)
{
try
{
active = process_next_msg(last_pop, last_flush);
}
catch (const std::exception &ex)
{
@@ -272,6 +276,10 @@ inline void spdlog::details::async_log_helper::worker_loop()
{
_err_handler("Unknown exception");
}
}
if (_worker_teardown_cb) _worker_teardown_cb();
}
// process next message in the queue
@@ -300,7 +308,7 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
_formatter->format(incoming_log_msg);
for (auto &s : _sinks)
{
if(s->should_log( incoming_log_msg.level))
if (s->should_log(incoming_log_msg.level))
{
s->log(incoming_log_msg);
}
@@ -374,5 +382,10 @@ inline void spdlog::details::async_log_helper::wait_empty_q()
}
}
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
}

View File

@@ -57,6 +57,19 @@ inline void spdlog::async_logger::flush()
_async_log_helper->flush(true);
}
// Error handler
inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
_async_log_helper->set_error_handler(err_handler);
}
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
{
return _err_handler;
}
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
{
_formatter = msg_formatter;

View File

@@ -121,44 +121,42 @@ inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
}
template <typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::trace, fmt, args...);
log(level::trace, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::debug, fmt, args...);
log(level::debug, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::info(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::info, fmt, args...);
log(level::info, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::warn, fmt, args...);
log(level::warn, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::error(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::err, fmt, args...);
log(level::err, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::critical, fmt, args...);
log(level::critical, fmt, arg1, args...);
}
template<typename T>
inline void spdlog::logger::trace(const T& msg)
{

View File

@@ -82,7 +82,7 @@ static int to12h(const tm& t)
using days_array = std::array<std::string, 7>;
static const days_array& days()
{
static const days_array arr{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const days_array arr{ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } };
return arr;
}
class a_formatter:public flag_formatter
@@ -96,7 +96,7 @@ class a_formatter:public flag_formatter
//Full weekday name
static const days_array& full_days()
{
static const days_array arr{ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
static const days_array arr{ { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" } };
return arr;
}
class A_formatter:public flag_formatter
@@ -111,7 +111,7 @@ class A_formatter:public flag_formatter
using months_array = std::array<std::string, 12>;
static const months_array& months()
{
static const months_array arr{ "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
static const months_array arr{ { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" } };
return arr;
}
class b_formatter:public flag_formatter
@@ -125,7 +125,7 @@ class b_formatter:public flag_formatter
//Full month name
static const months_array& full_months()
{
static const months_array arr{ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
static const months_array arr{ { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" } };
return arr;
}
class B_formatter:public flag_formatter

View File

@@ -61,23 +61,23 @@ inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string
// Create multi/single threaded rotating file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
}
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
}
// Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
}
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,19 @@
/*
Formatting library for C++ - std::ostream support
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
For the license information refer to format.h.
*/
#include "ostream.h"
namespace fmt {
namespace internal {
FMT_FUNC void write(std::ostream &os, Writer &w)
{
namespace {
// Write the content of w to os.
void write(std::ostream &os, Writer &w) {
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
@@ -25,13 +25,19 @@ namespace fmt {
data += n;
size -= n;
} while (size != 0);
}
}
}
}
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args)
{
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
internal::write(os, w);
}
write(os, w);
}
FMT_FUNC int fprintf(std::ostream &os, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
write(os, w);
return static_cast<int>(w.size());
}
} // namespace fmt

View File

@@ -1,17 +1,16 @@
/*
Formatting library for C++ - std::ostream support
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
For the license information refer to format.h.
*/
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
// commented out by spdlog
// #include "format.h"
#include "format.h"
#include <ostream>
namespace fmt
@@ -21,7 +20,7 @@ namespace internal
{
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;
@@ -31,7 +30,7 @@ private:
Char *start_;
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());
}
@@ -46,7 +45,7 @@ public:
start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_ + buf_size + 1, start_ + buf_size * 2);
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
}
return ch;
}
@@ -59,7 +58,7 @@ public:
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.
@@ -77,14 +76,11 @@ struct ConvertToIntImpl<T, true>
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
void write(std::ostream &os, Writer &w);
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter> &f,
void format(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value)
{
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
@@ -99,16 +95,28 @@ void format_arg(BasicFormatter<Char, ArgFormatter> &f,
}
/**
\rst
Prints formatted data to the stream *os*.
\rst
Prints formatted data to the stream *os*.
**Example**::
**Example**::
print(cerr, "Don't {}!", "panic");
\endrst
*/
print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY

View File

@@ -0,0 +1,238 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
// Disable bogus MSVC warnings.
#ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "posix.h"
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <unistd.h>
#else
# include <windows.h>
# include <io.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
typedef int RWResult;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
// Return type of read and write functions.
typedef ssize_t RWResult;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
}
fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
fmt::report_system_error(errno, "cannot close file");
}
fmt::BufferedFile::BufferedFile(
fmt::CStringRef filename, fmt::CStringRef mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
if (!file_)
FMT_THROW(SystemError(errno, "cannot open file {}", filename));
}
void fmt::BufferedFile::close() {
if (!file_)
return;
int result = FMT_SYSTEM(fclose(file_));
file_ = 0;
if (result != 0)
FMT_THROW(SystemError(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int fmt::BufferedFile::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1)
FMT_THROW(SystemError(errno, "cannot get file descriptor"));
return fd;
}
fmt::File::File(fmt::CStringRef path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
#else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
if (fd_ == -1)
FMT_THROW(SystemError(errno, "cannot open file {}", path));
}
fmt::File::~File() FMT_NOEXCEPT {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
fmt::report_system_error(errno, "cannot close file");
}
void fmt::File::close() {
if (fd_ == -1)
return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0)
FMT_THROW(SystemError(errno, "cannot close file"));
}
fmt::LongLong fmt::File::size() const {
#ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
DWORD size_upper = 0;
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(WindowsError(GetLastError(), "cannot get file size"));
}
fmt::ULongLong long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
typedef struct stat Stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(SystemError(errno, "cannot get file attributes"));
FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size),
"return type of File::size is not large enough");
return file_stat.st_size;
#endif
}
std::size_t fmt::File::read(void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(SystemError(errno, "cannot read from file"));
return internal::to_unsigned(result);
}
std::size_t fmt::File::write(const void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(SystemError(errno, "cannot write to file"));
return internal::to_unsigned(result);
}
fmt::File fmt::File::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd));
return File(new_fd);
}
void fmt::File::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(SystemError(errno,
"cannot duplicate file descriptor {} to {}", fd_, fd));
}
}
void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1)
ec = ErrorCode(errno);
}
void fmt::File::pipe(File &read_end, File &write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
#else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0)
FMT_THROW(SystemError(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = File(fds[0]);
write_end = File(fds[1]);
}
fmt::BufferedFile fmt::File::fdopen(const char *mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
BufferedFile file(f);
fd_ = -1;
return file;
}
long fmt::getpagesize() {
#ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0)
FMT_THROW(SystemError(errno, "cannot get memory page size"));
return size;
#endif
}

View File

@@ -0,0 +1,443 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
#if FMT_GCC_VERSION >= 407
# define FMT_UNUSED __attribute__((unused))
#else
# define FMT_UNUSED
#endif
#ifndef FMT_USE_STATIC_ASSERT
# define FMT_USE_STATIC_ASSERT 0
#endif
#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \
(FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600
# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message)
#else
# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b)
# define FMT_STATIC_ASSERT(cond, message) \
typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
namespace fmt
{
// An error code.
class ErrorCode
{
private:
int value_;
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT :
value_(value) {}
int get() const FMT_NOEXCEPT
{
return value_;
}
};
// A buffered file.
class BufferedFile
{
private:
FILE *file_;
friend class File;
explicit BufferedFile(FILE *f) : file_(f) {}
public:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT :
file_(0) {}
// Destroys the object closing the file it represents if any.
~BufferedFile() FMT_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy
{
FILE *file;
};
public:
// A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT :
file_(p.file) {}
// A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT :
file_(f.file_)
{
f.file_ = 0;
}
// A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p)
{
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other)
{
close();
file_ = other.file_;
other.file_ = 0;
return *this;
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {file_};
file_ = 0;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
file_(other.file_)
{
other.file_ = 0;
}
BufferedFile& operator=(BufferedFile &&other)
{
close();
file_ = other.file_;
other.file_ = 0;
return *this;
}
#endif
// Opens a file.
BufferedFile(CStringRef filename, CStringRef mode);
// Closes the file.
void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT
{
return file_;
}
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
int (fileno)() const;
void print(CStringRef format_str, const ArgList &args)
{
fmt::print(file_, format_str, args);
}
FMT_VARIADIC(void, print, CStringRef)
};
// A file. Closed file is represented by a File object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::SystemError in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class File
{
private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum
{
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT :
fd_(-1) {}
// Opens a file and constructs a File object representing this file.
File(CStringRef path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy
{
int fd;
};
public:
// A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT :
fd_(p.fd) {}
// A "move constructor" for moving from an lvalue.
File(File &other) FMT_NOEXCEPT :
fd_(other.fd_)
{
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary.
File &operator=(Proxy p)
{
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
File &operator=(File &other)
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Returns a proxy object for moving from a temporary:
// File file = File(...);
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(File);
public:
File(File &&other) FMT_NOEXCEPT :
fd_(other.fd_)
{
other.fd_ = -1;
}
File& operator=(File &&other)
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// Destroys the object closing the file it represents if any.
~File() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT
{
return fd_;
}
// Closes the file.
void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer.
std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static void pipe(File &read_end, File &write_end);
// Creates a BufferedFile object associated with this file and detaches
// this File object from the file.
BufferedFile fdopen(const char *mode);
};
// Returns the memory page size.
long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__)
# define FMT_LOCALE
#endif
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale
{
private:
# ifdef _MSC_VER
typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t)
{
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale)
{
_free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
{
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
public:
typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", NULL))
{
if (!locale_)
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
}
~Locale()
{
freelocale(locale_);
}
Type get() const
{
return locale_;
}
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char *&str) const
{
char *end = 0;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
} // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES
namespace std
{
// For compatibility with C++98.
inline fmt::BufferedFile &move(fmt::BufferedFile &f)
{
return f;
}
inline fmt::File &move(fmt::File &f)
{
return f;
}
}
#endif
#endif // FMT_POSIX_H_

View File

@@ -1,658 +0,0 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "ostream.h"
namespace fmt
{
namespace internal
{
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker
{
template <typename T>
static bool fits_in_int(T value)
{
unsigned max = std::numeric_limits<int>::max();
return value <= max;
}
static bool fits_in_int(bool)
{
return true;
}
};
template <>
struct IntChecker<true>
{
template <typename T>
static bool fits_in_int(T value)
{
return value >= std::numeric_limits<int>::min() &&
value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int)
{
return true;
}
};
class PrecisionHandler: public ArgVisitor<PrecisionHandler, int>
{
public:
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))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
};
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt: public ArgVisitor<IsZeroInt, bool>
{
public:
template <typename T>
bool visit_any_int(T value)
{
return value == 0;
}
};
template <typename T, typename U>
struct is_same
{
enum
{
value = 0
};
};
template <typename T>
struct is_same<T, T>
{
enum
{
value = 1
};
};
// An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is void, the argument is converted to
// corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template <typename T = void>
class ArgConverter: public ArgVisitor<ArgConverter<T>, void>
{
private:
internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type)
{}
void visit_bool(bool value)
{
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i';
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType;
if (sizeof(TargetType) <= sizeof(int))
{
// Extra casts are used to silence warnings.
if (is_signed)
{
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
}
else
{
arg_.type = Arg::UINT;
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
}
}
else
{
if (is_signed)
{
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.
class CharConverter: public ArgVisitor<CharConverter, void>
{
private:
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(internal::Arg &arg): arg_(arg)
{}
template <typename T>
void visit_any_int(T 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
// left alignment if it is negative.
class WidthHandler: public ArgVisitor<WidthHandler, unsigned>
{
private:
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec): spec_(spec)
{}
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;
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);
}
};
} // namespace internal
/**
\rst
A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
will be called. If the subclass doesn't contain a method with this signature,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called.
\endrst
*/
template <typename Impl, typename Char>
class BasicPrintfArgFormatter: public internal::ArgFormatterBase<Impl, Char>
{
private:
void write_null_pointer()
{
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> &w, FormatSpec &s)
: internal::ArgFormatterBase<Impl, Char>(w, s)
{}
/** 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. */
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. */
template <typename Char>
class PrintfArgFormatter
: public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>
{
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s)
{}
};
/** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter: private internal::FormatterBase
{
private:
BasicWriter<Char> &writer_;
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.
internal::Arg get_arg(
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);
public:
/**
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w)
{}
/** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicCStringRef<Char> format_str);
};
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s)
{
for (;;)
{
switch (*s++)
{
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index)
{
(void)s;
const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header(
const Char *&s, FormatSpec &spec)
{
unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s;
if (c >= '0' && c <= '9')
{
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = internal::parse_nonnegative_int(s);
if (*s == '$') // value is an argument index
{
++s;
arg_index = value;
}
else
{
if (c == '0')
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 width.
if (*s >= '0' && *s <= '9')
{
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);
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>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
{
PrintfFormatter<Char>(args, w).format(format);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(CStringRef format, ArgList args)
{
MemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args)
{
WMemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(CStringRef format, ArgList args)
{
return fprintf(stdout, format, args);
}
FMT_VARIADIC(int, printf, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
{
MemoryWriter w;
printf(w, format_str, args);
internal::write(os, w);
return static_cast<int>(w.size());
}
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "printf.cc"
#endif
#endif // FMT_PRINTF_H_

View File

@@ -0,0 +1,58 @@
/*
Formatting library for C++ - time formatting
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_TIME_H_
#define FMT_TIME_H_
#include "format.h"
#include <ctime>
namespace fmt
{
template <typename ArgFormatter>
void format(BasicFormatter<char, ArgFormatter> &f,
const char *&format_str, const std::tm &tm)
{
if (*format_str == ':')
++format_str;
const char *end = format_str;
while (*end && *end != '}')
++end;
if (*end != '}')
FMT_THROW(FormatError("missing '}' in format string"));
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
format.append(format_str, end + 1);
format[format.size() - 1] = '\0';
Buffer<char> &buffer = f.writer().buffer();
std::size_t start = buffer.size();
for (;;)
{
std::size_t size = buffer.capacity() - start;
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
if (count != 0)
{
buffer.resize(start + count);
break;
}
if (size >= format.size() * 256)
{
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
format_str = end + 1;
}
}
#endif // FMT_TIME_H_

View File

@@ -37,12 +37,12 @@ public:
template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
template <typename... Args> void log(level::level_enum lvl, const char* msg);
template <typename... Args> void trace(const char* fmt, const Args&... args);
template <typename... Args> void debug(const char* fmt, const Args&... args);
template <typename... Args> void info(const char* fmt, const Args&... args);
template <typename... Args> void warn(const char* fmt, const Args&... args);
template <typename... Args> void error(const char* fmt, const Args&... args);
template <typename... Args> void critical(const char* fmt, const Args&... args);
template <typename Arg1, typename... Args> void trace(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void debug(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void info(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void warn(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void error(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void critical(const char* fmt, const Arg1&, const Args&... args);
template <typename T> void log(level::level_enum lvl, const T&);
template <typename T> void trace(const T&);
@@ -59,10 +59,6 @@ public:
void set_pattern(const std::string&);
void set_formatter(formatter_ptr);
// error handler
void set_error_handler(log_err_handler);
log_err_handler error_handler();
// automatically call flush() if message level >= log_level
void flush_on(level::level_enum log_level);
@@ -70,6 +66,10 @@ public:
const std::vector<sink_ptr>& sinks() const;
// error handler
virtual void set_error_handler(log_err_handler);
virtual log_err_handler error_handler();
protected:
virtual void _sink_it(details::log_msg&);
virtual void _set_pattern(const std::string&);

View File

@@ -64,16 +64,15 @@ template<class Mutex>
class rotating_file_sink : public base_sink < Mutex >
{
public:
rotating_file_sink(const filename_t &base_filename, const filename_t &extension,
std::size_t max_size, std::size_t max_files ) :
rotating_file_sink(const filename_t &base_filename,
std::size_t max_size, std::size_t max_files) :
_base_filename(base_filename),
_extension(extension),
_max_size(max_size),
_max_files(max_files),
_current_size(0),
_file_helper()
{
_file_helper.open(calc_filename(_base_filename, 0, _extension));
_file_helper.open(calc_filename(_base_filename, 0));
_current_size = _file_helper.size(); //expensive. called only once
}
@@ -95,21 +94,21 @@ protected:
}
private:
static filename_t calc_filename(const filename_t& filename, std::size_t index, const filename_t& extension)
static filename_t calc_filename(const filename_t& filename, std::size_t index)
{
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index)
w.write(SPDLOG_FILENAME_T("{}.{}.{}"), filename, index, extension);
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index);
else
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, extension);
w.write(SPDLOG_FILENAME_T("{}"), filename);
return w.str();
}
// Rotate files:
// log.txt -> log.1.txt
// log.1.txt -> log2.txt
// log.2.txt -> log3.txt
// log.3.txt -> delete
// log.txt -> log.txt.1
// log.txt.1 -> log.txt.2
// log.txt.2 -> log.txt.3
// lo3.txt.3 -> delete
void _rotate()
{
@@ -117,8 +116,8 @@ private:
_file_helper.close();
for (auto i = _max_files; i > 0; --i)
{
filename_t src = calc_filename(_base_filename, i - 1, _extension);
filename_t target = calc_filename(_base_filename, i, _extension);
filename_t src = calc_filename(_base_filename, i - 1);
filename_t target = calc_filename(_base_filename, i);
if (details::file_helper::file_exists(target))
{
@@ -135,7 +134,6 @@ private:
_file_helper.reopen(true);
}
filename_t _base_filename;
filename_t _extension;
std::size_t _max_size;
std::size_t _max_files;
std::size_t _current_size;
@@ -150,27 +148,27 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
*/
struct default_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD_hh-mm.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
// Create filename for the form basename.YYYY-MM-DD_hh-mm
static filename_t calc_filename(const filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
return w.str();
}
};
/*
* Generator of daily log file names in format basename.YYYY-MM-DD.extension
* Generator of daily log file names in format basename.YYYY-MM-DD
*/
struct dateonly_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
// Create filename for the form basename.YYYY-MM-DD
static filename_t calc_filename(const filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
return w.str();
}
};
@@ -185,17 +183,15 @@ public:
//create daily file sink which rotates on given time
daily_file_sink(
const filename_t& base_filename,
const filename_t& extension,
int rotation_hour,
int rotation_minute) : _base_filename(base_filename),
_extension(extension),
_rotation_h(rotation_hour),
_rotation_m(rotation_minute)
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
_rotation_tp = _next_rotation_tp();
_file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
}
void flush() override
@@ -208,7 +204,7 @@ protected:
{
if (std::chrono::system_clock::now() >= _rotation_tp)
{
_file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
_rotation_tp = _next_rotation_tp();
}
_file_helper.write(msg);
@@ -231,7 +227,6 @@ private:
}
filename_t _base_filename;
filename_t _extension;
int _rotation_h;
int _rotation_m;
std::chrono::system_clock::time_point _rotation_tp;

View File

@@ -7,7 +7,7 @@
#pragma once
#define SPDLOG_VERSION "0.12.0"
#define SPDLOG_VERSION "0.13.0"
#include <spdlog/tweakme.h>
#include <spdlog/common.h>
@@ -124,7 +124,7 @@ std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_b
// Create and register a logger with templated sink type
// Example:
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename");
template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);

View File

@@ -6,6 +6,19 @@
#include<iostream>
class failing_sink: public spdlog::sinks::sink
{
void log(const spdlog::details::log_msg& msg) override
{
throw std::runtime_error("some error happened during log");
}
void flush()
{}
};
TEST_CASE("default_error_handler", "[errors]]")
{
prepare_logdir();
@@ -22,7 +35,10 @@ TEST_CASE("default_error_handler", "[errors]]")
}
struct custom_ex {};
struct custom_ex
{};
TEST_CASE("custom_error_handler", "[errors]]")
{
prepare_logdir();
@@ -39,6 +55,17 @@ TEST_CASE("custom_error_handler", "[errors]]")
REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("default_error_handler2", "[errors]]")
{
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string& msg)
{
throw custom_ex();
});
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
}
TEST_CASE("async_error_handler", "[errors]]")
{
prepare_logdir();
@@ -62,3 +89,25 @@ TEST_CASE("async_error_handler", "[errors]]")
REQUIRE(count_lines(filename) == 2);
REQUIRE(file_contents("logs/custom_err.txt") == err_msg);
}
// Make sure async error handler is executed
TEST_CASE("async_error_handler2", "[errors]]")
{
prepare_logdir();
std::string err_msg("This is async handler error message");
spdlog::set_async_mode(128);
{
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string& msg)
{
std::ofstream ofs("logs/custom_err2.txt");
if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt");
ofs << err_msg;
});
logger->info("Hello failure");
spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown
spdlog::set_sync_mode();
}
REQUIRE(file_contents("logs/custom_err2.txt") == err_msg);
}

View File

@@ -7,7 +7,7 @@
TEST_CASE("simple_file_logger", "[simple_logger]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
@@ -24,7 +24,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
TEST_CASE("flush_on", "[flush_on]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
@@ -50,7 +50,7 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
logger->info("Test message {}", i);
logger->flush();
auto filename = basename + ".txt";
auto filename = basename;
REQUIRE(count_lines(filename) == 10);
}
@@ -64,14 +64,14 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
logger->info("Test message {}", i);
logger->flush();
auto filename = basename + ".txt";
auto filename = basename;
REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++)
logger->info("Test message {}", i);
logger->flush();
REQUIRE(get_filesize(filename) <= 1024);
auto filename1 = basename + ".1.txt";
auto filename1 = basename + ".1";
REQUIRE(get_filesize(filename1) <= 1024);
}
@@ -83,7 +83,7 @@ TEST_CASE("daily_logger", "[daily_logger]]")
std::string basename = "logs/daily_log";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0);
logger->flush_on(spdlog::level::info);
@@ -106,9 +106,9 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
w.write("{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
@@ -118,11 +118,11 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
struct custom_daily_file_name_calculator
{
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename, const spdlog::filename_t& extension)
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
return w.str();
}
};
@@ -138,9 +138,9 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);

View File

@@ -1,14 +1,17 @@
#include "includes.h"
void prepare_logdir()
{
spdlog::drop_all();
#ifdef _WIN32
auto rv = system("del /F /Q logs\\*");
system("if not exist logs mkdir logs");
system("del /F /Q logs\\*");
#else
auto rv = system("rm -f logs/*");
#endif
auto rv = system("mkdir -p logs");
rv = system("rm -f logs/*");
(void)rv;
#endif
}