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 e72930b0..3d161cf6 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,23 @@
# spdlog
-Very fast, header only, C++ logging library.
+Very fast, header only, C++ logging library. [](https://travis-ci.org/gabime/spdlog)
## Install
Just copy the files to your build tree and use a C++11 compiler
-## Tested on:
-* gcc 4.8.1 and above
-* clang 3.5
-* visual studio 2013
-* mingw with g++ 4.9.x
+## Platforms
+ * 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.
* No dependencies - just copy and use.
-* Cross platform - Linux / Windows on 32/64 bits.
-* **new!** Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library.
+* 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.
@@ -33,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):
@@ -45,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|
@@ -72,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);
@@ -82,7 +80,7 @@ int main(int, char* [])
console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
-
+
//
// Runtime log levels
//
@@ -90,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
//
@@ -102,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 ***");
@@ -117,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..
@@ -126,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)
@@ -153,5 +151,7 @@ void custom_class_example()
spdlog::get("console")->info("custom class with operator<<: {}..", c);
spdlog::get("console")->info() << "custom class with operator<<: " << c << "..";
}
-
```
+
+## Documentation
+Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
diff --git a/bench/results.txt b/bench/results.txt
deleted file mode 100644
index fdd5718a..00000000
--- a/bench/results.txt
+++ /dev/null
@@ -1,235 +0,0 @@
-----------------------------------------------------------
-Single threaded benchmarks.. (1 thread, 1,000,000 lines)
-----------------------------------------------------------
-**************** boost-bench ****************
-
-real 0m4.382s
-user 0m4.213s
-sys 0m0.048s
-
-real 0m4.159s
-user 0m4.120s
-sys 0m0.040s
-
-real 0m4.169s
-user 0m4.117s
-sys 0m0.052s
-**************** glog-bench ****************
-
-real 0m1.082s
-user 0m0.944s
-sys 0m0.136s
-
-real 0m1.079s
-user 0m0.977s
-sys 0m0.101s
-
-real 0m1.066s
-user 0m0.951s
-sys 0m0.114s
-**************** easylogging-bench ****************
-
-real 0m0.975s
-user 0m0.963s
-sys 0m0.012s
-
-real 0m0.986s
-user 0m0.954s
-sys 0m0.033s
-
-real 0m0.963s
-user 0m0.919s
-sys 0m0.044s
-**************** spdlog-bench ****************
-
-real 0m0.302s
-user 0m0.285s
-sys 0m0.016s
-
-real 0m0.311s
-user 0m0.287s
-sys 0m0.025s
-
-real 0m0.308s
-user 0m0.276s
-sys 0m0.032s
-----------------------------------------------------------
-Multi threaded benchmarks.. (10 threads, 1,000,000 lines)
-----------------------------------------------------------
-**************** boost-bench-mt ****************
-
-real 0m16.293s
-user 0m38.723s
-sys 0m8.469s
-
-real 0m16.029s
-user 0m39.186s
-sys 0m8.413s
-
-real 0m16.257s
-user 0m38.322s
-sys 0m7.880s
-**************** glog-bench-mt ****************
-
-real 0m4.455s
-user 0m12.871s
-sys 0m13.508s
-
-real 0m5.039s
-user 0m14.239s
-sys 0m15.900s
-
-real 0m3.032s
-user 0m8.654s
-sys 0m9.473s
-**************** easylogging-bench-mt ****************
-
-real 0m4.076s
-user 0m4.350s
-sys 0m2.861s
-
-real 0m2.857s
-user 0m3.270s
-sys 0m1.744s
-
-real 0m4.588s
-user 0m5.085s
-sys 0m3.058s
-**************** spdlog-bench-mt ****************
-
-real 0m2.374s
-user 0m4.369s
-sys 0m10.426s
-
-real 0m0.968s
-user 0m1.804s
-sys 0m4.186s
-
-real 0m1.527s
-user 0m3.132s
-sys 0m6.427s
-----------------------------------------------------------
-Multi threaded benchmarks.. (100 threads, 1,000,000 lines)
-----------------------------------------------------------
-**************** boost-bench-mt ****************
-
-real 0m15.623s
-user 0m39.283s
-sys 0m8.428s
-
-real 0m15.008s
-user 0m36.851s
-sys 0m7.956s
-
-real 0m15.478s
-user 0m38.873s
-sys 0m8.368s
-**************** glog-bench-mt ****************
-
-real 0m1.139s
-user 0m3.003s
-sys 0m5.214s
-
-real 0m1.167s
-user 0m3.004s
-sys 0m5.431s
-
-real 0m1.159s
-user 0m2.909s
-sys 0m5.456s
-**************** easylogging-bench-mt ****************
-
-real 0m4.510s
-user 0m4.565s
-sys 0m3.510s
-
-real 0m8.841s
-user 0m8.363s
-sys 0m7.057s
-
-real 0m5.638s
-user 0m5.531s
-sys 0m4.168s
-**************** spdlog-bench-mt ****************
-
-real 0m0.497s
-user 0m0.951s
-sys 0m2.743s
-
-real 0m0.502s
-user 0m0.940s
-sys 0m2.816s
-
-real 0m0.504s
-user 0m0.911s
-sys 0m2.860s
----------------------------------------------------------------
-Async, single threaded benchmark.. (1 thread, 1,000,000 lines)
----------------------------------------------------------------
-**************** spdlog-async ****************
-Total: 1000000
-Threads: 1
-Delta = 0.216366 seconds
-Rate = 4.62179e+06/sec
-Total: 1000000
-Threads: 1
-Delta = 0.215076 seconds
-Rate = 4.64953e+06/sec
-Total: 1000000
-Threads: 1
-Delta = 0.210712 seconds
-Rate = 4.74581e+06/sec
-**************** g2log-async ****************
-Total: 1000000
-Threads: 1
-Delta = 1.85039 seconds
-Rate = 540428/sec
-
-Exiting, log location: logs/g2log-async.g2log.20141220-214929.log
-Total: 1000000
-Threads: 1
-Delta = 1.85434 seconds
-Rate = 539274/sec
-
-Exiting, log location: logs/g2log-async.g2log.20141220-214935.log
-Total: 1000000
-Threads: 1
-Delta = 1.86829 seconds
-Rate = 535249/sec
-
-Exiting, log location: logs/g2log-async.g2log.20141220-214941.log
----------------------------------------------------------------
-Async, multi threaded benchmark.. (10 threads, 1,000,000 lines)
----------------------------------------------------------------
-**************** spdlog-async ****************
-Total: 1000000
-Threads: 10
-Delta = 0.175684 seconds
-Rate = 5.69204e+06/sec
-Total: 1000000
-Threads: 10
-Delta = 0.173104 seconds
-Rate = 5.77688e+06/sec
-Total: 1000000
-Threads: 10
-Delta = 0.173881 seconds
-Rate = 5.75106e+06/sec
-**************** g2log-async ****************
-Total: 1000000
-Threads: 10
-Delta = 0.945252 seconds
-Rate = 1.05792e+06/sec
-
-Exiting, log location: logs/g2log-async.g2log.20141220-214958.log
-Total: 1000000
-Threads: 10
-Delta = 0.950362 seconds
-Rate = 1.05223e+06/sec
-
-Exiting, log location: logs/g2log-async.g2log.20141220-215004.log
-Total: 1000000
-Threads: 10
-Delta = 0.943302 seconds
-Rate = 1.06011e+06/sec
-
-Exiting, log location: logs/g2log-async.g2log.20141220-215011.log
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)
-
-bench-debug: bench.cpp
- $(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
+example-debug: example.cpp
+ $(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
+bench-debug: bench.cpp
+ $(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/example/bench.cpp b/example/bench.cpp
index b2f41b12..ba76e2a6 100644
--- a/example/bench.cpp
+++ b/example/bench.cpp
@@ -50,7 +50,8 @@ void bench_mt(int howmany, std::shared_ptr log, int thread_count
int main(int argc, char* argv[])
{
- int howmany = 1048576;
+ int queue_size = 1048576;
+ int howmany = 1000000;
int threads = 10;
bool auto_flush = false;
int file_size = 30 * 1024 * 1024;
@@ -63,6 +64,8 @@ int main(int argc, char* argv[])
howmany = atoi(argv[1]);
if (argc > 2)
threads = atoi(argv[2]);
+ if (argc > 3)
+ queue_size = atoi(argv[3]);
cout << "*******************************************************************************\n";
@@ -92,7 +95,7 @@ int main(int argc, char* argv[])
cout << "*******************************************************************************\n";
- spdlog::set_async_mode(howmany);
+ spdlog::set_async_mode(queue_size);
for(int i = 0; i < 3; ++i)
{
diff --git a/example/example.cpp b/example/example.cpp
index 96226e32..6159770a 100644
--- a/example/example.cpp
+++ b/example/example.cpp
@@ -93,7 +93,7 @@ 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!";
-
+ spdlog::drop_all(); //Close all loggers
//
// syslog example. linux only..
//
@@ -112,7 +112,8 @@ int main(int, char*[])
// Example of user defined class with operator<<
class some_class {};
-std::ostream& operator<<(std::ostream& os, const some_class&) {
+std::ostream& operator<<(std::ostream& os, const some_class&)
+{
return os << "some_class";
}
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 8a6bfbf2..01b22121 100644
--- a/include/spdlog/common.h
+++ b/include/spdlog/common.h
@@ -36,13 +36,6 @@
#define SPDLOG_NOEXCEPT throw()
#endif
-// under linux, you can use the much faster CLOCK_REALTIME_COARSE clock.
-// this clock is less accurate - can be off by few millis - depending on the kernel HZ
-// uncomment to use it instead of the regular (and slower) clock
-
-//#ifdef __linux__
-//#define SPDLOG_CLOCK_COARSE
-//#endif
namespace spdlog
{
@@ -56,25 +49,26 @@ 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;
+
//Log level enum
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"};
@@ -119,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 56804259..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
@@ -59,16 +60,17 @@ class async_log_helper
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
+ size_t thread_id;
std::string txt;
async_msg() = default;
~async_msg() = default;
- async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
- logger_name(std::move(other.logger_name)),
- level(std::move(other.level)),
- time(std::move(other.time)),
- txt(std::move(other.txt))
+async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
+ logger_name(std::move(other.logger_name)),
+ level(std::move(other.level)),
+ time(std::move(other.time)),
+ txt(std::move(other.txt))
{}
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
@@ -76,6 +78,7 @@ class async_log_helper
logger_name = std::move(other.logger_name);
level = other.level;
time = std::move(other.time);
+ thread_id = other.thread_id;
txt = std::move(other.txt);
return *this;
}
@@ -88,6 +91,7 @@ class async_log_helper
logger_name(m.logger_name),
level(m.level),
time(m.time),
+ thread_id(m.thread_id),
txt(m.raw.data(), m.raw.size())
{}
@@ -99,6 +103,7 @@ class async_log_helper
msg.logger_name = logger_name;
msg.level = level;
msg.time = time;
+ msg.thread_id = thread_id;
msg.raw << txt;
}
};
@@ -115,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);
@@ -141,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;
@@ -152,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);
+
+
};
}
@@ -164,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)
{}
@@ -195,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)));
}
@@ -210,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)
{
@@ -225,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;
@@ -233,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;
@@ -245,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;
@@ -257,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 c39c6849..6c0905b0 100644
--- a/include/spdlog/details/format.cc
+++ b/include/spdlog/details/format.cc
@@ -1,34 +1,31 @@
/*
-
-Modified version of cppformat formatting library
-
-Orginal license:
-
-Copyright (c) 2012 - 2014, Victor Zverovich
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this
-list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
-and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-
+ Formatting library for C++
+
+ Copyright (c) 2012 - 2015, Victor Zverovich
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "format.h"
#include
@@ -38,19 +35,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
#include
-#ifdef _WIN32
-# ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-# endif
-# 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 spdlog::details::fmt::LongLong;
-using spdlog::details::fmt::ULongLong;
-using spdlog::details::fmt::internal::Arg;
+using fmt::internal::Arg;
// Check if exceptions are disabled.
#if __GNUC__ && !__EXCEPTIONS
@@ -88,45 +87,66 @@ using spdlog::details::fmt::internal::Arg;
#if _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
+# pragma warning(disable: 4702) // unreachable code
+// Disable deprecation warning for strerror. The latter is not called but
+// MSVC fails to detect it.
+# pragma warning(disable: 4996)
#endif
+// Dummy implementations of strerror_r and strerror_s called if corresponding
+// system functions are not available.
+static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
+ return fmt::internal::Null<>();
+}
+static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
+ return fmt::internal::Null<>();
+}
+
+namespace fmt {
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
- va_list args;
- va_start(args, format);
- int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
- va_end(args);
- return result;
+ va_list args;
+ va_start(args, format);
+ int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
+ va_end(args);
+ return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
+#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
+# define FMT_SWPRINTF snwprintf
+#else
+# define FMT_SWPRINTF swprintf
+#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
+
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template
struct IntChecker {
- template
- static bool fits_in_int(T value) {
- unsigned max = INT_MAX;
- return value <= max;
- }
+ template
+ static bool fits_in_int(T value) {
+ unsigned max = INT_MAX;
+ return value <= max;
+ }
+ static bool fits_in_int(bool) { return true; }
};
template <>
struct IntChecker {
- template
- static bool fits_in_int(T value) {
- return value >= INT_MIN && value <= INT_MAX;
- }
+ template
+ static bool fits_in_int(T value) {
+ return value >= INT_MIN && value <= INT_MAX;
+ }
};
const char RESET_COLOR[] = "\x1b[0m";
-typedef void(*FormatFunc)(spdlog::details::fmt::Writer &, int, spdlog::details::fmt::StringRef);
+typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
@@ -137,271 +157,415 @@ typedef void(*FormatFunc)(spdlog::details::fmt::Writer &, int, spdlog::details::
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
-FMT_FUNC int safe_strerror(
- int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT(true) {
- assert(buffer != 0 && buffer_size != 0);
- int result = 0;
-#ifdef __ANDROID__
- result = strerror_r(error_code, buffer, buffer_size);
-#elif defined(_GNU_SOURCE)
- char *message = strerror_r(error_code, buffer, buffer_size);
- // If the buffer is full then the message is probably truncated.
- if (message == buffer && strlen(buffer) == buffer_size - 1)
- result = ERANGE;
- buffer = message;
-#elif __MINGW32__
- errno = 0;
- (void)buffer_size;
- buffer = strerror(error_code);
- result = errno;
-#elif _WIN32
- result = strerror_s(buffer, buffer_size, error_code);
- // If the buffer is full then the message is probably truncated.
- if (result == 0 && std::strlen(buffer) == buffer_size - 1)
- result = ERANGE;
-#else
- result = strerror_r(error_code, buffer, buffer_size);
- if (result == -1)
- result = errno; // glibc versions before 2.13 return result in errno.
-#endif
- return result;
+int safe_strerror(
+ int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
+ FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
+
+ class StrError {
+ private:
+ int error_code_;
+ char *&buffer_;
+ std::size_t buffer_size_;
+
+ // A noop assignment operator to avoid bogus warnings.
+ void operator=(const StrError &) {}
+
+ // Handle the result of XSI-compliant version of strerror_r.
+ int handle(int result) {
+ // glibc versions before 2.13 return result in errno.
+ return result == -1 ? errno : result;
+ }
+
+ // Handle the result of GNU-specific version of strerror_r.
+ int handle(char *message) {
+ // If the buffer is full then the message is probably truncated.
+ if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
+ return ERANGE;
+ buffer_ = message;
+ return 0;
+ }
+
+ // Handle the case when strerror_r is not available.
+ int handle(fmt::internal::Null<>) {
+ return fallback(strerror_s(buffer_, buffer_size_, error_code_));
+ }
+
+ // Fallback to strerror_s when strerror_r is not available.
+ int fallback(int result) {
+ // If the buffer is full then the message is probably truncated.
+ return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
+ ERANGE : result;
+ }
+
+ // Fallback to strerror if strerror_r and strerror_s are not available.
+ int fallback(fmt::internal::Null<>) {
+ errno = 0;
+ buffer_ = strerror(error_code_);
+ return errno;
+ }
+
+ public:
+ StrError(int err_code, char *&buf, std::size_t buf_size)
+ : error_code_(err_code), buffer_(buf), buffer_size_(buf_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();
}
-FMT_FUNC void format_error_code(spdlog::details::fmt::Writer &out, int error_code,
- spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
- // Report error code making sure that the output fits into
- // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
- // bad_alloc.
- out.clear();
- static const char SEP[] = ": ";
- static const char FMT_ERROR[] = "error ";
- spdlog::details::fmt::internal::IntTraits::MainType ec_value = error_code;
- // Subtract 2 to account for terminating null characters in SEP and FMT_ERROR.
- std::size_t error_code_size =
- sizeof(SEP) + sizeof(FMT_ERROR) + spdlog::details::fmt::internal::count_digits(ec_value) - 2;
- if (message.size() <= spdlog::details::fmt::internal::INLINE_BUFFER_SIZE - error_code_size)
- out << message << SEP;
- out << FMT_ERROR << error_code;
- assert(out.size() <= spdlog::details::fmt::internal::INLINE_BUFFER_SIZE);
+void format_error_code(fmt::Writer &out, int error_code,
+ fmt::StringRef message) FMT_NOEXCEPT {
+ // Report error code making sure that the output fits into
+ // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
+ // bad_alloc.
+ out.clear();
+ static const char SEP[] = ": ";
+ static const char ERROR_STR[] = "error ";
+ fmt::internal::IntTraits::MainType ec_value = error_code;
+ // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
+ std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
+ error_code_size += fmt::internal::count_digits(ec_value);
+ if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size)
+ out << message << SEP;
+ out << ERROR_STR << error_code;
+ assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE);
}
-FMT_FUNC void report_error(FormatFunc func,
- int error_code, spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
- spdlog::details::fmt::MemoryWriter full_message;
- func(full_message, error_code, message);
- // Use Writer::data instead of Writer::c_str to avoid potential memory
- // allocation.
- std::fwrite(full_message.data(), full_message.size(), 1, stderr);
- std::fputc('\n', stderr);
+void report_error(FormatFunc func,
+ int error_code, fmt::StringRef message) FMT_NOEXCEPT {
+ fmt::MemoryWriter full_message;
+ func(full_message, error_code, message);
+ // Use Writer::data instead of Writer::c_str to avoid potential memory
+ // allocation.
+ std::fwrite(full_message.data(), full_message.size(), 1, stderr);
+ std::fputc('\n', stderr);
}
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
-class IsZeroInt : public spdlog::details::fmt::internal::ArgVisitor {
-public:
- template
- bool visit_any_int(T value) {
- return value == 0;
- }
+class IsZeroInt : public fmt::internal::ArgVisitor {
+ public:
+ template
+ bool visit_any_int(T value) { return value == 0; }
};
// Parses an unsigned integer advancing s to the end of the parsed input.
// This function assumes that the first character of s is a digit.
template
-FMT_FUNC int parse_nonnegative_int(const Char *&s) {
- assert('0' <= *s && *s <= '9');
- unsigned value = 0;
- do {
- unsigned new_value = value * 10 + (*s++ - '0');
- // Check if value wrapped around.
- if (new_value < value) {
- value = UINT_MAX;
- break;
- }
- value = new_value;
- } while ('0' <= *s && *s <= '9');
- if (value > INT_MAX)
- FMT_THROW(spdlog::details::fmt::FormatError("number is too big"));
- return value;
+int parse_nonnegative_int(const Char *&s) {
+ assert('0' <= *s && *s <= '9');
+ unsigned value = 0;
+ do {
+ unsigned new_value = value * 10 + (*s++ - '0');
+ // Check if value wrapped around.
+ if (new_value < value) {
+ value = UINT_MAX;
+ break;
+ }
+ value = new_value;
+ } while ('0' <= *s && *s <= '9');
+ if (value > INT_MAX)
+ FMT_THROW(fmt::FormatError("number is too big"));
+ 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 =
- spdlog::details::fmt::format("format specifier '{}' requires numeric argument", spec);
- FMT_THROW(spdlog::details::fmt::FormatError(message));
- }
+ if (arg.type > Arg::LAST_NUMERIC_TYPE) {
+ std::string message =
+ fmt::format("format specifier '{}' requires numeric argument", spec);
+ FMT_THROW(fmt::FormatError(message));
+ }
}
template
-FMT_FUNC void check_sign(const Char *&s, const Arg &arg) {
- char sign = static_cast(*s);
- require_numeric_argument(arg, sign);
- if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) {
- FMT_THROW(spdlog::details::fmt::FormatError(spdlog::details::fmt::format(
- "format specifier '{}' requires signed argument", sign)));
- }
- ++s;
+void check_sign(const Char *&s, const Arg &arg) {
+ char sign = static_cast(*s);
+ require_numeric_argument(arg, sign);
+ if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) {
+ FMT_THROW(fmt::FormatError(fmt::format(
+ "format specifier '{}' requires signed argument", sign)));
+ }
+ ++s;
}
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
-class WidthHandler : public spdlog::details::fmt::internal::ArgVisitor {
-private:
- spdlog::details::fmt::FormatSpec &spec_;
-
-public:
- explicit WidthHandler(spdlog::details::fmt::FormatSpec &spec) : spec_(spec) {}
-
- unsigned visit_unhandled_arg() {
- FMT_THROW(spdlog::details::fmt::FormatError("width is not integer"));
- return 0;
- }
-
- template
- unsigned visit_any_int(T value) {
- typedef typename spdlog::details::fmt::internal::IntTraits::MainType UnsignedType;
- UnsignedType width = value;
- if (spdlog::details::fmt::internal::is_negative(value)) {
- spec_.align_ = spdlog::details::fmt::ALIGN_LEFT;
- width = 0 - width;
- }
- if (width > INT_MAX)
- FMT_THROW(spdlog::details::fmt::FormatError("number is too big"));
- return static_cast(width);
+class WidthHandler : public fmt::internal::ArgVisitor {
+ private:
+ fmt::FormatSpec &spec_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
+
+ public:
+ explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {}
+
+ void report_unhandled_arg() {
+ FMT_THROW(fmt::FormatError("width is not integer"));
+ }
+
+ template
+ unsigned visit_any_int(T value) {
+ typedef typename fmt::internal::IntTraits::MainType UnsignedType;
+ UnsignedType width = value;
+ if (fmt::internal::is_negative(value)) {
+ spec_.align_ = fmt::ALIGN_LEFT;
+ width = 0 - width;
}
+ if (width > INT_MAX)
+ FMT_THROW(fmt::FormatError("number is too big"));
+ return static_cast(width);
+ }
};
class PrecisionHandler :
- public spdlog::details::fmt::internal::ArgVisitor {
-public:
- unsigned visit_unhandled_arg() {
- FMT_THROW(spdlog::details::fmt::FormatError("precision is not integer"));
- return 0;
- }
-
- template
- int visit_any_int(T value) {
- if (!IntChecker::is_signed>::fits_in_int(value))
- FMT_THROW(spdlog::details::fmt::FormatError("number is too big"));
- return static_cast(value);
- }
+ public fmt::internal::ArgVisitor {
+ public:
+ void report_unhandled_arg() {
+ FMT_THROW(fmt::FormatError("precision is not integer"));
+ }
+
+ template
+ int visit_any_int(T value) {
+ if (!IntChecker::is_signed>::fits_in_int(value))
+ FMT_THROW(fmt::FormatError("number is too big"));
+ return static_cast(value);
+ }
};
// Converts an integer argument to an integral type T for printf.
template
-class ArgConverter : public spdlog::details::fmt::internal::ArgVisitor, void> {
-private:
- spdlog::details::fmt::internal::Arg &arg_;
- wchar_t type_;
-
-public:
- ArgConverter(spdlog::details::fmt::internal::Arg &arg, wchar_t type)
- : arg_(arg), type_(type) {}
-
- template
- void visit_any_int(U value) {
- bool is_signed = type_ == 'd' || type_ == 'i';
- using spdlog::details::fmt::internal::Arg;
- if (sizeof(T) <= sizeof(int)) {
- // Extra casts are used to silence warnings.
- if (is_signed) {
- arg_.type = Arg::INT;
- arg_.int_value = static_cast(static_cast(value));
- }
- else {
- arg_.type = Arg::UINT;
- arg_.uint_value = static_cast(
- static_cast::Type>(value));
- }
- }
- else {
- if (is_signed) {
- arg_.type = Arg::LONG_LONG;
- arg_.long_long_value =
- static_cast::Type>(value);
- }
- else {
- arg_.type = Arg::ULONG_LONG;
- arg_.ulong_long_value =
- static_cast::Type>(value);
- }
- }
+class ArgConverter : public fmt::internal::ArgVisitor, void> {
+ private:
+ fmt::internal::Arg &arg_;
+ wchar_t type_;
+
+ FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
+
+ public:
+ ArgConverter(fmt::internal::Arg &arg, wchar_t type)
+ : arg_(arg), type_(type) {}
+
+ template
+ void visit_any_int(U value) {
+ bool is_signed = type_ == 'd' || type_ == 'i';
+ using fmt::internal::Arg;
+ if (sizeof(T) <= sizeof(int)) {
+ // Extra casts are used to silence warnings.
+ if (is_signed) {
+ arg_.type = Arg::INT;
+ arg_.int_value = static_cast(static_cast(value));
+ } else {
+ arg_.type = Arg::UINT;
+ arg_.uint_value = static_cast(
+ static_cast::Type>(value));
+ }
+ } else {
+ if (is_signed) {
+ arg_.type = Arg::LONG_LONG;
+ arg_.long_long_value =
+ static_cast::Type>(value);
+ } else {
+ arg_.type = Arg::ULONG_LONG;
+ arg_.ulong_long_value =
+ static_cast::Type>(value);
+ }
}
+ }
};
// Converts an integer argument to char for printf.
-class CharConverter : public spdlog::details::fmt::internal::ArgVisitor {
-private:
- spdlog::details::fmt::internal::Arg &arg_;
+class CharConverter : public fmt::internal::ArgVisitor {
+ private:
+ fmt::internal::Arg &arg_;
-public:
- explicit CharConverter(spdlog::details::fmt::internal::Arg &arg) : arg_(arg) {}
+ FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
- template
- void visit_any_int(T value) {
- arg_.type = Arg::CHAR;
- arg_.int_value = static_cast(value);
- }
+ public:
+ explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {}
+
+ template
+ void visit_any_int(T value) {
+ arg_.type = Arg::CHAR;
+ arg_.int_value = static_cast(value);
+ }
};
+} // namespace
-// 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);
+namespace internal {
-template <>
-inline Arg::StringValue ignore_incompatible_str(
- Arg::StringValue) {
- return Arg::StringValue();
-}
+template
+class BasicArgFormatter : public ArgVisitor {
+ private:
+ BasicWriter &writer_;
+ FormatSpec &spec_;
-template <>
-inline Arg::StringValue ignore_incompatible_str(
- Arg::StringValue s) {
- return s;
-}
-} // namespace
+ 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_); }
-FMT_FUNC void spdlog::details::fmt::SystemError::init(
- int error_code, StringRef format_str, ArgList args) {
- error_code_ = error_code;
- MemoryWriter w;
- internal::format_system_error(w, error_code, format(format_str, args));
- std::runtime_error &base = *this;
- base = std::runtime_error(w.str());
+ 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, CStringRef format_str, ArgList args) {
+ error_code_ = err_code;
+ MemoryWriter w;
+ internal::format_system_error(w, err_code, format(format_str, args));
+ std::runtime_error &base = *this;
+ base = std::runtime_error(w.str());
}
template
-FMT_FUNC int spdlog::details::fmt::internal::CharTraits::format_float(
+int fmt::internal::CharTraits::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
- if (width == 0) {
- return precision < 0 ?
- FMT_SNPRINTF(buffer, size, format, value) :
- FMT_SNPRINTF(buffer, size, format, precision, value);
- }
+ if (width == 0) {
return precision < 0 ?
- FMT_SNPRINTF(buffer, size, format, width, value) :
- FMT_SNPRINTF(buffer, size, format, width, precision, value);
+ FMT_SNPRINTF(buffer, size, format, value) :
+ FMT_SNPRINTF(buffer, size, format, precision, value);
+ }
+ return precision < 0 ?
+ FMT_SNPRINTF(buffer, size, format, width, value) :
+ FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template
-FMT_FUNC int spdlog::details::fmt::internal::CharTraits::format_float(
+int fmt::internal::CharTraits::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
- if (width == 0) {
- return precision < 0 ?
- swprintf(buffer, size, format, value) :
- swprintf(buffer, size, format, precision, value);
- }
+ if (width == 0) {
return precision < 0 ?
- swprintf(buffer, size, format, width, value) :
- swprintf(buffer, size, format, width, precision, value);
+ FMT_SWPRINTF(buffer, size, format, value) :
+ FMT_SWPRINTF(buffer, size, format, precision, value);
+ }
+ return precision < 0 ?
+ FMT_SWPRINTF(buffer, size, format, width, value) :
+ FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template
-const char spdlog::details::fmt::internal::BasicData::DIGITS[] =
+const char fmt::internal::BasicData::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
@@ -420,751 +584,735 @@ const char spdlog::details::fmt::internal::BasicData::DIGITS[] =
factor * 1000000000
template
-const uint32_t spdlog::details::fmt::internal::BasicData::POWERS_OF_10_32[] = {
- 0, FMT_POWERS_OF_10(1)
+const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = {
+ 0, FMT_POWERS_OF_10(1)
};
template
-const uint64_t spdlog::details::fmt::internal::BasicData::POWERS_OF_10_64[] = {
- 0,
- FMT_POWERS_OF_10(1),
- FMT_POWERS_OF_10(ULongLong(1000000000)),
- // Multiply several constants instead of using a single long long constant
- // to avoid warnings about C++98 not supporting long long.
- ULongLong(1000000000) * ULongLong(1000000000) * 10
+const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = {
+ 0,
+ FMT_POWERS_OF_10(1),
+ FMT_POWERS_OF_10(fmt::ULongLong(1000000000)),
+ // Multiply several constants instead of using a single long long constant
+ // to avoid warnings about C++98 not supporting long long.
+ fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10
};
-FMT_FUNC void spdlog::details::fmt::internal::report_unknown_type(char code, const char *type) {
- if (std::isprint(static_cast(code))) {
- FMT_THROW(spdlog::details::fmt::FormatError(
- spdlog::details::fmt::format("unknown format code '{}' for {}", code, type)));
- }
- FMT_THROW(spdlog::details::fmt::FormatError(
- spdlog::details::fmt::format("unknown format code '\\x{:02x}' for {}",
- static_cast(code), type)));
+FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) {
+ (void)type;
+ if (std::isprint(static_cast(code))) {
+ FMT_THROW(fmt::FormatError(
+ fmt::format("unknown format code '{}' for {}", code, type)));
+ }
+ FMT_THROW(fmt::FormatError(
+ fmt::format("unknown format code '\\x{:02x}' for {}",
+ static_cast(code), type)));
}
-#ifdef _WIN32
-
-FMT_FUNC spdlog::details::fmt::internal::UTF8ToUTF16::UTF8ToUTF16(spdlog::details::fmt::StringRef s) {
- int length = MultiByteToWideChar(
- CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0);
- static const char FMT_ERROR[] = "cannot convert string from UTF-8 to UTF-16";
- if (length == 0)
- FMT_THROW(WindowsError(GetLastError(), FMT_ERROR));
- buffer_.resize(length);
- length = MultiByteToWideChar(
- CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length);
- if (length == 0)
- FMT_THROW(WindowsError(GetLastError(), FMT_ERROR));
+#if FMT_USE_WINDOWS_H
+
+FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) {
+ 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 + 1);
+ length = MultiByteToWideChar(
+ 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 spdlog::details::fmt::internal::UTF16ToUTF8::UTF16ToUTF8(spdlog::details::fmt::WStringRef s) {
- if (int error_code = convert(s)) {
- FMT_THROW(WindowsError(error_code,
- "cannot convert string from UTF-16 to UTF-8"));
- }
+FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
+ if (int error_code = convert(s)) {
+ FMT_THROW(WindowsError(error_code,
+ "cannot convert string from UTF-16 to UTF-8"));
+ }
}
-FMT_FUNC int spdlog::details::fmt::internal::UTF16ToUTF8::convert(spdlog::details::fmt::WStringRef s) {
- int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0);
- if (length == 0)
- return GetLastError();
- buffer_.resize(length);
- length = WideCharToMultiByte(
- CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0);
- if (length == 0)
- return GetLastError();
- return 0;
+FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) {
+ 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 + 1);
+ length = WideCharToMultiByte(
+ 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 spdlog::details::fmt::WindowsError::init(
- int error_code, StringRef format_str, ArgList args) {
- error_code_ = error_code;
- MemoryWriter w;
- internal::format_windows_error(w, error_code, format(format_str, args));
- std::runtime_error &base = *this;
- base = std::runtime_error(w.str());
+FMT_FUNC void fmt::WindowsError::init(
+ 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));
+ std::runtime_error &base = *this;
+ base = std::runtime_error(w.str());
}
-#endif
-
-FMT_FUNC void spdlog::details::fmt::internal::format_system_error(
- spdlog::details::fmt::Writer &out, int error_code,
- spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
- 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 spdlog::details::fmt::internal::format_windows_error(
- spdlog::details::fmt::Writer &out, int error_code,
- spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
- class String {
- private:
- LPWSTR str_;
-
- public:
- String() : str_() {}
- ~String() {
- LocalFree(str_);
- }
- LPWSTR *ptr() {
- return &str_;
- }
- LPCWSTR c_str() const {
- return str_;
- }
- };
- FMT_TRY {
- String system_message;
- if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+FMT_FUNC void fmt::internal::format_windows_error(
+ fmt::Writer &out, int error_code,
+ fmt::StringRef message) FMT_NOEXCEPT {
+ class String {
+ private:
+ LPWSTR str_;
+
+ public:
+ String() : str_() {}
+ ~String() { LocalFree(str_); }
+ LPWSTR *ptr() { return &str_; }
+ LPCWSTR c_str() const { return str_; }
+ };
+ FMT_TRY {
+ String system_message;
+ if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast(system_message.ptr()), 0, 0)) {
- UTF16ToUTF8 utf8_message;
- if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) {
- out << message << ": " << utf8_message;
- return;
- }
- }
- } FMT_CATCH(...) {}
- format_error_code(out, error_code, message);
-}
-#endif
-
-// An argument formatter.
-template
-class spdlog::details::fmt::internal::ArgFormatter :
- public spdlog::details::fmt::internal::ArgVisitor, void> {
-private:
- spdlog::details::fmt::BasicFormatter &formatter_;
- spdlog::details::fmt::BasicWriter &writer_;
- spdlog::details::fmt::FormatSpec &spec_;
- const Char *format_;
-
-public:
- ArgFormatter(
- spdlog::details::fmt::BasicFormatter &f, spdlog::details::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_);
+ UTF16ToUTF8 utf8_message;
+ if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) {
+ out << message << ": " << utf8_message;
+ return;
+ }
}
+ } FMT_CATCH(...) {}
+ format_error_code(out, error_code, message);
+}
- template
- void visit_any_double(T value) {
- writer_.write_double(value, spec_);
- }
+#endif // FMT_USE_WINDOWS_H
- 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 spdlog::details::fmt::BasicWriter::CharPtr CharPtr;
- CharPtr out = CharPtr();
- if (spec_.width_ > 1) {
- Char fill = static_cast(spec_.fill());
- out = writer_.grow_buffer(spec_.width_);
- if (spec_.align_ == spdlog::details::fmt::ALIGN_RIGHT) {
- std::fill_n(out, spec_.width_ - 1, fill);
- out += spec_.width_ - 1;
- }
- else if (spec_.align_ == spdlog::details::fmt::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 = static_cast(value);
+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);
+}
- 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*/;
+ }
}
- void visit_wstring(Arg::StringValue value) {
- writer_.write_str(ignore_incompatible_str(value), spec_);
+ return;
+ }
+ 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')
- spdlog::details::fmt::internal::report_unknown_type(spec_.type_, "pointer");
- spec_.flags_ = spdlog::details::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) {
+ FMT_THROW(std::runtime_error("buffer overflow"));
+}
template
template
-FMT_FUNC void spdlog::details::fmt::BasicWriter::write_str(
- const Arg::StringValue &str, const FormatSpec &spec) {
- // Check if StrChar is convertible to Char.
- internal::CharTraits::convert(StrChar());
- if (spec.type_ && spec.type_ != 's')
- internal::report_unknown_type(spec.type_, "string");
- const StrChar *s = str.value;
- std::size_t size = str.size;
- if (size == 0) {
- if (!s)
- FMT_THROW(FormatError("string pointer is null"));
- if (*s)
- size = std::char_traits::length(s);
- }
- write_str(s, size, spec);
+void fmt::BasicWriter::write_str(
+ const Arg::StringValue &s, const FormatSpec &spec) {
+ // Check if StrChar is convertible to Char.
+ internal::CharTraits::convert(StrChar());
+ if (spec.type_ && spec.type_ != 's')
+ internal::report_unknown_type(spec.type_, "string");
+ const StrChar *str_value = s.value;
+ std::size_t str_size = s.size;
+ if (str_size == 0) {
+ if (!str_value)
+ FMT_THROW(FormatError("string pointer is null"));
+ if (*str_value)
+ str_size = std::char_traits::length(str_value);
+ }
+ std::size_t precision = spec.precision_;
+ if (spec.precision_ >= 0 && precision < str_size)
+ str_size = spec.precision_;
+ write_str(str_value, str_size, spec);
}
template
-inline Arg spdlog::details::fmt::BasicFormatter::parse_arg_index(const Char *&s) {
- const char *error = 0;
- Arg arg = *s < '0' || *s > '9' ? next_arg(error) : get_arg(parse_nonnegative_int(s), error);
- if (error) {
- FMT_THROW(FormatError(*s != '}' && *s != ':' ? "invalid format string" : error));
- }
- return arg;
+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();
}
-FMT_FUNC Arg spdlog::details::fmt::internal::FormatterBase::do_get_arg(
- unsigned arg_index, const char *&error) {
- Arg arg = args_[arg_index];
- if (arg.type == Arg::NONE)
- error = "argument index out of range";
- return arg;
+template
+inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) {
+ const char *error = 0;
+ Arg arg = *s < '0' || *s > '9' ?
+ next_arg(error) : get_arg(parse_nonnegative_int(s), error);
+ if (error) {
+ FMT_THROW(FormatError(
+ *s != '}' && *s != ':' ? "invalid format string" : error));
+ }
+ return arg;
}
-inline Arg spdlog::details::fmt::internal::FormatterBase::next_arg(const char *&error) {
- if (next_arg_index_ >= 0)
- return do_get_arg(next_arg_index_++, error);
- error = "cannot switch from manual to automatic argument indexing";
- 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;
}
-inline Arg spdlog::details::fmt::internal::FormatterBase::get_arg(
+FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
- if (next_arg_index_ <= 0) {
- next_arg_index_ = -1;
- return do_get_arg(arg_index, error);
- }
+ Arg arg = args_[arg_index];
+ 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;
+}
+
+inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) {
+ if (next_arg_index_ >= 0)
+ return do_get_arg(next_arg_index_++, error);
+ error = "cannot switch from manual to automatic argument indexing";
+ 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 Arg();
+ return false;
+ }
+ next_arg_index_ = -1;
+ return true;
+}
+
+inline Arg fmt::internal::FormatterBase::get_arg(
+ unsigned arg_index, const char *&error) {
+ return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg();
}
template
-FMT_FUNC void spdlog::details::fmt::internal::PrintfFormatter::parse_flags(
+void fmt::internal::PrintfFormatter::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;
- }
+ 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
-FMT_FUNC Arg spdlog::details::fmt::internal::PrintfFormatter::get_arg(
+Arg fmt::internal::PrintfFormatter::get_arg(
const Char *s, unsigned arg_index) {
- const char *error = 0;
- Arg arg = arg_index == UINT_MAX ?
- next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
- if (error)
- FMT_THROW(FormatError(!*s ? "invalid format string" : error));
- return arg;
+ (void)s;
+ const char *error = 0;
+ Arg arg = arg_index == UINT_MAX ?
+ next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
+ if (error)
+ FMT_THROW(FormatError(!*s ? "invalid format string" : error));
+ return arg;
}
template
-FMT_FUNC unsigned spdlog::details::fmt::internal::PrintfFormatter::parse_header(
- const Char *&s, FormatSpec &spec) {
- unsigned arg_index = UINT_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 = 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;
- }
- }
+unsigned fmt::internal::PrintfFormatter::parse_header(
+ const Char *&s, FormatSpec &spec) {
+ unsigned arg_index = UINT_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 = 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_ = parse_nonnegative_int(s);
+ }
+ parse_flags(spec, s);
+ // Parse width.
+ if (*s >= '0' && *s <= '9') {
+ spec.width_ = parse_nonnegative_int(s);
+ } else if (*s == '*') {
+ ++s;
+ spec.width_ = WidthHandler(spec).visit(get_arg(s));
+ }
+ return arg_index;
+}
+
+template
+void fmt::internal::PrintfFormatter::format(
+ BasicWriter &writer, BasicCStringRef 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;
}
- else if (*s == '*') {
+ 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_ = parse_nonnegative_int(s);
+ } else if (*s == '*') {
++s;
- spec.width_ = WidthHandler(spec).visit(get_arg(s));
+ spec.precision_ = PrecisionHandler().visit(get_arg(s));
+ }
}
- return arg_index;
-}
-template
-FMT_FUNC void spdlog::details::fmt::internal::PrintfFormatter::format(
- BasicWriter &writer, BasicStringRef format,
- const ArgList &args) {
- const Char *start = format.c_str();
- set_args(args);
- 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_ = parse_nonnegative_int(s);
- }
- else if (*s == '*') {
- ++s;
- spec.precision_ = PrecisionHandler().visit(get_arg(s));
- }
- }
+ Arg arg = get_arg(s, arg_index);
+ if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
+ spec.flags_ &= ~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.
+ }
- Arg arg = get_arg(s, arg_index);
- if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
- spec.flags_ &= ~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.
+ switch (*s++) {
+ case 'h':
+ if (*s == 'h')
+ ArgConverter(arg, *++s).visit(arg);
+ else
+ ArgConverter(arg, *s).visit(arg);
+ break;
+ case 'l':
+ if (*s == 'l')
+ ArgConverter(arg, *++s).visit(arg);
+ else
+ ArgConverter(arg, *s).visit(arg);
+ break;
+ case 'j':
+ ArgConverter(arg, *s).visit(arg);
+ break;
+ case 'z':
+ ArgConverter(arg, *s).visit(arg);
+ break;
+ case 't':
+ ArgConverter(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(arg, *s).visit(arg);
+ }
- // Parse length and convert the argument to the required type.
- switch (*s++) {
- case 'h':
- if (*s == 'h')
- ArgConverter(arg, *++s).visit(arg);
- else
- ArgConverter(arg, *s).visit(arg);
- break;
- case 'l':
- if (*s == 'l')
- ArgConverter(arg, *++s).visit(arg);
- else
- ArgConverter(arg, *s).visit(arg);
- break;
- case 'j':
- ArgConverter(arg, *s).visit(arg);
- break;
- case 'z':
- ArgConverter(arg, *s).visit(arg);
- break;
- case 't':
- ArgConverter(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(arg, *s).visit(arg);
- }
+ // Parse type.
+ if (!*s)
+ FMT_THROW(FormatError("invalid format string"));
+ spec.type_ = static_cast(*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
+ CharConverter(arg).visit(arg);
+ break;
+ }
+ }
- // Parse type.
- if (!*s)
- FMT_THROW(FormatError("invalid format string"));
- spec.type_ = static_cast(*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
- CharConverter(arg).visit(arg);
- break;
- }
- }
+ start = s;
- start = s;
+ // Format argument.
+ internal::PrintfArgFormatter(writer, spec).visit(arg);
+ }
+ write(writer, 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);
+template
+const Char *fmt::BasicFormatter::format(
+ const Char *&format_str, const Arg &arg) {
+ const Char *s = format_str;
+ FormatSpec spec;
+ if (*s == ':') {
+ if (arg.type == Arg::CUSTOM) {
+ arg.custom.format(this, arg.custom.value, &s);
+ return s;
+ }
+ ++s;
+ // Parse fill and alignment.
+ if (Char c = *s) {
+ const Char *p = s + 1;
+ spec.align_ = ALIGN_DEFAULT;
+ do {
+ switch (*p) {
+ case '<':
+ spec.align_ = ALIGN_LEFT;
break;
- case Arg::WSTRING:
- writer.write_str(ignore_incompatible_str(arg.wstring), spec);
+ case '>':
+ spec.align_ = ALIGN_RIGHT;
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);
+ case '=':
+ spec.align_ = ALIGN_NUMERIC;
break;
- case Arg::CUSTOM: {
- if (spec.type_)
- internal::report_unknown_type(spec.type_, "object");
- const void *s = "s";
- arg.custom.format(&writer, arg.custom.value, &s);
+ case '^':
+ spec.align_ = ALIGN_CENTER;
break;
}
- default:
- assert(false);
- break;
+ if (spec.align_ != ALIGN_DEFAULT) {
+ if (p != s) {
+ if (c == '}') break;
+ if (c == '{')
+ FMT_THROW(FormatError("invalid fill character '{'"));
+ s += 2;
+ spec.fill_ = c;
+ } else ++s;
+ if (spec.align_ == ALIGN_NUMERIC)
+ require_numeric_argument(arg, '=');
+ break;
}
+ } while (--p >= s);
}
- write(writer, start, s);
-}
-template
-FMT_FUNC const Char *spdlog::details::fmt::BasicFormatter::format(
- const Char *&format_str, const Arg &arg) {
- const Char *s = format_str;
- FormatSpec spec;
- if (*s == ':') {
- if (arg.type == Arg::CUSTOM) {
- arg.custom.format(this, arg.custom.value, &s);
- return s;
- }
- ++s;
- // Parse fill and alignment.
- if (Char c = *s) {
- const Char *p = s + 1;
- spec.align_ = ALIGN_DEFAULT;
- do {
- switch (*p) {
- case '<':
- spec.align_ = ALIGN_LEFT;
- break;
- case '>':
- spec.align_ = ALIGN_RIGHT;
- break;
- case '=':
- spec.align_ = ALIGN_NUMERIC;
- break;
- case '^':
- spec.align_ = ALIGN_CENTER;
- break;
- }
- if (spec.align_ != ALIGN_DEFAULT) {
- if (p != s) {
- if (c == '}') break;
- if (c == '{')
- FMT_THROW(FormatError("invalid fill character '{'"));
- s += 2;
- spec.fill_ = c;
- }
- else ++s;
- if (spec.align_ == ALIGN_NUMERIC)
- require_numeric_argument(arg, '=');
- break;
- }
- } while (--p >= s);
- }
+ // Parse sign.
+ switch (*s) {
+ case '+':
+ check_sign(s, arg);
+ spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
+ break;
+ case '-':
+ check_sign(s, arg);
+ spec.flags_ |= MINUS_FLAG;
+ break;
+ case ' ':
+ check_sign(s, arg);
+ spec.flags_ |= SIGN_FLAG;
+ break;
+ }
+
+ if (*s == '#') {
+ require_numeric_argument(arg, '#');
+ spec.flags_ |= HASH_FLAG;
+ ++s;
+ }
- // Parse sign.
- switch (*s) {
- case '+':
- check_sign(s, arg);
- spec.flags_ |= SIGN_FLAG | PLUS_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') {
+ 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.
+ if (*s == '.') {
+ ++s;
+ spec.precision_ = 0;
+ if ('0' <= *s && *s <= '9') {
+ spec.precision_ = parse_nonnegative_int(s);
+ } else if (*s == '{') {
+ ++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;
+ switch (precision_arg.type) {
+ case Arg::INT:
+ if (precision_arg.int_value < 0)
+ FMT_THROW(FormatError("negative precision"));
+ value = precision_arg.int_value;
break;
- case '-':
- check_sign(s, arg);
- spec.flags_ |= MINUS_FLAG;
+ case Arg::UINT:
+ value = precision_arg.uint_value;
break;
- case ' ':
- check_sign(s, arg);
- spec.flags_ |= SIGN_FLAG;
+ case Arg::LONG_LONG:
+ if (precision_arg.long_long_value < 0)
+ FMT_THROW(FormatError("negative precision"));
+ value = precision_arg.long_long_value;
break;
+ case Arg::ULONG_LONG:
+ value = precision_arg.ulong_long_value;
+ break;
+ default:
+ FMT_THROW(FormatError("precision is not integer"));
}
-
- if (*s == '#') {
- require_numeric_argument(arg, '#');
- spec.flags_ |= HASH_FLAG;
- ++s;
- }
-
- // Parse width and zero flag.
- 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);
- }
-
- // Parse precision.
- if (*s == '.') {
- ++s;
- spec.precision_ = 0;
- if ('0' <= *s && *s <= '9') {
- spec.precision_ = parse_nonnegative_int(s);
- }
- else if (*s == '{') {
- ++s;
- const Arg &precision_arg = parse_arg_index(s);
- if (*s++ != '}')
- FMT_THROW(FormatError("invalid format string"));
- ULongLong value = 0;
- switch (precision_arg.type) {
- case Arg::INT:
- if (precision_arg.int_value < 0)
- FMT_THROW(FormatError("negative precision"));
- value = precision_arg.int_value;
- break;
- case Arg::UINT:
- value = precision_arg.uint_value;
- break;
- case Arg::LONG_LONG:
- if (precision_arg.long_long_value < 0)
- FMT_THROW(FormatError("negative precision"));
- value = precision_arg.long_long_value;
- break;
- case Arg::ULONG_LONG:
- value = precision_arg.ulong_long_value;
- break;
- default:
- FMT_THROW(FormatError("precision is not integer"));
- }
- if (value > INT_MAX)
- FMT_THROW(FormatError("number is too big"));
- spec.precision_ = static_cast(value);
- }
- else {
- FMT_THROW(FormatError("missing precision specifier"));
- }
- if (arg.type != Arg::DOUBLE && arg.type != Arg::LONG_DOUBLE) {
- FMT_THROW(FormatError(
- "precision specifier requires floating-point argument"));
- }
- }
-
- // Parse type.
- if (*s != '}' && *s)
- spec.type_ = static_cast(*s++);
+ if (value > INT_MAX)
+ FMT_THROW(FormatError("number is too big"));
+ spec.precision_ = static_cast(value);
+ } else {
+ FMT_THROW(FormatError("missing precision specifier"));
+ }
+ 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")));
+ }
}
- if (*s++ != '}')
- FMT_THROW(FormatError("missing '}' in format string"));
- start_ = s;
+ // Parse type.
+ if (*s != '}' && *s)
+ spec.type_ = static_cast(*s++);
+ }
- // Format argument.
- internal::ArgFormatter(*this, spec, s - 1).visit(arg);
- return s;
+ if (*s++ != '}')
+ FMT_THROW(FormatError("missing '}' in format string"));
+
+ // Format argument.
+ internal::ArgFormatter(*this, spec, s - 1).visit(arg);
+ return s;
}
template
-FMT_FUNC void spdlog::details::fmt::BasicFormatter::format(
- BasicStringRef format_str, const ArgList &args) {
- const Char *s = start_ = format_str.c_str();
- set_args(args);
- while (*s) {
- Char c = *s++;
- if (c != '{' && c != '}') continue;
- if (*s == c) {
- 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);
+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;
+ continue;
}
- write(writer_, start_, s);
+ if (c == '}')
+ FMT_THROW(FormatError("unmatched '}' in format string"));
+ 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);
}
-FMT_FUNC void spdlog::details::fmt::report_system_error(
- int error_code, spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
- report_error(internal::format_system_error, error_code, message);
+FMT_FUNC void fmt::report_system_error(
+ int error_code, fmt::StringRef message) FMT_NOEXCEPT {
+ report_error(internal::format_system_error, error_code, message);
}
-#ifdef _WIN32
-FMT_FUNC void spdlog::details::fmt::report_windows_error(
- int error_code, spdlog::details::fmt::StringRef message) FMT_NOEXCEPT(true) {
- report_error(internal::format_windows_error, error_code, message);
+#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 spdlog::details::fmt::print(std::FILE *f, StringRef format_str, ArgList args) {
- MemoryWriter w;
- w.write(format_str, args);
- std::fwrite(w.data(), 1, w.size(), f);
+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 spdlog::details::fmt::print(StringRef format_str, ArgList args) {
- print(stdout, format_str, args);
+FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) {
+ print(stdout, format_str, args);
}
-FMT_FUNC void spdlog::details::fmt::print(std::ostream &os, StringRef format_str, ArgList args) {
- MemoryWriter w;
- w.write(format_str, args);
- os.write(w.data(), w.size());
+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 spdlog::details::fmt::print_colored(Color c, StringRef format, ArgList args) {
- char escape[] = "\x1b[30m";
- escape[3] = '0' + static_cast(c);
- std::fputs(escape, stdout);
- print(format, args);
- std::fputs(RESET_COLOR, stdout);
+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);
+ print(format, args);
+ std::fputs(RESET_COLOR, stdout);
}
-FMT_FUNC int spdlog::details::fmt::fprintf(std::FILE *f, StringRef format, ArgList args) {
- MemoryWriter w;
- printf(w, format, args);
- std::size_t size = w.size();
- return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size);
+FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) {
+ MemoryWriter w;
+ printf(w, format, args);
+ std::size_t size = w.size();
+ return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size);
}
+#ifndef FMT_HEADER_ONLY
+
+template struct fmt::internal::BasicData;
+
// Explicit instantiations for char.
-template const char *spdlog::details::fmt::BasicFormatter::format(
- const char *&format_str, const spdlog::details::fmt::internal::Arg &arg);
+template void fmt::internal::FixedBuffer::grow(std::size_t);
-template void spdlog::details::fmt::BasicFormatter::format(
- BasicStringRef format, const ArgList &args);
+template const char *fmt::BasicFormatter::format(
+ const char *&format_str, const fmt::internal::Arg &arg);
-template void spdlog::details::fmt::internal::PrintfFormatter