diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..3557a2f9
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,108 @@
+# Adapted from various sources, including:
+# - Louis Dionne's Hana: https://github.com/ldionne/hana
+# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
+# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
+language: cpp
+
+# Test matrix:
+# - Build matrix per compiler: C++11/C++14 + Debug/Release
+# - Optionally: AddressSanitizer (ASAN)
+# - Valgrind: all release builds are also tested with valgrind
+# - clang 3.4, 3.5, 3.6, trunk
+# - Note: 3.4 and trunk are tested with/without ASAN,
+# the rest is only tested with ASAN=On.
+# - gcc 4.9, 5.0
+#
+matrix:
+ include:
+ # Test clang-3.5: C++11/C++14, Buidd=Debug/Release, ASAN=On/Off
+ - env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11 ASAN=On LIBCXX=On
+ os: linux
+ addons: &clang35
+ apt:
+ packages:
+ - clang-3.5
+ - valgrind
+ sources:
+ - ubuntu-toolchain-r-test
+ - llvm-toolchain-precise-3.5
+
+
+ - env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=On
+ os: linux
+ addons: *clang35
+
+
+
+# Test gcc-4.8: C++11, Build=Debug/Release, ASAN=Off
+ - env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
+ os: linux
+ addons: &gcc48
+ apt:
+ packages:
+ - g++-4.8
+ - valgrind
+ sources:
+ - ubuntu-toolchain-r-test
+
+ - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
+ os: linux
+ addons: *gcc48
+
+ # Test gcc-4.9: C++11, Build=Debug/Release, ASAN=Off
+ - env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
+ os: linux
+ addons: &gcc49
+ apt:
+ packages:
+ - g++-4.9
+ - valgrind
+ sources:
+ - ubuntu-toolchain-r-test
+
+ - env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
+ os: linux
+ addons: *gcc49
+
+# Install dependencies
+before_install:
+ - export CHECKOUT_PATH=`pwd`;
+ - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
+ - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
+ - if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi
+ - which $CXX
+ - which $CC
+ - which valgrind
+ - if [ -n "$CLANG_VERSION" ]; then sudo CXX=$CXX CC=$CC ./tests/install_libcxx.sh; fi
+
+install:
+ - cd $CHECKOUT_PATH
+
+ # Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469.
+ # It is fixed in valgrind 3.10 so this won't be necessary if someone
+ # replaces the current valgrind (3.7) with valgrind-3.10
+ - sed -i 's/march=native/msse4.2/' example/Makefile
+
+ - if [ ! -d build ]; then mkdir build; fi
+ - export CXX_FLAGS=""
+ - export CXX_LINKER_FLAGS=""
+ - if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi
+ - if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi
+ - if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi
+ - if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi
+ - if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi
+ - CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}"
+
+ # Build examples
+ - cd example
+ - if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi
+ - if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi
+
+
+script:
+ - ./"${BIN}"
+ - valgrind --trace-children=yes --leak-check=full ./"${BIN}"
+ - cd $CHECKOUT_PATH/tests; make rebuild; ./tests
+
+notifications:
+ email: false
diff --git a/README.md b/README.md
index 7db15533..3d161cf6 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# spdlog
-Very fast, header only, C++ logging library.
+Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)
## Install
Just copy the files to your build tree and use a C++11 compiler
@@ -9,7 +9,7 @@ Just copy the files to your build tree and use a C++11 compiler
* Linux (gcc 4.8.1+, clang 3.5+)
* Windows (visual studio 2013+, mingw with g++ 4.9.1+)
* Mac OSX (clang 3.5+)
-
+
##Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only.
@@ -17,7 +17,7 @@ Just copy the files to your build tree and use a C++11 compiler
* Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library.
* ostream call style is supported too.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
-* [Custom](https://github.com/gabime/spdlog/wiki/Custom-formatting) formatting.
+* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Multi/Single threaded loggers.
* Various log targets:
* Rotating log files.
@@ -31,7 +31,7 @@ Just copy the files to your build tree and use a C++11 compiler
## Benchmarks
-Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
+Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
#### Synchronous mode
Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of 3 runs):
@@ -43,7 +43,7 @@ Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of
|100| 15.008 |1.139s |4.512s |0.497s|
-#### Asynchronous mode
+#### Asynchronous mode
Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs):
|threads|g2log async logger |spdlog async mode|
@@ -70,7 +70,7 @@ int main(int, char* [])
console->info("Welcome to spdlog!") ;
console->info("An info message example {}..", 1);
console->info() << "Streams are supported too " << 1;
-
+
//Formatting examples
console->info("Easy padding in numbers like {:08d}", 12);
console->info("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
@@ -80,7 +80,7 @@ int main(int, char* [])
console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
-
+
//
// Runtime log levels
//
@@ -88,7 +88,7 @@ int main(int, char* [])
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("Now it should..");
-
+
//
// Create a file rotating logger with 5mb size max and 3 rotated files
//
@@ -100,8 +100,8 @@ int main(int, char* [])
// 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);
-
- //
+
+ //
// Customize msg format for all messages
//
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
@@ -115,7 +115,7 @@ int main(int, char* [])
//
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
-
+
//
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
@@ -124,14 +124,14 @@ int main(int, char* [])
spdlog::set_async_mode(q_size);
auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
async_file->info() << "This is async log.." << "Should be very fast!";
-
- //
+
+ //
// syslog example. linux only..
//
#ifdef __linux__
std::string ident = "spdlog-example";
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
}
catch (const spd::spdlog_ex& ex)
@@ -155,4 +155,3 @@ void custom_class_example()
## Documentation
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
-
diff --git a/example/Makefile b/example/Makefile
index 80e6bd3d..8b17c7f2 100644
--- a/example/Makefile
+++ b/example/Makefile
@@ -1,32 +1,29 @@
CXX ?= g++
-CXXFLAGS = -march=native -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
-CXX_RELEASE_FLAGS = -O3 -flto
-CXX_DEBUG_FLAGS= -g
+CXXFLAGS =
+CXX_FLAGS = -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -I../include
+CXX_RELEASE_FLAGS = -O3 -march=native
+CXX_DEBUG_FLAGS= -g
all: example bench
debug: example-debug bench-debug
example: example.cpp
- $(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
+ $(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
bench: bench.cpp
- $(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
-
+ $(CXX) bench.cpp -o bench $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
+
example-debug: example.cpp
- $(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
-
+ $(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
+
bench-debug: bench.cpp
- $(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
-
-
+ $(CXX) bench.cpp -o bench-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
clean:
- rm -f *.o logs/*.txt example example-debug bench bench-debug
+ rm -f *.o logs/*.txt example example-debug bench bench-debug
rebuild: clean all
rebuild-debug: clean debug
-
-
diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h
index 7bcde900..517ce92f 100644
--- a/include/spdlog/async_logger.h
+++ b/include/spdlog/async_logger.h
@@ -58,19 +58,22 @@ public:
const It& end,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
- const std::function& worker_warmup_cb = nullptr);
+ const std::function& worker_warmup_cb = nullptr,
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
async_logger(const std::string& logger_name,
sinks_init_list sinks,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
- const std::function& worker_warmup_cb = nullptr);
+ const std::function& worker_warmup_cb = nullptr,
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
- const std::function& worker_warmup_cb = nullptr);
+ const std::function& worker_warmup_cb = nullptr,
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
protected:
diff --git a/include/spdlog/common.h b/include/spdlog/common.h
index cde5a9eb..01b22121 100644
--- a/include/spdlog/common.h
+++ b/include/spdlog/common.h
@@ -49,8 +49,8 @@ class sink;
// Common types across the lib
using log_clock = std::chrono::system_clock;
-using sink_ptr = std::shared_ptr < sinks::sink > ;
-using sinks_init_list = std::initializer_list < sink_ptr > ;
+using sink_ptr = std::shared_ptr < sinks::sink >;
+using sinks_init_list = std::initializer_list < sink_ptr >;
using formatter_ptr = std::shared_ptr;
@@ -59,16 +59,16 @@ namespace level
{
typedef enum
{
- trace = 0,
- debug = 1,
- info = 2,
- notice = 3,
- warn = 4,
- err = 5,
+ trace = 0,
+ debug = 1,
+ info = 2,
+ notice = 3,
+ warn = 4,
+ err = 5,
critical = 6,
- alert = 7,
- emerg = 8,
- off = 9
+ alert = 7,
+ emerg = 8,
+ off = 9
} level_enum;
static const char* level_names[] { "trace", "debug", "info", "notice", "warning", "error", "critical", "alert", "emerg", "off"};
@@ -113,4 +113,4 @@ private:
};
-} //spdlog
+} //spdlog
\ No newline at end of file
diff --git a/include/spdlog/details/async_log_helper.h b/include/spdlog/details/async_log_helper.h
index 657c70bb..59c1b2dc 100644
--- a/include/spdlog/details/async_log_helper.h
+++ b/include/spdlog/details/async_log_helper.h
@@ -43,6 +43,7 @@
#include "./mpmc_bounded_q.h"
#include "./log_msg.h"
#include "./format.h"
+#include "os.h"
namespace spdlog
@@ -119,7 +120,8 @@ public:
const std::vector& sinks,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
- const std::function& worker_warmup_cb = nullptr);
+ const std::function& worker_warmup_cb = nullptr,
+ const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero());
void log(const details::log_msg& msg);
@@ -145,6 +147,9 @@ private:
// worker thread warmup callback - one can set thread priority, affinity, etc
const std::function _worker_warmup_cb;
+ // auto periodic sink flush parameter
+ const std::chrono::milliseconds _flush_interval_ms;
+
// worker thread
std::thread _worker_thread;
@@ -156,10 +161,14 @@ private:
// pop next message from the queue and process it
// return true if a message was available (queue was not empty), will set the last_pop to the pop time
- bool process_next_msg(clock::time_point& last_pop);
+ bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush);
+
+ void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
// sleep,yield or return immediatly using the time passed since last message as a hint
- static void sleep_or_yield(const clock::time_point& last_op_time);
+ static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
+
+
};
}
@@ -168,12 +177,13 @@ private:
///////////////////////////////////////////////////////////////////////////////
// async_sink class implementation
///////////////////////////////////////////////////////////////////////////////
-inline spdlog::details::async_log_helper::async_log_helper(formatter_ptr formatter, const std::vector& sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb):
+inline spdlog::details::async_log_helper::async_log_helper(formatter_ptr formatter, const std::vector& sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms):
_formatter(formatter),
_sinks(sinks),
_q(queue_size),
_overflow_policy(overflow_policy),
_worker_warmup_cb(worker_warmup_cb),
+ _flush_interval_ms(flush_interval_ms),
_worker_thread(&async_log_helper::worker_loop, this)
{}
@@ -199,10 +209,12 @@ inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
async_msg new_msg(msg);
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
{
- auto last_op_time = clock::now();
+ auto last_op_time = details::os::now();
+ auto now = last_op_time;
do
{
- sleep_or_yield(last_op_time);
+ now = details::os::now();
+ sleep_or_yield(now, last_op_time);
}
while (!_q.enqueue(std::move(new_msg)));
}
@@ -214,8 +226,9 @@ inline void spdlog::details::async_log_helper::worker_loop()
try
{
if (_worker_warmup_cb) _worker_warmup_cb();
- clock::time_point last_pop = clock::now();
- while(process_next_msg(last_pop));
+ auto last_pop = details::os::now();
+ auto last_flush = last_pop;
+ while(process_next_msg(last_pop, last_flush));
}
catch (const std::exception& ex)
{
@@ -229,7 +242,7 @@ inline void spdlog::details::async_log_helper::worker_loop()
// process next message in the queue
// return true if this thread should still be active (no msg with level::off was received)
-inline bool spdlog::details::async_log_helper::process_next_msg(clock::time_point& last_pop)
+inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
{
async_msg incoming_async_msg;
@@ -237,7 +250,7 @@ inline bool spdlog::details::async_log_helper::process_next_msg(clock::time_poin
if (_q.dequeue(incoming_async_msg))
{
- last_pop = clock::now();
+ last_pop = details::os::now();
if(incoming_async_msg.level == level::off)
return false;
@@ -249,11 +262,22 @@ inline bool spdlog::details::async_log_helper::process_next_msg(clock::time_poin
}
else //empty queue
{
- sleep_or_yield(last_pop);
+ auto now = details::os::now();
+ handle_flush_interval(now, last_flush);
+ sleep_or_yield(now, last_pop);
}
return true;
}
+inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
+{
+ if (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms)
+ {
+ for (auto &s : _sinks)
+ s->flush();
+ now = last_flush = details::os::now();
+ }
+}
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
@@ -261,12 +285,12 @@ inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_f
// sleep,yield or return immediatly using the time passed since last message as a hint
-inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_point& last_op_time)
+inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time)
{
using std::chrono::milliseconds;
using namespace std::this_thread;
- auto time_since_op = clock::now() - last_op_time;
+ auto time_since_op = now - last_op_time;
// spin upto 1 ms
if (time_since_op <= milliseconds(1))
diff --git a/include/spdlog/details/async_logger_impl.h b/include/spdlog/details/async_logger_impl.h
index e113e1d2..f60407e3 100644
--- a/include/spdlog/details/async_logger_impl.h
+++ b/include/spdlog/details/async_logger_impl.h
@@ -39,9 +39,10 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
const It& end,
size_t queue_size,
const async_overflow_policy overflow_policy,
- const std::function& worker_warmup_cb) :
+ const std::function& worker_warmup_cb,
+ const std::chrono::milliseconds& flush_interval_ms) :
logger(logger_name, begin, end),
- _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb))
+ _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms))
{
}
@@ -49,15 +50,17 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
sinks_init_list sinks,
size_t queue_size,
const async_overflow_policy overflow_policy,
- const std::function& worker_warmup_cb) :
- async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb) {}
+ const std::function& worker_warmup_cb,
+ const std::chrono::milliseconds& flush_interval_ms) :
+ async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
const async_overflow_policy overflow_policy,
- const std::function& worker_warmup_cb) :
- async_logger(logger_name, { single_sink }, queue_size, overflow_policy, worker_warmup_cb) {}
+ const std::function& worker_warmup_cb,
+ const std::chrono::milliseconds& flush_interval_ms) :
+ async_logger(logger_name, { single_sink }, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {}
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h
index 8ae67bf0..4ba556ac 100644
--- a/include/spdlog/details/file_helper.h
+++ b/include/spdlog/details/file_helper.h
@@ -48,7 +48,7 @@ public:
const int open_tries = 5;
const int open_interval = 10;
- explicit file_helper(bool force_flush):
+ explicit file_helper(bool force_flush) :
_fd(nullptr),
_force_flush(force_flush)
{}
@@ -62,7 +62,7 @@ public:
}
- void open(const std::string& fname, bool truncate=false)
+ void open(const std::string& fname, bool truncate = false)
{
close();
@@ -70,7 +70,7 @@ public:
_filename = fname;
for (int tries = 0; tries < open_tries; ++tries)
{
- if(!os::fopen_s(&_fd, fname, mode))
+ if (!os::fopen_s(&_fd, fname, mode))
return;
std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
@@ -81,12 +81,16 @@ public:
void reopen(bool truncate)
{
- if(_filename.empty())
+ if (_filename.empty())
throw spdlog_ex("Failed re opening file - was not opened before");
open(_filename, truncate);
}
+ void flush() {
+ std::fflush(_fd);
+ }
+
void close()
{
if (_fd)
@@ -101,14 +105,37 @@ public:
size_t size = msg.formatted.size();
auto data = msg.formatted.data();
- if(std::fwrite(data, 1, size, _fd) != size)
+ if (std::fwrite(data, 1, size, _fd) != size)
throw spdlog_ex("Failed writing to file " + _filename);
- if(_force_flush)
+ if (_force_flush)
std::fflush(_fd);
}
+ long size()
+ {
+ if (!_fd)
+ throw spdlog_ex("Cannot use size() on closed file " + _filename);
+
+ auto pos = ftell(_fd);
+ if (fseek(_fd, 0, SEEK_END) != 0)
+ throw spdlog_ex("fseek failed on file " + _filename);
+
+ auto size = ftell(_fd);
+
+ if(fseek(_fd, pos, SEEK_SET) !=0)
+ throw spdlog_ex("fseek failed on file " + _filename);
+
+ if (size == -1)
+ throw spdlog_ex("ftell failed on file " + _filename);
+
+
+ return size;
+
+
+ }
+
const std::string& filename() const
{
return _filename;
@@ -128,6 +155,8 @@ public:
}
}
+
+
private:
FILE* _fd;
std::string _filename;
@@ -137,4 +166,3 @@ private:
};
}
}
-
diff --git a/include/spdlog/details/format.cc b/include/spdlog/details/format.cc
index 4de135cf..873d8274 100644
--- a/include/spdlog/details/format.cc
+++ b/include/spdlog/details/format.cc
@@ -35,11 +35,18 @@
#include
#include
-#ifdef _WIN32
-# ifdef __MINGW32__
-# include
+#if defined(_WIN32) && defined(__MINGW32__)
+# include
+#endif
+
+#if FMT_USE_WINDOWS_H
+# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
+# include
+# else
+# define NOMINMAX
+# include
+# undef NOMINMAX
# endif
-# include
#endif
using fmt::internal::Arg;
@@ -88,13 +95,14 @@ using fmt::internal::Arg;
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
-static inline fmt::internal::None<> strerror_r(int, char *, ...) {
- return fmt::internal::None<>();
+static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
+ return fmt::internal::Null<>();
}
-static inline fmt::internal::None<> strerror_s(char *, std::size_t, ...) {
- return fmt::internal::None<>();
+static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
+ return fmt::internal::Null<>();
}
+namespace fmt {
namespace {
#ifndef _MSC_VER
@@ -125,6 +133,7 @@ struct IntChecker {
unsigned max = INT_MAX;
return value <= max;
}
+ static bool fits_in_int(bool) { return true; }
};
template <>
@@ -150,7 +159,7 @@ typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef);
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
- assert(buffer != 0 && buffer_size != 0);
+ FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError {
private:
@@ -177,7 +186,7 @@ int safe_strerror(
}
// Handle the case when strerror_r is not available.
- int handle(fmt::internal::None<>) {
+ int handle(fmt::internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
@@ -189,17 +198,20 @@ int safe_strerror(
}
// Fallback to strerror if strerror_r and strerror_s are not available.
- int fallback(fmt::internal::None<>) {
+ int fallback(fmt::internal::Null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
- StrError(int error_code, char *&buffer, std::size_t buffer_size)
- : error_code_(error_code), buffer_(buffer), buffer_size_(buffer_size) {}
+ StrError(int err_code, char *&buf, std::size_t buf_size)
+ : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
- int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
+ int run() {
+ strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r.
+ return handle(strerror_r(error_code_, buffer_, buffer_size_));
+ }
};
return StrError(error_code, buffer, buffer_size).run();
}
@@ -259,6 +271,11 @@ int parse_nonnegative_int(const Char *&s) {
return value;
}
+template
+inline bool is_name_start(Char c) {
+ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
+}
+
inline void require_numeric_argument(const Arg &arg, char spec) {
if (arg.type > Arg::LAST_NUMERIC_TYPE) {
std::string message =
@@ -379,24 +396,139 @@ class CharConverter : public fmt::internal::ArgVisitor {
arg_.int_value = static_cast(value);
}
};
-
-// This function template is used to prevent compile errors when handling
-// incompatible string arguments, e.g. handling a wide string in a narrow
-// string formatter.
-template
-Arg::StringValue ignore_incompatible_str(Arg::StringValue);
-
-template <>
-inline Arg::StringValue ignore_incompatible_str(
- Arg::StringValue) { return Arg::StringValue(); }
-
-template <>
-inline Arg::StringValue ignore_incompatible_str(
- Arg::StringValue s) { return s; }
} // namespace
+namespace internal {
+
+template
+class BasicArgFormatter : public ArgVisitor {
+ private:
+ BasicWriter &writer_;
+ FormatSpec &spec_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(BasicArgFormatter);
+
+ protected:
+ BasicWriter &writer() { return writer_; }
+ const FormatSpec &spec() const { return spec_; }
+
+ public:
+ BasicArgFormatter(BasicWriter &w, FormatSpec &s)
+ : writer_(w), spec_(s) {}
+
+ template
+ void visit_any_int(T value) { writer_.write_int(value, spec_); }
+
+ template
+ void visit_any_double(T value) { writer_.write_double(value, spec_); }
+
+ void visit_bool(bool value) {
+ if (spec_.type_) {
+ writer_.write_int(value, spec_);
+ return;
+ }
+ const char *str_value = value ? "true" : "false";
+ Arg::StringValue str = { str_value, strlen(str_value) };
+ writer_.write_str(str, spec_);
+ }
+
+ void visit_char(int value) {
+ if (spec_.type_ && spec_.type_ != 'c') {
+ spec_.flags_ |= CHAR_FLAG;
+ writer_.write_int(value, spec_);
+ return;
+ }
+ if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
+ FMT_THROW(FormatError("invalid format specifier for char"));
+ typedef typename BasicWriter::CharPtr CharPtr;
+ Char fill = internal::CharTraits::cast(spec_.fill());
+ CharPtr out = CharPtr();
+ if (spec_.width_ > 1) {
+ out = writer_.grow_buffer(spec_.width_);
+ if (spec_.align_ == ALIGN_RIGHT) {
+ std::fill_n(out, spec_.width_ - 1, fill);
+ out += spec_.width_ - 1;
+ } else if (spec_.align_ == ALIGN_CENTER) {
+ out = writer_.fill_padding(out, spec_.width_, 1, fill);
+ } else {
+ std::fill_n(out + 1, spec_.width_ - 1, fill);
+ }
+ } else {
+ out = writer_.grow_buffer(1);
+ }
+ *out = internal::CharTraits::cast(value);
+ }
+
+ void visit_string(Arg::StringValue value) {
+ writer_.write_str(value, spec_);
+ }
+
+ using ArgVisitor::visit_wstring;
+
+ void visit_wstring(Arg::StringValue value) {
+ writer_.write_str(value, spec_);
+ }
+
+ void visit_pointer(const void *value) {
+ if (spec_.type_ && spec_.type_ != 'p')
+ report_unknown_type(spec_.type_, "pointer");
+ spec_.flags_ = HASH_FLAG;
+ spec_.type_ = 'x';
+ writer_.write_int(reinterpret_cast(value), spec_);
+ }
+};
+
+// An argument formatter.
+template
+class ArgFormatter : public BasicArgFormatter, Char> {
+ private:
+ BasicFormatter &formatter_;
+ const Char *format_;
+
+ public:
+ ArgFormatter(BasicFormatter &f, FormatSpec &s, const Char *fmt)
+ : BasicArgFormatter, Char>(f.writer(), s),
+ formatter_(f), format_(fmt) {}
+
+ void visit_custom(Arg::CustomValue c) {
+ c.format(&formatter_, c.value, &format_);
+ }
+};
+
+template
+class PrintfArgFormatter :
+ public BasicArgFormatter, Char> {
+ public:
+ PrintfArgFormatter(BasicWriter &w, FormatSpec &s)
+ : BasicArgFormatter, Char>(w, s) {}
+
+ void visit_char(int value) {
+ const FormatSpec &fmt_spec = this->spec();
+ BasicWriter &w = this->writer();
+ if (fmt_spec.type_ && fmt_spec.type_ != 'c')
+ w.write_int(value, fmt_spec);
+ typedef typename BasicWriter::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(value);
+ }
+};
+} // namespace internal
+} // namespace fmt
+
FMT_FUNC void fmt::SystemError::init(
- int err_code, StringRef format_str, ArgList args) {
+ int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_system_error(w, err_code, format(format_str, args));
@@ -477,19 +609,23 @@ FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) {
static_cast(code), type)));
}
-#ifdef _WIN32
+#if FMT_USE_WINDOWS_H
FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) {
- int length = MultiByteToWideChar(
- CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0);
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
+ if (s.size() > INT_MAX)
+ FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
+ int s_size = static_cast(s.size());
+ int length = MultiByteToWideChar(
+ CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
- buffer_.resize(length);
+ buffer_.resize(length + 1);
length = MultiByteToWideChar(
- CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length);
+ CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
+ buffer_[length] = 0;
}
FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
@@ -500,19 +636,23 @@ FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
}
FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) {
- int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0);
+ if (s.size() > INT_MAX)
+ return ERROR_INVALID_PARAMETER;
+ int s_size = static_cast(s.size());
+ int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0);
if (length == 0)
return GetLastError();
- buffer_.resize(length);
+ buffer_.resize(length + 1);
length = WideCharToMultiByte(
- CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0);
+ CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0);
if (length == 0)
return GetLastError();
+ buffer_[length] = 0;
return 0;
}
FMT_FUNC void fmt::WindowsError::init(
- int err_code, StringRef format_str, ArgList args) {
+ int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
@@ -520,30 +660,6 @@ FMT_FUNC void fmt::WindowsError::init(
base = std::runtime_error(w.str());
}
-#endif
-
-FMT_FUNC void fmt::internal::format_system_error(
- fmt::Writer &out, int error_code,
- fmt::StringRef message) FMT_NOEXCEPT {
- FMT_TRY {
- MemoryBuffer buffer;
- buffer.resize(INLINE_BUFFER_SIZE);
- for (;;) {
- char *system_message = &buffer[0];
- int result = safe_strerror(error_code, system_message, buffer.size());
- if (result == 0) {
- out << message << ": " << system_message;
- return;
- }
- if (result != ERANGE)
- break; // Can't get error message, report error code instead.
- buffer.resize(buffer.size() * 2);
- }
- } FMT_CATCH(...) {}
- format_error_code(out, error_code, message);
-}
-
-#ifdef _WIN32
FMT_FUNC void fmt::internal::format_windows_error(
fmt::Writer &out, int error_code,
fmt::StringRef message) FMT_NOEXCEPT {
@@ -572,81 +688,74 @@ FMT_FUNC void fmt::internal::format_windows_error(
} FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
-#endif
-// An argument formatter.
-template
-class fmt::internal::ArgFormatter :
- public fmt::internal::ArgVisitor, void> {
- private:
- fmt::BasicFormatter &formatter_;
- fmt::BasicWriter &writer_;
- fmt::FormatSpec &spec_;
- const Char *format_;
+#endif // FMT_USE_WINDOWS_H
- FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatter);
-
- public:
- ArgFormatter(
- fmt::BasicFormatter &f,fmt::FormatSpec &s, const Char *fmt)
- : formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {}
-
- template
- void visit_any_int(T value) { writer_.write_int(value, spec_); }
-
- template
- void visit_any_double(T value) { writer_.write_double(value, spec_); }
-
- void visit_char(int value) {
- if (spec_.type_ && spec_.type_ != 'c') {
- spec_.flags_ |= CHAR_FLAG;
- writer_.write_int(value, spec_);
- return;
- }
- if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
- FMT_THROW(FormatError("invalid format specifier for char"));
- typedef typename fmt::BasicWriter::CharPtr CharPtr;
- Char fill = static_cast(spec_.fill());
- if (spec_.precision_ == 0) {
- std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill);
- return;
- }
- CharPtr out = CharPtr();
- if (spec_.width_ > 1) {
- out = writer_.grow_buffer(spec_.width_);
- if (spec_.align_ == fmt::ALIGN_RIGHT) {
- std::fill_n(out, spec_.width_ - 1, fill);
- out += spec_.width_ - 1;
- } else if (spec_.align_ == fmt::ALIGN_CENTER) {
- out = writer_.fill_padding(out, spec_.width_, 1, fill);
- } else {
- std::fill_n(out + 1, spec_.width_ - 1, fill);
+FMT_FUNC void fmt::internal::format_system_error(
+ fmt::Writer &out, int error_code,
+ fmt::StringRef message) FMT_NOEXCEPT {
+ FMT_TRY {
+ MemoryBuffer buffer;
+ buffer.resize(INLINE_BUFFER_SIZE);
+ for (;;) {
+ char *system_message = &buffer[0];
+ int result = safe_strerror(error_code, system_message, buffer.size());
+ if (result == 0) {
+ out << message << ": " << system_message;
+ return;
}
- } else {
- out = writer_.grow_buffer(1);
+ if (result != ERANGE)
+ break; // Can't get error message, report error code instead.
+ buffer.resize(buffer.size() * 2);
}
- *out = static_cast(value);
- }
+ } FMT_CATCH(...) {}
+ format_error_code(out, error_code, message);
+}
- void visit_string(Arg::StringValue value) {
- writer_.write_str(value, spec_);
+template
+void fmt::internal::ArgMap::init(const ArgList &args) {
+ if (!map_.empty())
+ return;
+ typedef internal::NamedArg NamedArg;
+ const NamedArg *named_arg = 0;
+ bool use_values =
+ args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
+ if (use_values) {
+ for (unsigned i = 0;/*nothing*/; ++i) {
+ internal::Arg::Type arg_type = args.type(i);
+ switch (arg_type) {
+ case internal::Arg::NONE:
+ return;
+ case internal::Arg::NAMED_ARG:
+ named_arg = static_cast(args.values_[i].pointer);
+ map_.insert(Pair(named_arg->name, *named_arg));
+ break;
+ default:
+ /*nothing*/;
+ }
+ }
+ return;
}
- void visit_wstring(Arg::StringValue value) {
- writer_.write_str(ignore_incompatible_str(value), spec_);
+ for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
+ internal::Arg::Type arg_type = args.type(i);
+ if (arg_type == internal::Arg::NAMED_ARG) {
+ named_arg = static_cast(args.args_[i].pointer);
+ map_.insert(Pair(named_arg->name, *named_arg));
+ }
}
-
- void visit_pointer(const void *value) {
- if (spec_.type_ && spec_.type_ != 'p')
- fmt::internal::report_unknown_type(spec_.type_, "pointer");
- spec_.flags_ = fmt::HASH_FLAG;
- spec_.type_ = 'x';
- writer_.write_int(reinterpret_cast(value), spec_);
+ for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
+ switch (args.args_[i].type) {
+ case internal::Arg::NONE:
+ return;
+ case internal::Arg::NAMED_ARG:
+ named_arg = static_cast(args.args_[i].pointer);
+ map_.insert(Pair(named_arg->name, *named_arg));
+ break;
+ default:
+ /*nothing*/;
+ }
}
-
- void visit_custom(Arg::CustomValue c) {
- c.format(&formatter_, c.value, &format_);
- }
-};
+}
template
void fmt::internal::FixedBuffer::grow(std::size_t) {
@@ -675,6 +784,19 @@ void fmt::BasicWriter::write_str(
write_str(str_value, str_size, spec);
}
+template
+inline Arg fmt::BasicFormatter::get_arg(
+ BasicStringRef arg_name, const char *&error) {
+ if (check_no_auto_index(error)) {
+ map_.init(args());
+ const Arg *arg = map_.find(arg_name);
+ if (arg)
+ return *arg;
+ error = "argument not found";
+ }
+ return Arg();
+}
+
template
inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) {
const char *error = 0;
@@ -687,11 +809,33 @@ inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) {
return arg;
}
+template
+inline Arg fmt::BasicFormatter::parse_arg_name(const Char *&s) {
+ assert(is_name_start(*s));
+ const Char *start = s;
+ Char c;
+ do {
+ c = *++s;
+ } while (is_name_start(c) || ('0' <= c && c <= '9'));
+ const char *error = 0;
+ Arg arg = get_arg(fmt::BasicStringRef(start, s - start), error);
+ if (error)
+ FMT_THROW(fmt::FormatError(error));
+ return arg;
+}
+
FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index];
- if (arg.type == Arg::NONE)
+ switch (arg.type) {
+ case Arg::NONE:
error = "argument index out of range";
+ break;
+ case Arg::NAMED_ARG:
+ arg = *static_cast(arg.pointer);
+ default:
+ /*nothing*/;
+ }
return arg;
}
@@ -702,14 +846,19 @@ inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) {
return Arg();
}
+inline bool fmt::internal::FormatterBase::check_no_auto_index(
+ const char *&error) {
+ if (next_arg_index_ > 0) {
+ error = "cannot switch from automatic to manual argument indexing";
+ return false;
+ }
+ next_arg_index_ = -1;
+ return true;
+}
+
inline Arg fmt::internal::FormatterBase::get_arg(
unsigned arg_index, const char *&error) {
- if (next_arg_index_ <= 0) {
- next_arg_index_ = -1;
- return do_get_arg(arg_index, error);
- }
- error = "cannot switch from automatic to manual argument indexing";
- return Arg();
+ return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg();
}
template
@@ -787,10 +936,8 @@ unsigned fmt::internal::PrintfFormatter::parse_header(
template
void fmt::internal::PrintfFormatter::format(
- BasicWriter &writer, BasicStringRef format_str,
- const ArgList &args) {
+ BasicWriter &writer, BasicCStringRef format_str) {
const Char *start = format_str.c_str();
- set_args(args);
const Char *s = start;
while (*s) {
Char c = *s++;
@@ -881,73 +1028,7 @@ void fmt::internal::PrintfFormatter::format(
start = s;
// Format argument.
- switch (arg.type) {
- case Arg::INT:
- writer.write_int(arg.int_value, spec);
- break;
- case Arg::UINT:
- writer.write_int(arg.uint_value, spec);
- break;
- case Arg::LONG_LONG:
- writer.write_int(arg.long_long_value, spec);
- break;
- case Arg::ULONG_LONG:
- writer.write_int(arg.ulong_long_value, spec);
- break;
- case Arg::CHAR: {
- if (spec.type_ && spec.type_ != 'c')
- writer.write_int(arg.int_value, spec);
- typedef typename BasicWriter::CharPtr CharPtr;
- CharPtr out = CharPtr();
- if (spec.width_ > 1) {
- Char fill = ' ';
- out = writer.grow_buffer(spec.width_);
- if (spec.align_ != ALIGN_LEFT) {
- std::fill_n(out, spec.width_ - 1, fill);
- out += spec.width_ - 1;
- } else {
- std::fill_n(out + 1, spec.width_ - 1, fill);
- }
- } else {
- out = writer.grow_buffer(1);
- }
- *out = static_cast(arg.int_value);
- break;
- }
- case Arg::DOUBLE:
- writer.write_double(arg.double_value, spec);
- break;
- case Arg::LONG_DOUBLE:
- writer.write_double(arg.long_double_value, spec);
- break;
- case Arg::CSTRING:
- arg.string.size = 0;
- writer.write_str(arg.string, spec);
- break;
- case Arg::STRING:
- writer.write_str(arg.string, spec);
- break;
- case Arg::WSTRING:
- writer.write_str(ignore_incompatible_str(arg.wstring), spec);
- break;
- case Arg::POINTER:
- if (spec.type_ && spec.type_ != 'p')
- internal::report_unknown_type(spec.type_, "pointer");
- spec.flags_= HASH_FLAG;
- spec.type_ = 'x';
- writer.write_int(reinterpret_cast(arg.pointer), spec);
- break;
- case Arg::CUSTOM: {
- if (spec.type_)
- internal::report_unknown_type(spec.type_, "object");
- const void *str_format = "s";
- arg.custom.format(&writer, arg.custom.value, &str_format);
- break;
- }
- default:
- assert(false);
- break;
- }
+ internal::PrintfArgFormatter(writer, spec).visit(arg);
}
write(writer, start, s);
}
@@ -1019,16 +1100,47 @@ const Char *fmt::BasicFormatter::format(
++s;
}
- // Parse width and zero flag.
+ // Parse zero flag.
+ if (*s == '0') {
+ require_numeric_argument(arg, '0');
+ spec.align_ = ALIGN_NUMERIC;
+ spec.fill_ = '0';
+ ++s;
+ }
+
+ // Parse width.
if ('0' <= *s && *s <= '9') {
- if (*s == '0') {
- require_numeric_argument(arg, '0');
- spec.align_ = ALIGN_NUMERIC;
- spec.fill_ = '0';
- }
- // Zero may be parsed again as a part of the width, but it is simpler
- // and more efficient than checking if the next char is a digit.
spec.width_ = parse_nonnegative_int(s);
+ } else if (*s == '{') {
+ ++s;
+ Arg width_arg = is_name_start(*s) ?
+ parse_arg_name(s) : parse_arg_index(s);
+ if (*s++ != '}')
+ FMT_THROW(FormatError("invalid format string"));
+ ULongLong value = 0;
+ switch (width_arg.type) {
+ case Arg::INT:
+ if (width_arg.int_value < 0)
+ FMT_THROW(FormatError("negative width"));
+ value = width_arg.int_value;
+ break;
+ case Arg::UINT:
+ value = width_arg.uint_value;
+ break;
+ case Arg::LONG_LONG:
+ if (width_arg.long_long_value < 0)
+ FMT_THROW(FormatError("negative width"));
+ value = width_arg.long_long_value;
+ break;
+ case Arg::ULONG_LONG:
+ value = width_arg.ulong_long_value;
+ break;
+ default:
+ FMT_THROW(FormatError("width is not integer"));
+ }
+ if (value > INT_MAX)
+ FMT_THROW(FormatError("number is too big"));
+ spec.width_ = static_cast(value);
}
// Parse precision.
@@ -1039,7 +1151,8 @@ const Char *fmt::BasicFormatter::format(
spec.precision_ = parse_nonnegative_int(s);
} else if (*s == '{') {
++s;
- const Arg &precision_arg = parse_arg_index(s);
+ Arg precision_arg =
+ is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s);
if (*s++ != '}')
FMT_THROW(FormatError("invalid format string"));
ULongLong value = 0;
@@ -1069,7 +1182,7 @@ const Char *fmt::BasicFormatter::format(
} else {
FMT_THROW(FormatError("missing precision specifier"));
}
- if (arg.type < Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) {
+ if (arg.type <= Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) {
FMT_THROW(FormatError(
fmt::format("precision not allowed in {} format specifier",
arg.type == Arg::POINTER ? "pointer" : "integer")));
@@ -1083,7 +1196,6 @@ const Char *fmt::BasicFormatter::format(
if (*s++ != '}')
FMT_THROW(FormatError("missing '}' in format string"));
- start_ = s;
// Format argument.
internal::ArgFormatter(*this, spec, s - 1).visit(arg);
@@ -1091,25 +1203,24 @@ const Char *fmt::BasicFormatter::format(
}
template
-void fmt::BasicFormatter::format(
- BasicStringRef format_str, const ArgList &args) {
- const Char *s = start_ = format_str.c_str();
- set_args(args);
+void fmt::BasicFormatter::format(BasicCStringRef format_str) {
+ const Char *s = format_str.c_str();
+ const Char *start = s;
while (*s) {
Char c = *s++;
if (c != '{' && c != '}') continue;
if (*s == c) {
- write(writer_, start_, s);
- start_ = ++s;
+ write(writer_, start, s);
+ start = ++s;
continue;
}
if (c == '}')
FMT_THROW(FormatError("unmatched '}' in format string"));
- write(writer_, start_, s - 1);
- Arg arg = parse_arg_index(s);
- s = format(s, arg);
+ write(writer_, start, s - 1);
+ Arg arg = is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s);
+ start = s = format(s, arg);
}
- write(writer_, start_, s);
+ write(writer_, start, s);
}
FMT_FUNC void fmt::report_system_error(
@@ -1117,30 +1228,30 @@ FMT_FUNC void fmt::report_system_error(
report_error(internal::format_system_error, error_code, message);
}
-#ifdef _WIN32
+#if FMT_USE_WINDOWS_H
FMT_FUNC void fmt::report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message);
}
#endif
-FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) {
+FMT_FUNC void fmt::print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
-FMT_FUNC void fmt::print(StringRef format_str, ArgList args) {
+FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
-FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) {
+FMT_FUNC void fmt::print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
os.write(w.data(), w.size());
}
-FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) {
+FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = '0' + static_cast(c);
std::fputs(escape, stdout);
@@ -1148,7 +1259,7 @@ FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) {
std::fputs(RESET_COLOR, stdout);
}
-FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) {
+FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
@@ -1157,6 +1268,8 @@ FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) {
#ifndef FMT_HEADER_ONLY
+template struct fmt::internal::BasicData;
+
// Explicit instantiations for char.
template void fmt::internal::FixedBuffer::grow(std::size_t);
@@ -1164,11 +1277,10 @@ template void fmt::internal::FixedBuffer::grow(std::size_t);
template const char *fmt::BasicFormatter::format(
const char *&format_str, const fmt::internal::Arg &arg);
-template void fmt::BasicFormatter::format(
- BasicStringRef format, const ArgList &args);
+template void fmt::BasicFormatter::format(CStringRef format);
template void fmt::internal::PrintfFormatter::format(
- BasicWriter &writer, BasicStringRef format, const ArgList &args);
+ BasicWriter &writer, CStringRef format);
template int fmt::internal::CharTraits::format_float(
char *buffer, std::size_t size, const char *format,
@@ -1186,11 +1298,10 @@ template const wchar_t *fmt::BasicFormatter::format(
const wchar_t *&format_str, const fmt::internal::Arg &arg);
template void fmt::BasicFormatter::format(
- BasicStringRef format, const ArgList &args);
+ BasicCStringRef format);
template void fmt::internal::PrintfFormatter::format(
- BasicWriter &writer, BasicStringRef format,
- const ArgList &args);
+ BasicWriter &writer, WCStringRef format);
template int fmt::internal::CharTraits::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
diff --git a/include/spdlog/details/format.h b/include/spdlog/details/format.h
index 0e63d8e9..8e55b29f 100644
--- a/include/spdlog/details/format.h
+++ b/include/spdlog/details/format.h
@@ -27,7 +27,6 @@
#ifndef FMT_FORMAT_H_
#define FMT_FORMAT_H_
-
#define FMT_HEADER_ONLY
#include
@@ -41,6 +40,7 @@
#include
#include
#include
+#include