Commit 93be7713 authored by gabime's avatar gabime

astyle

parent 6ab2f0e0
/* /*
A modified version of Bounded MPMC queue by Dmitry Vyukov. A modified version of Bounded MPMC queue by Dmitry Vyukov.
Original code from: Original code from:
http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
licensed by Dmitry Vyukov under the terms below: licensed by Dmitry Vyukov under the terms below:
Simplified BSD license Simplified BSD license
Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of 1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer. conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list 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 of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution. provided with the distribution.
THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the authors and The views and conclusions contained in the software and documentation are those of the authors and
should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov. should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov.
*/ */
/* /*
The code in its current form adds the license below: The code in its current form adds the license below:
Copyright(c) 2015 Gabi Melman. Copyright(c) 2015 Gabi Melman.
Distributed under the MIT License (http://opensource.org/licenses/MIT) Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/ */
#pragma once #pragma once
#include "spdlog/common.h" #include "spdlog/common.h"
#include <atomic> #include <atomic>
#include <utility> #include <utility>
namespace spdlog namespace spdlog
{ {
namespace details namespace details
{ {
template<typename T> template<typename T>
class mpmc_bounded_queue class mpmc_bounded_queue
{ {
public: public:
using item_type = T; using item_type = T;
mpmc_bounded_queue(size_t buffer_size) mpmc_bounded_queue(size_t buffer_size)
:max_size_(buffer_size), :max_size_(buffer_size),
buffer_(new cell_t[buffer_size]), buffer_(new cell_t[buffer_size]),
buffer_mask_(buffer_size - 1) buffer_mask_(buffer_size - 1)
{ {
//queue size must be power of two //queue size must be power of two
if (!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) if (!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
throw spdlog_ex("async logger queue size must be power of two"); throw spdlog_ex("async logger queue size must be power of two");
for (size_t i = 0; i != buffer_size; i += 1) for (size_t i = 0; i != buffer_size; i += 1)
buffer_[i].sequence_.store(i, std::memory_order_relaxed); buffer_[i].sequence_.store(i, std::memory_order_relaxed);
enqueue_pos_.store(0, std::memory_order_relaxed); enqueue_pos_.store(0, std::memory_order_relaxed);
dequeue_pos_.store(0, std::memory_order_relaxed); dequeue_pos_.store(0, std::memory_order_relaxed);
} }
~mpmc_bounded_queue() ~mpmc_bounded_queue()
{ {
delete[] buffer_; delete[] buffer_;
} }
bool enqueue(T&& data) bool enqueue(T&& data)
{ {
cell_t* cell; cell_t* cell;
size_t pos = enqueue_pos_.load(std::memory_order_relaxed); size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
for (;;) { for (;;)
cell = &buffer_[pos & buffer_mask_]; {
size_t seq = cell->sequence_.load(std::memory_order_acquire); cell = &buffer_[pos & buffer_mask_];
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos); size_t seq = cell->sequence_.load(std::memory_order_acquire);
if (dif == 0) { intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos);
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) if (dif == 0)
break; {
} if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
else if (dif < 0) { break;
return false; }
} else if (dif < 0)
else { {
pos = enqueue_pos_.load(std::memory_order_relaxed); return false;
} }
} else
cell->data_ = std::move(data); {
cell->sequence_.store(pos + 1, std::memory_order_release); pos = enqueue_pos_.load(std::memory_order_relaxed);
return true; }
} }
cell->data_ = std::move(data);
bool dequeue(T& data) cell->sequence_.store(pos + 1, std::memory_order_release);
{ return true;
cell_t* cell; }
size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
for (;;) { bool dequeue(T& data)
cell = &buffer_[pos & buffer_mask_]; {
size_t seq = cell_t* cell;
cell->sequence_.load(std::memory_order_acquire); size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1); for (;;)
if (dif == 0) { {
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) cell = &buffer_[pos & buffer_mask_];
break; size_t seq =
} cell->sequence_.load(std::memory_order_acquire);
else if (dif < 0) intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1);
return false; if (dif == 0)
else {
pos = dequeue_pos_.load(std::memory_order_relaxed); if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
} break;
data = std::move(cell->data_); }
cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release); else if (dif < 0)
return true; return false;
} else
pos = dequeue_pos_.load(std::memory_order_relaxed);
bool is_empty() }
{ data = std::move(cell->data_);
size_t front, front1, back; cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release);
// try to take a consistent snapshot of front/tail. return true;
do { }
front = enqueue_pos_.load(std::memory_order_acquire);
back = dequeue_pos_.load(std::memory_order_acquire); bool is_empty()
front1 = enqueue_pos_.load(std::memory_order_relaxed); {
} while (front != front1); size_t front, front1, back;
return back == front; // try to take a consistent snapshot of front/tail.
} do
{
private: front = enqueue_pos_.load(std::memory_order_acquire);
struct cell_t back = dequeue_pos_.load(std::memory_order_acquire);
{ front1 = enqueue_pos_.load(std::memory_order_relaxed);
std::atomic<size_t> sequence_; }
T data_; while (front != front1);
}; return back == front;
}
size_t const max_size_;
private:
static size_t const cacheline_size = 64; struct cell_t
typedef char cacheline_pad_t[cacheline_size]; {
std::atomic<size_t> sequence_;
cacheline_pad_t pad0_; T data_;
cell_t* const buffer_; };
size_t const buffer_mask_;
cacheline_pad_t pad1_; size_t const max_size_;
std::atomic<size_t> enqueue_pos_;
cacheline_pad_t pad2_; static size_t const cacheline_size = 64;
std::atomic<size_t> dequeue_pos_; typedef char cacheline_pad_t[cacheline_size];
cacheline_pad_t pad3_;
cacheline_pad_t pad0_;
mpmc_bounded_queue(mpmc_bounded_queue const&) = delete; cell_t* const buffer_;
void operator= (mpmc_bounded_queue const&) = delete; size_t const buffer_mask_;
}; cacheline_pad_t pad1_;
std::atomic<size_t> enqueue_pos_;
} // ns details cacheline_pad_t pad2_;
} // ns spdlog std::atomic<size_t> dequeue_pos_;
cacheline_pad_t pad3_;
mpmc_bounded_queue(mpmc_bounded_queue const&) = delete;
void operator= (mpmc_bounded_queue const&) = delete;
};
} // ns details
} // ns spdlog
// //
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#pragma once #pragma once
#include "spdlog/common.h" #include "spdlog/common.h"
#include <cstdio> #include <cstdio>
#include <ctime> #include <ctime>
#include <functional> #include <functional>
#include <string> #include <string>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#ifdef _WIN32 #ifdef _WIN32
#ifndef NOMINMAX #ifndef NOMINMAX
#define NOMINMAX //prevent windows redefining min/max #define NOMINMAX //prevent windows redefining min/max
#endif #endif
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h> #include <windows.h>
#include <process.h> // _get_pid support #include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle and _isatty support #include <io.h> // _get_osfhandle and _isatty support
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <share.h> #include <share.h>
#endif #endif
#else // unix #else // unix
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#ifdef __linux__ #ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id #include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#elif __FreeBSD__ #elif __FreeBSD__
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id #include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#endif #endif
#endif //unix #endif //unix
#ifndef __has_feature // Clang - feature checking macros. #ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers. #define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif #endif
namespace spdlog namespace spdlog
{ {
namespace details namespace details
{ {
namespace os namespace os
{ {
inline spdlog::log_clock::time_point now() inline spdlog::log_clock::time_point now()
{ {
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE #if defined __linux__ && defined SPDLOG_CLOCK_COARSE
timespec ts; timespec ts;
::clock_gettime(CLOCK_REALTIME_COARSE, &ts); ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
return std::chrono::time_point<log_clock, typename log_clock::duration>( return std::chrono::time_point<log_clock, typename log_clock::duration>(
std::chrono::duration_cast<typename log_clock::duration>( std::chrono::duration_cast<typename log_clock::duration>(
std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
#else #else
return log_clock::now(); return log_clock::now();
#endif #endif
} }
inline std::tm localtime(const std::time_t &time_tt) inline std::tm localtime(const std::time_t &time_tt)
{ {
#ifdef _WIN32 #ifdef _WIN32
std::tm tm; std::tm tm;
localtime_s(&tm, &time_tt); localtime_s(&tm, &time_tt);
#else #else
std::tm tm; std::tm tm;
localtime_r(&time_tt, &tm); localtime_r(&time_tt, &tm);
#endif #endif
return tm; return tm;
} }
inline std::tm localtime() inline std::tm localtime()
{ {
std::time_t now_t = time(nullptr); std::time_t now_t = time(nullptr);
return localtime(now_t); return localtime(now_t);
} }
inline std::tm gmtime(const std::time_t &time_tt) inline std::tm gmtime(const std::time_t &time_tt)
{ {
#ifdef _WIN32 #ifdef _WIN32
std::tm tm; std::tm tm;
gmtime_s(&tm, &time_tt); gmtime_s(&tm, &time_tt);
#else #else
std::tm tm; std::tm tm;
gmtime_r(&time_tt, &tm); gmtime_r(&time_tt, &tm);
#endif #endif
return tm; return tm;
} }
inline std::tm gmtime() inline std::tm gmtime()
{ {
std::time_t now_t = time(nullptr); std::time_t now_t = time(nullptr);
return gmtime(now_t); return gmtime(now_t);
} }
inline bool operator==(const std::tm& tm1, const std::tm& tm2) inline bool operator==(const std::tm& tm1, const std::tm& tm2)
{ {
return (tm1.tm_sec == tm2.tm_sec && return (tm1.tm_sec == tm2.tm_sec &&
tm1.tm_min == tm2.tm_min && tm1.tm_min == tm2.tm_min &&
tm1.tm_hour == tm2.tm_hour && tm1.tm_hour == tm2.tm_hour &&
tm1.tm_mday == tm2.tm_mday && tm1.tm_mday == tm2.tm_mday &&
tm1.tm_mon == tm2.tm_mon && tm1.tm_mon == tm2.tm_mon &&
tm1.tm_year == tm2.tm_year && tm1.tm_year == tm2.tm_year &&
tm1.tm_isdst == tm2.tm_isdst); tm1.tm_isdst == tm2.tm_isdst);
} }
inline bool operator!=(const std::tm& tm1, const std::tm& tm2) inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
{ {
return !(tm1 == tm2); return !(tm1 == tm2);
} }
// eol definition // eol definition
#if !defined (SPDLOG_EOL) #if !defined (SPDLOG_EOL)
#ifdef _WIN32 #ifdef _WIN32
#define SPDLOG_EOL "\r\n" #define SPDLOG_EOL "\r\n"
#else #else
#define SPDLOG_EOL "\n" #define SPDLOG_EOL "\n"
#endif #endif
#endif #endif
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1; SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
inline void prevent_child_fd(FILE *f) inline void prevent_child_fd(FILE *f)
{ {
#ifdef _WIN32 #ifdef _WIN32
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
throw spdlog_ex("SetHandleInformation failed", errno); throw spdlog_ex("SetHandleInformation failed", errno);
#else #else
auto fd = fileno(f); auto fd = fileno(f);
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
#endif #endif
} }
//fopen_s on non windows for writing //fopen_s on non windows for writing
inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode) inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#else #else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif #endif
#else //unix #else //unix
*fp = fopen((filename.c_str()), mode.c_str()); *fp = fopen((filename.c_str()), mode.c_str());
#endif #endif
#ifdef SPDLOG_PREVENT_CHILD_FD #ifdef SPDLOG_PREVENT_CHILD_FD
if (*fp != nullptr) if (*fp != nullptr)
prevent_child_fd(*fp); prevent_child_fd(*fp);
#endif #endif
return *fp == nullptr; return *fp == nullptr;
} }
inline int remove(const filename_t &filename) inline int remove(const filename_t &filename)
{ {
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wremove(filename.c_str()); return _wremove(filename.c_str());
#else #else
return std::remove(filename.c_str()); return std::remove(filename.c_str());
#endif #endif
} }
inline int rename(const filename_t& filename1, const filename_t& filename2) inline int rename(const filename_t& filename1, const filename_t& filename2)
{ {
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wrename(filename1.c_str(), filename2.c_str()); return _wrename(filename1.c_str(), filename2.c_str());
#else #else
return std::rename(filename1.c_str(), filename2.c_str()); return std::rename(filename1.c_str(), filename2.c_str());
#endif #endif
} }
//Return if file exists //Return if file exists
inline bool file_exists(const filename_t& filename) inline bool file_exists(const filename_t& filename)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
auto attribs = GetFileAttributesW(filename.c_str()); auto attribs = GetFileAttributesW(filename.c_str());
#else #else
auto attribs = GetFileAttributesA(filename.c_str()); auto attribs = GetFileAttributesA(filename.c_str());
#endif #endif
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#else //common linux/unix all have the stat system call #else //common linux/unix all have the stat system call
struct stat buffer; struct stat buffer;
return (stat(filename.c_str(), &buffer) == 0); return (stat(filename.c_str(), &buffer) == 0);
#endif #endif
} }
//Return file size according to open FILE* object //Return file size according to open FILE* object
inline size_t filesize(FILE *f) inline size_t filesize(FILE *f)
{ {
if (f == nullptr) if (f == nullptr)
throw spdlog_ex("Failed getting file size. fd is null"); throw spdlog_ex("Failed getting file size. fd is null");
#if defined ( _WIN32) && !defined(__CYGWIN__) #if defined ( _WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f); int fd = _fileno(f);
#if _WIN64 //64 bits #if _WIN64 //64 bits
struct _stat64 st; struct _stat64 st;
if (_fstat64(fd, &st) == 0) if (_fstat64(fd, &st) == 0)
return st.st_size; return st.st_size;
#else //windows 32 bits #else //windows 32 bits
long ret = _filelength(fd); long ret = _filelength(fd);
if (ret >= 0) if (ret >= 0)
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
#endif #endif
#else // unix #else // unix
int fd = fileno(f); int fd = fileno(f);
//64 bits(but not in osx or cygwin, where fstat64 is deprecated) //64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__) #if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
struct stat64 st; struct stat64 st;
if (fstat64(fd, &st) == 0) if (fstat64(fd, &st) == 0)
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
#else // unix 32 bits or cygwin #else // unix 32 bits or cygwin
struct stat st; struct stat st;
if (fstat(fd, &st) == 0) if (fstat(fd, &st) == 0)
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
#endif #endif
#endif #endif
throw spdlog_ex("Failed getting file size from fd", errno); throw spdlog_ex("Failed getting file size from fd", errno);
} }
//Return utc offset in minutes or throw spdlog_ex on failure //Return utc offset in minutes or throw spdlog_ex on failure
inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
{ {
#ifdef _WIN32 #ifdef _WIN32
#if _WIN32_WINNT < _WIN32_WINNT_WS08 #if _WIN32_WINNT < _WIN32_WINNT_WS08
TIME_ZONE_INFORMATION tzinfo; TIME_ZONE_INFORMATION tzinfo;
auto rv = GetTimeZoneInformation(&tzinfo); auto rv = GetTimeZoneInformation(&tzinfo);
#else #else
DYNAMIC_TIME_ZONE_INFORMATION tzinfo; DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
auto rv = GetDynamicTimeZoneInformation(&tzinfo); auto rv = GetDynamicTimeZoneInformation(&tzinfo);
#endif #endif
if (rv == TIME_ZONE_ID_INVALID) if (rv == TIME_ZONE_ID_INVALID)
throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
int offset = -tzinfo.Bias; int offset = -tzinfo.Bias;
if (tm.tm_isdst) if (tm.tm_isdst)
offset -= tzinfo.DaylightBias; offset -= tzinfo.DaylightBias;
else else
offset -= tzinfo.StandardBias; offset -= tzinfo.StandardBias;
return offset; return offset;
#else #else
#if defined(sun) || defined(__sun) #if defined(sun) || defined(__sun)
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper struct helper
{ {
static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime()) static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime())
{ {
int local_year = localtm.tm_year + (1900 - 1); int local_year = localtm.tm_year + (1900 - 1);
int gmt_year = gmtm.tm_year + (1900 - 1); int gmt_year = gmtm.tm_year + (1900 - 1);
long int days = ( long int days = (
// difference in day of year // difference in day of year
localtm.tm_yday - gmtm.tm_yday localtm.tm_yday - gmtm.tm_yday
// + intervening leap days // + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2)) + ((local_year >> 2) - (gmt_year >> 2))
- (local_year / 100 - gmt_year / 100) - (local_year / 100 - gmt_year / 100)
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) + ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
// + difference in years * 365 */ // + difference in years * 365 */
+ (long int)(local_year - gmt_year) * 365 + (long int)(local_year - gmt_year) * 365
); );
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
return secs; return secs;
} }
}; };
long int offset_seconds = helper::calculate_gmt_offset(tm); long int offset_seconds = helper::calculate_gmt_offset(tm);
#else #else
long int offset_seconds = tm.tm_gmtoff; long int offset_seconds = tm.tm_gmtoff;
#endif #endif
return static_cast<int>(offset_seconds / 60); return static_cast<int>(offset_seconds / 60);
#endif #endif
} }
//Return current thread id as size_t //Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013) //It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
inline size_t _thread_id() inline size_t _thread_id()
{ {
#ifdef _WIN32 #ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId()); return static_cast<size_t>(::GetCurrentThreadId());
#elif __linux__ #elif __linux__
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) # if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
# define SYS_gettid __NR_gettid # define SYS_gettid __NR_gettid
# endif # endif
return static_cast<size_t>(syscall(SYS_gettid)); return static_cast<size_t>(syscall(SYS_gettid));
#elif __FreeBSD__ #elif __FreeBSD__
long tid; long tid;
thr_self(&tid); thr_self(&tid);
return static_cast<size_t>(tid); return static_cast<size_t>(tid);
#elif __APPLE__ #elif __APPLE__
uint64_t tid; uint64_t tid;
pthread_threadid_np(nullptr, &tid); pthread_threadid_np(nullptr, &tid);
return static_cast<size_t>(tid); return static_cast<size_t>(tid);
#else //Default to standard C++11 (other Unix) #else //Default to standard C++11 (other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id())); return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif #endif
} }
//Return current thread id as size_t (from thread local storage) //Return current thread id as size_t (from thread local storage)
inline size_t thread_id() inline size_t thread_id()
{ {
#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local) #if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local)
return _thread_id(); return _thread_id();
#else #else
static thread_local const size_t tid = _thread_id(); static thread_local const size_t tid = _thread_id();
return tid; return tid;
#endif #endif
} }
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#define SPDLOG_FILENAME_T(s) L ## s #define SPDLOG_FILENAME_T(s) L ## s
inline std::string filename_to_str(const filename_t& filename) inline std::string filename_to_str(const filename_t& filename)
{ {
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c; std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
return c.to_bytes(filename); return c.to_bytes(filename);
} }
#else #else
#define SPDLOG_FILENAME_T(s) s #define SPDLOG_FILENAME_T(s) s
inline std::string filename_to_str(const filename_t& filename) inline std::string filename_to_str(const filename_t& filename)
{ {
return filename; return filename;
} }
#endif #endif
inline std::string errno_to_string(char[256], char* res) inline std::string errno_to_string(char[256], char* res)
{ {
return std::string(res); return std::string(res);
} }
inline std::string errno_to_string(char buf[256], int res) inline std::string errno_to_string(char buf[256], int res)
{ {
if (res == 0) if (res == 0)
{ {
return std::string(buf); return std::string(buf);
} }
else else
{ {
return "Unknown error"; return "Unknown error";
} }
} }
// Return errno string (thread safe) // Return errno string (thread safe)
inline std::string errno_str(int err_num) inline std::string errno_str(int err_num)
{ {
char buf[256]; char buf[256];
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf); SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
#ifdef _WIN32 #ifdef _WIN32
if (strerror_s(buf, buf_size, err_num) == 0) if (strerror_s(buf, buf_size, err_num) == 0)
return std::string(buf); return std::string(buf);
else else
return "Unknown error"; return "Unknown error";
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \ #elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version ((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0) if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf); return std::string(buf);
else else
return "Unknown error"; return "Unknown error";
#else // gnu version (might not use the given buf, so its retval pointer must be used) #else // gnu version (might not use the given buf, so its retval pointer must be used)
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
return errno_to_string(buf, err); // use overloading to select correct stringify function return errno_to_string(buf, err); // use overloading to select correct stringify function
#endif #endif
} }
inline int pid() inline int pid()
{ {
#ifdef _WIN32 #ifdef _WIN32
return ::_getpid(); return ::_getpid();
#else #else
return static_cast<int>(::getpid()); return static_cast<int>(::getpid());
#endif #endif
} }
// Detrmine if the terminal supports colors // Detrmine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/ // Source: https://github.com/agauniyal/rang/
inline bool is_color_terminal() inline bool is_color_terminal()
{ {
#ifdef _WIN32 #ifdef _WIN32
return true; return true;
#else #else
static constexpr const char* Terms[] = static constexpr const char* Terms[] =
{ {
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
"linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm" "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
}; };
const char *env_p = std::getenv("TERM"); const char *env_p = std::getenv("TERM");
if (env_p == nullptr) if (env_p == nullptr)
{ {
return false; return false;
} }
static const bool result = std::any_of( static const bool result = std::any_of(
std::begin(Terms), std::end(Terms), [&](const char* term) std::begin(Terms), std::end(Terms), [&](const char* term)
{ {
return std::strstr(env_p, term) != nullptr; return std::strstr(env_p, term) != nullptr;
}); });
return result; return result;
#endif #endif
} }
// Detrmine if the terminal attached // Detrmine if the terminal attached
// Source: https://github.com/agauniyal/rang/ // Source: https://github.com/agauniyal/rang/
inline bool in_terminal(FILE* file) inline bool in_terminal(FILE* file)
{ {
#ifdef _WIN32 #ifdef _WIN32
return _isatty(_fileno(file)) ? true : false; return _isatty(_fileno(file)) ? true : false;
#else #else
return isatty(fileno(file)) ? true : false; return isatty(fileno(file)) ? true : false;
#endif #endif
} }
} //os } //os
} //details } //details
} //spdlog } //spdlog
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -13,57 +13,65 @@ ...@@ -13,57 +13,65 @@
#include "format.h" #include "format.h"
#include <ostream> #include <ostream>
namespace fmt { namespace fmt
{
namespace internal { namespace internal
{
template <class Char> template <class Char>
class FormatBuf : public std::basic_streambuf<Char> { class FormatBuf : public std::basic_streambuf<Char>
private: {
typedef typename std::basic_streambuf<Char>::int_type int_type; private:
typedef typename std::basic_streambuf<Char>::traits_type traits_type; typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
Buffer<Char> &buffer_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {} public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
protected:
// The put-area is actually always empty. This makes the implementation protected:
// simpler and has the advantage that the streambuf and the buffer are always // The put-area is actually always empty. This makes the implementation
// in sync and sputc never writes into uninitialized memory. The obvious // simpler and has the advantage that the streambuf and the buffer are always
// disadvantage is that each call to sputc always results in a (virtual) call // in sync and sputc never writes into uninitialized memory. The obvious
// to overflow. There is no disadvantage here for sputn since this always // disadvantage is that each call to sputc always results in a (virtual) call
// results in a call to xsputn. // to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof())) int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE
buffer_.push_back(static_cast<Char>(ch)); {
return ch; if (!traits_type::eq_int_type(ch, traits_type::eof()))
} buffer_.push_back(static_cast<Char>(ch));
return ch;
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { }
buffer_.append(s, s + count);
return count; std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
} {
buffer_.append(s, s + count);
return count;
}
}; };
Yes &convert(std::ostream &); Yes &convert(std::ostream &);
struct DummyStream : std::ostream { struct DummyStream : std::ostream
DummyStream(); // Suppress a bogus warning in MSVC. {
// Hide all operator<< overloads from std::ostream. DummyStream(); // Suppress a bogus warning in MSVC.
void operator<<(Null<>); // Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
}; };
No &operator<<(std::ostream &, int); No &operator<<(std::ostream &, int);
template<typename T> template<typename T>
struct ConvertToIntImpl<T, true> { struct ConvertToIntImpl<T, true>
// Convert to int only if T doesn't have an overloaded operator<<. {
enum { // Convert to int only if T doesn't have an overloaded operator<<.
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No) enum
}; {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
}; };
// Write the content of w to os. // Write the content of w to os.
...@@ -73,16 +81,17 @@ FMT_API void write(std::ostream &os, Writer &w); ...@@ -73,16 +81,17 @@ FMT_API void write(std::ostream &os, Writer &w);
// Formats a value. // Formats a value.
template <typename Char, typename ArgFormatter_, typename T> template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f, void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
const Char *&format_str, const T &value) { const Char *&format_str, const T &value)
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer); internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
output << value; output << value;
BasicStringRef<Char> str(&buffer[0], buffer.size()); BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg; typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str)); format_str = f.format(format_str, MakeArg(str));
} }
/** /**
......
...@@ -64,112 +64,134 @@ ...@@ -64,112 +64,134 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
namespace fmt { namespace fmt
{
// An error code. // An error code.
class ErrorCode { class ErrorCode
private: {
int value_; private:
int value_;
public: public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {} explicit ErrorCode(int value = 0) FMT_NOEXCEPT :
value_(value) {}
int get() const FMT_NOEXCEPT { return value_; } int get() const FMT_NOEXCEPT
{
return value_;
}
}; };
// A buffered file. // A buffered file.
class BufferedFile { class BufferedFile
private: {
FILE *file_; private:
FILE *file_;
friend class File; friend class File;
explicit BufferedFile(FILE *f) : file_(f) {} explicit BufferedFile(FILE *f) : file_(f) {}
public: public:
// Constructs a BufferedFile object which doesn't represent any file. // Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {} BufferedFile() FMT_NOEXCEPT :
file_(FMT_NULL) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~BufferedFile() FMT_NOEXCEPT; FMT_API ~BufferedFile() FMT_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES #if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue // Emulate a move constructor and a move assignment operator if rvalue
// references are not supported. // references are not supported.
private: private:
// A proxy object to emulate a move constructor. // A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly. // It is private to make it impossible call operator Proxy directly.
struct Proxy { struct Proxy
FILE *file; {
}; FILE *file;
};
public: public:
// A "move constructor" for moving from a temporary. // A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {} BufferedFile(Proxy p) FMT_NOEXCEPT :
file_(p.file) {}
// A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) { // A "move constructor" for moving from an lvalue.
f.file_ = FMT_NULL; BufferedFile(BufferedFile &f) FMT_NOEXCEPT :
} file_(f.file_)
{
// A "move assignment operator" for moving from a temporary. f.file_ = FMT_NULL;
BufferedFile &operator=(Proxy p) { }
close();
file_ = p.file; // A "move assignment operator" for moving from a temporary.
return *this; BufferedFile &operator=(Proxy p)
} {
close();
// A "move assignment operator" for moving from an lvalue. file_ = p.file;
BufferedFile &operator=(BufferedFile &other) { return *this;
close(); }
file_ = other.file_;
other.file_ = FMT_NULL; // A "move assignment operator" for moving from an lvalue.
return *this; BufferedFile &operator=(BufferedFile &other)
} {
close();
// Returns a proxy object for moving from a temporary: file_ = other.file_;
// BufferedFile file = BufferedFile(...); other.file_ = FMT_NULL;
operator Proxy() FMT_NOEXCEPT { return *this;
Proxy p = {file_}; }
file_ = FMT_NULL;
return p; // Returns a proxy object for moving from a temporary:
} // BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {file_};
file_ = FMT_NULL;
return p;
}
#else #else
private: private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
public: public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) { BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
other.file_ = FMT_NULL; file_(other.file_)
} {
other.file_ = FMT_NULL;
BufferedFile& operator=(BufferedFile &&other) { }
close();
file_ = other.file_; BufferedFile& operator=(BufferedFile &&other)
other.file_ = FMT_NULL; {
return *this; close();
} file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
#endif #endif
// Opens a file. // Opens a file.
FMT_API BufferedFile(CStringRef filename, CStringRef mode); FMT_API BufferedFile(CStringRef filename, CStringRef mode);
// Closes the file. // Closes the file.
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; } FILE *get() const FMT_NOEXCEPT
{
return file_;
}
// We place parentheses around fileno to workaround a bug in some versions // We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
FMT_API int (fileno)() const; FMT_API int (fileno)() const;
void print(CStringRef format_str, const ArgList &args) { void print(CStringRef format_str, const ArgList &args)
fmt::print(file_, format_str, args); {
} fmt::print(file_, format_str, args);
FMT_VARIADIC(void, print, CStringRef) }
FMT_VARIADIC(void, print, CStringRef)
}; };
// A file. Closed file is represented by a File object with descriptor -1. // A file. Closed file is represented by a File object with descriptor -1.
...@@ -178,125 +200,141 @@ public: ...@@ -178,125 +200,141 @@ public:
// closing the file multiple times will cause a crash on Windows rather // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class File { class File
private: {
int fd_; // File descriptor. private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor. // Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {} explicit File(int fd) : fd_(fd) {}
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum { enum
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. {
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
}; RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a File object which doesn't represent any file. // Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT : fd_(-1) {} File() FMT_NOEXCEPT :
fd_(-1) {}
// Opens a file and constructs a File object representing this file. // Opens a file and constructs a File object representing this file.
FMT_API File(CStringRef path, int oflag); FMT_API File(CStringRef path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES #if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue // Emulate a move constructor and a move assignment operator if rvalue
// references are not supported. // references are not supported.
private: private:
// A proxy object to emulate a move constructor. // A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly. // It is private to make it impossible call operator Proxy directly.
struct Proxy { struct Proxy
int fd; {
}; int fd;
};
public:
// A "move constructor" for moving from a temporary. public:
File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} // A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT :
// A "move constructor" for moving from an lvalue. fd_(p.fd) {}
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1; // A "move constructor" for moving from an lvalue.
} File(File &other) FMT_NOEXCEPT :
fd_(other.fd_)
// A "move assignment operator" for moving from a temporary. {
File &operator=(Proxy p) { other.fd_ = -1;
close(); }
fd_ = p.fd;
return *this; // A "move assignment operator" for moving from a temporary.
} File &operator=(Proxy p)
{
// A "move assignment operator" for moving from an lvalue. close();
File &operator=(File &other) { fd_ = p.fd;
close(); return *this;
fd_ = other.fd_; }
other.fd_ = -1;
return *this; // A "move assignment operator" for moving from an lvalue.
} File &operator=(File &other)
{
// Returns a proxy object for moving from a temporary: close();
// File file = File(...); fd_ = other.fd_;
operator Proxy() FMT_NOEXCEPT { other.fd_ = -1;
Proxy p = {fd_}; return *this;
fd_ = -1; }
return p;
} // Returns a proxy object for moving from a temporary:
// File file = File(...);
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else #else
private: private:
FMT_DISALLOW_COPY_AND_ASSIGN(File); FMT_DISALLOW_COPY_AND_ASSIGN(File);
public: public:
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) { File(File &&other) FMT_NOEXCEPT :
other.fd_ = -1; fd_(other.fd_)
} {
other.fd_ = -1;
File& operator=(File &&other) { }
close();
fd_ = other.fd_; File& operator=(File &&other)
other.fd_ = -1; {
return *this; close();
} fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif #endif
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~File() FMT_NOEXCEPT; FMT_API ~File() FMT_NOEXCEPT;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const FMT_NOEXCEPT
{
return fd_;
}
// Closes the file. // Closes the file.
FMT_API void close(); FMT_API void close();
// Returns the file size. The size has signed type for consistency with // Returns the file size. The size has signed type for consistency with
// stat::st_size. // stat::st_size.
FMT_API LongLong size() const; FMT_API LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count); FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file. // Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void *buffer, std::size_t count); FMT_API std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
FMT_API static File dup(int fd); FMT_API static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd); FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT; FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(File &read_end, File &write_end); FMT_API static void pipe(File &read_end, File &write_end);
// Creates a BufferedFile object associated with this file and detaches // Creates a BufferedFile object associated with this file and detaches
// this File object from the file. // this File object from the file.
FMT_API BufferedFile fdopen(const char *mode); FMT_API BufferedFile fdopen(const char *mode);
}; };
// Returns the memory page size. // Returns the memory page size.
...@@ -309,58 +347,77 @@ long getpagesize(); ...@@ -309,58 +347,77 @@ long getpagesize();
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
// A "C" numeric locale. // A "C" numeric locale.
class Locale { class Locale
private: {
private:
# ifdef _MSC_VER # ifdef _MSC_VER
typedef _locale_t locale_t; typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC }; enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t) { static locale_t newlocale(int category_mask, const char *locale, locale_t)
return _create_locale(category_mask, locale); {
} return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { static void freelocale(locale_t locale)
_free_locale(locale); {
} _free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) { static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
return _strtod_l(nptr, endptr, locale); {
} return _strtod_l(nptr, endptr, locale);
}
# endif # endif
locale_t locale_; locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
public:
typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
if (!locale_)
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
Type get() const { return locale_; } public:
typedef locale_t Type;
// Converts string to floating-point number and advances str past the end
// of the parsed input. Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL))
double strtod(const char *&str) const { {
char *end = FMT_NULL; if (!locale_)
double result = strtod_l(str, &end, locale_); FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
str = end; }
return result; ~Locale()
} {
freelocale(locale_);
}
Type get() const
{
return locale_;
}
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char *&str) const
{
char *end = FMT_NULL;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
}; };
#endif // FMT_LOCALE #endif // FMT_LOCALE
} // namespace fmt } // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES #if !FMT_USE_RVALUE_REFERENCES
namespace std { namespace std
{
// For compatibility with C++98. // For compatibility with C++98.
inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; } inline fmt::BufferedFile &move(fmt::BufferedFile &f)
inline fmt::File &move(fmt::File &f) { return f; } {
return f;
}
inline fmt::File &move(fmt::File &f)
{
return f;
}
} }
#endif #endif
......
...@@ -15,78 +15,118 @@ ...@@ -15,78 +15,118 @@
#include "ostream.h" #include "ostream.h"
namespace fmt { namespace fmt
namespace internal { {
namespace internal
{
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> template <bool IsSigned>
struct IntChecker { struct IntChecker
template <typename T> {
static bool fits_in_int(T value) { template <typename T>
unsigned max = std::numeric_limits<int>::max(); static bool fits_in_int(T value)
return value <= max; {
} unsigned max = std::numeric_limits<int>::max();
static bool fits_in_int(bool) { return true; } return value <= max;
}
static bool fits_in_int(bool)
{
return true;
}
}; };
template <> template <>
struct IntChecker<true> { struct IntChecker<true>
template <typename T> {
static bool fits_in_int(T value) { template <typename T>
return value >= std::numeric_limits<int>::min() && static bool fits_in_int(T value)
value <= std::numeric_limits<int>::max(); {
} return value >= std::numeric_limits<int>::min() &&
static bool fits_in_int(int) { return true; } value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int)
{
return true;
}
}; };
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> { class PrecisionHandler : public ArgVisitor<PrecisionHandler, int>
public: {
void report_unhandled_arg() { public:
FMT_THROW(FormatError("precision is not integer")); void report_unhandled_arg()
} {
FMT_THROW(FormatError("precision is not integer"));
template <typename T> }
int visit_any_int(T value) {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) template <typename T>
FMT_THROW(FormatError("number is too big")); int visit_any_int(T value)
return static_cast<int>(value); {
} if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
}; };
// IsZeroInt::visit(arg) returns true iff arg is a zero integer. // IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> { class IsZeroInt : public ArgVisitor<IsZeroInt, bool>
public: {
template <typename T> public:
bool visit_any_int(T value) { return value == 0; } template <typename T>
bool visit_any_int(T value)
{
return value == 0;
}
}; };
// returns the default type for format specific "%s" // returns the default type for format specific "%s"
class DefaultType : public ArgVisitor<DefaultType, char> { class DefaultType : public ArgVisitor<DefaultType, char>
public: {
char visit_char(int) { return 'c'; } public:
char visit_char(int)
{
return 'c';
}
char visit_bool(bool) { return 's'; } char visit_bool(bool)
{
return 's';
}
char visit_pointer(const void *) { return 'p'; } char visit_pointer(const void *)
{
return 'p';
}
template <typename T> template <typename T>
char visit_any_int(T) { return 'd'; } char visit_any_int(T)
{
return 'd';
}
template <typename T> template <typename T>
char visit_any_double(T) { return 'g'; } char visit_any_double(T)
{
return 'g';
}
char visit_unhandled_arg() { return 's'; } char visit_unhandled_arg()
{
return 's';
}
}; };
template <typename T, typename U> template <typename T, typename U>
struct is_same { struct is_same
enum { value = 0 }; {
enum { value = 0 };
}; };
template <typename T> template <typename T>
struct is_same<T, T> { struct is_same<T, T>
enum { value = 1 }; {
enum { value = 1 };
}; };
// An argument visitor that converts an integer argument to T for printf, // An argument visitor that converts an integer argument to T for printf,
...@@ -94,108 +134,128 @@ struct is_same<T, T> { ...@@ -94,108 +134,128 @@ struct is_same<T, T> {
// corresponding signed or unsigned type depending on the type specifier: // corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned) // 'd' and 'i' - signed, other - unsigned)
template <typename T = void> template <typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> { class ArgConverter : public ArgVisitor<ArgConverter<T>, void>
private: {
internal::Arg &arg_; private:
wchar_t type_; internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(internal::Arg &arg, wchar_t type) public:
: arg_(arg), type_(type) {} ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
void visit_bool(bool value) {
if (type_ != 's') void visit_bool(bool value)
visit_any_int(value); {
} if (type_ != 's')
visit_any_int(value);
void visit_char(char value) { }
if (type_ != 's')
visit_any_int(value); void visit_char(char value)
} {
if (type_ != 's')
template <typename U> visit_any_int(value);
void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
if (type_ == 's') {
is_signed = std::numeric_limits<U>::is_signed;
} }
using internal::Arg; template <typename U>
typedef typename internal::Conditional< void visit_any_int(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i';
if (type_ == 's')
{
is_signed = std::numeric_limits<U>::is_signed;
}
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType; is_same<T, void>::value, U, T>::type TargetType;
if (sizeof(TargetType) <= sizeof(int)) { if (sizeof(TargetType) <= sizeof(int))
// Extra casts are used to silence warnings. {
if (is_signed) { // Extra casts are used to silence warnings.
arg_.type = Arg::INT; if (is_signed)
arg_.int_value = static_cast<int>(static_cast<TargetType>(value)); {
} else { arg_.type = Arg::INT;
arg_.type = Arg::UINT; arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned; }
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value)); else
} {
} else { arg_.type = Arg::UINT;
if (is_signed) { typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
arg_.type = Arg::LONG_LONG; arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
// glibc's printf doesn't sign extend arguments of smaller types: }
// std::printf("%lld", -42); // prints "4294967254" }
// but we don't have to do the same because it's a UB. else
arg_.long_long_value = static_cast<LongLong>(value); {
} else { if (is_signed)
arg_.type = Arg::ULONG_LONG; {
arg_.ulong_long_value = arg_.type = Arg::LONG_LONG;
static_cast<typename internal::MakeUnsigned<U>::Type>(value); // glibc's printf doesn't sign extend arguments of smaller types:
} // std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_.long_long_value = static_cast<LongLong>(value);
}
else
{
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value =
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
}
}
} }
}
}; };
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void> { class CharConverter : public ArgVisitor<CharConverter, void>
private: {
internal::Arg &arg_; private:
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public: public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {} explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
template <typename T> template <typename T>
void visit_any_int(T value) { void visit_any_int(T value)
arg_.type = internal::Arg::CHAR; {
arg_.int_value = static_cast<char>(value); arg_.type = internal::Arg::CHAR;
} arg_.int_value = static_cast<char>(value);
}
}; };
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> { class WidthHandler : public ArgVisitor<WidthHandler, unsigned>
private: {
FormatSpec &spec_; private:
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() {
FMT_THROW(FormatError("width is not integer")); void report_unhandled_arg()
} {
FMT_THROW(FormatError("width is not integer"));
template <typename T> }
unsigned visit_any_int(T value) {
typedef typename internal::IntTraits<T>::MainType UnsignedType; template <typename T>
UnsignedType width = static_cast<UnsignedType>(value); unsigned visit_any_int(T value)
if (internal::is_negative(value)) { {
spec_.align_ = ALIGN_LEFT; typedef typename internal::IntTraits<T>::MainType UnsignedType;
width = 0 - width; UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value))
{
spec_.align_ = ALIGN_LEFT;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
} }
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
}; };
} // namespace internal } // namespace internal
...@@ -218,314 +278,359 @@ class WidthHandler : public ArgVisitor<WidthHandler, unsigned> { ...@@ -218,314 +278,359 @@ class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
*/ */
template <typename Impl, typename Char, typename Spec> template <typename Impl, typename Char, typename Spec>
class BasicPrintfArgFormatter : class BasicPrintfArgFormatter :
public internal::ArgFormatterBase<Impl, Char, Spec> { public internal::ArgFormatterBase<Impl, Char, Spec>
private: {
void write_null_pointer() { private:
this->spec().type_ = 0; void write_null_pointer()
this->write("(nil)"); {
} this->spec().type_ = 0;
this->write("(nil)");
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base; }
public: typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
/**
\rst public:
Constructs an argument formatter object. /**
*writer* is a reference to the output writer and *spec* contains format \rst
specifier information for standard argument types. Constructs an argument formatter object.
\endrst *writer* is a reference to the output writer and *spec* contains format
*/ specifier information for standard argument types.
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s) \endrst
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {} */
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
/** Formats an argument of type ``bool``. */ : internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
void visit_bool(bool value) {
Spec &fmt_spec = this->spec(); /** Formats an argument of type ``bool``. */
if (fmt_spec.type_ != 's') void visit_bool(bool value)
return this->visit_any_int(value); {
fmt_spec.type_ = 0; Spec &fmt_spec = this->spec();
this->write(value); if (fmt_spec.type_ != 's')
} return this->visit_any_int(value);
fmt_spec.type_ = 0;
/** Formats a character. */ this->write(value);
void visit_char(int value) { }
const Spec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer(); /** Formats a character. */
if (fmt_spec.type_ && fmt_spec.type_ != 'c') void visit_char(int value)
w.write_int(value, fmt_spec); {
typedef typename BasicWriter<Char>::CharPtr CharPtr; const Spec &fmt_spec = this->spec();
CharPtr out = CharPtr(); BasicWriter<Char> &w = this->writer();
if (fmt_spec.width_ > 1) { if (fmt_spec.type_ && fmt_spec.type_ != 'c')
Char fill = ' '; w.write_int(value, fmt_spec);
out = w.grow_buffer(fmt_spec.width_); typedef typename BasicWriter<Char>::CharPtr CharPtr;
if (fmt_spec.align_ != ALIGN_LEFT) { CharPtr out = CharPtr();
std::fill_n(out, fmt_spec.width_ - 1, fill); if (fmt_spec.width_ > 1)
out += fmt_spec.width_ - 1; {
} else { Char fill = ' ';
std::fill_n(out + 1, fmt_spec.width_ - 1, fill); out = w.grow_buffer(fmt_spec.width_);
} if (fmt_spec.align_ != ALIGN_LEFT)
} else { {
out = w.grow_buffer(1); std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1;
}
else
{
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
}
}
else
{
out = w.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
/** Formats a null-terminated C string. */
void visit_cstring(const char *value)
{
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
/** Formats a pointer. */
void visit_pointer(const void *value)
{
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c)
{
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
} }
*out = static_cast<Char>(value);
}
/** Formats a null-terminated C string. */
void visit_cstring(const char *value) {
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
/** Formats a pointer. */
void visit_pointer(const void *value) {
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c) {
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
}; };
/** The default printf argument formatter. */ /** The default printf argument formatter. */
template <typename Char> template <typename Char>
class PrintfArgFormatter : class PrintfArgFormatter :
public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec> { public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>
public: {
/** Constructs an argument formatter object. */ public:
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s) /** Constructs an argument formatter object. */
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {} PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {}
}; };
/** This template formats data and writes the output to a writer. */ /** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> > template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase { class PrintfFormatter : private internal::FormatterBase
private: {
BasicWriter<Char> &writer_; private:
BasicWriter<Char> &writer_;
void parse_flags(FormatSpec &spec, const Char *&s);
void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument. // Returns the argument with specified index or, if arg_index is equal
internal::Arg get_arg( // to the maximum unsigned value, the next argument.
const Char *s, internal::Arg get_arg(
unsigned arg_index = (std::numeric_limits<unsigned>::max)()); const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec); // Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
/** public:
\rst /**
Constructs a ``PrintfFormatter`` object. References to the arguments and \rst
the writer are stored in the formatter object so make sure they have Constructs a ``PrintfFormatter`` object. References to the arguments and
appropriate lifetimes. the writer are stored in the formatter object so make sure they have
\endrst appropriate lifetimes.
*/ \endrst
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w) */
: FormatterBase(al), writer_(w) {} explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w) {}
/** Formats stored arguments and writes the output to the writer. */
void format(BasicCStringRef<Char> format_str); /** Formats stored arguments and writes the output to the writer. */
void format(BasicCStringRef<Char> format_str);
}; };
template <typename Char, typename AF> template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) { void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s)
for (;;) { {
switch (*s++) { for (;;)
case '-': {
spec.align_ = ALIGN_LEFT; switch (*s++)
break; {
case '+': case '-':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG; spec.align_ = ALIGN_LEFT;
break; break;
case '0': case '+':
spec.fill_ = '0'; spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break; break;
case ' ': case '0':
spec.flags_ |= SIGN_FLAG; spec.fill_ = '0';
break; break;
case '#': case ' ':
spec.flags_ |= HASH_FLAG; spec.flags_ |= SIGN_FLAG;
break; break;
default: case '#':
--s; spec.flags_ |= HASH_FLAG;
return; break;
default:
--s;
return;
}
} }
}
} }
template <typename Char, typename AF> template <typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s, internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index) { unsigned arg_index)
(void)s; {
const char *error = FMT_NULL; (void)s;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ? const char *error = FMT_NULL;
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
if (error) next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
FMT_THROW(FormatError(!*s ? "invalid format string" : error)); if (error)
return arg; FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
} }
template <typename Char, typename AF> template <typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header( unsigned PrintfFormatter<Char, AF>::parse_header(
const Char *&s, FormatSpec &spec) { const Char *&s, FormatSpec &spec)
unsigned arg_index = std::numeric_limits<unsigned>::max(); {
Char c = *s; unsigned arg_index = std::numeric_limits<unsigned>::max();
if (c >= '0' && c <= '9') { Char c = *s;
// Parse an argument index (if followed by '$') or a width possibly if (c >= '0' && c <= '9')
// preceded with '0' flag(s). {
unsigned value = internal::parse_nonnegative_int(s); // Parse an argument index (if followed by '$') or a width possibly
if (*s == '$') { // value is an argument index // preceded with '0' flag(s).
++s; unsigned value = internal::parse_nonnegative_int(s);
arg_index = value; if (*s == '$') // value is an argument index
} else { {
if (c == '0') ++s;
spec.fill_ = '0'; arg_index = value;
if (value != 0) { }
// Nonzero value means that we parsed width and don't need to else
// parse it or flags again, so return now. {
spec.width_ = value; if (c == '0')
return arg_index; spec.fill_ = '0';
} if (value != 0)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
} }
} parse_flags(spec, s);
parse_flags(spec, s); // Parse width.
// Parse width. if (*s >= '0' && *s <= '9')
if (*s >= '0' && *s <= '9') { {
spec.width_ = internal::parse_nonnegative_int(s); spec.width_ = internal::parse_nonnegative_int(s);
} else if (*s == '*') {
++s;
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template <typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) {
const Char *start = format_str.c_str();
const Char *s = start;
while (*s) {
Char c = *s++;
if (c != '%') continue;
if (*s == c) {
write(writer_, start, s);
start = ++s;
continue;
} }
write(writer_, start, s - 1); else if (*s == '*')
{
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.') {
++s;
if ('0' <= *s && *s <= '9') {
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
} else if (*s == '*') {
++s; ++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s)); spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
} else {
spec.precision_ = 0;
}
}
using internal::Arg;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0') {
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::ArgConverter;
switch (*s++) {
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (spec.type_ == 's') {
// set the format type to the default if 's' is specified
spec.type_ = internal::DefaultType().visit(arg);
} }
return arg_index;
}
if (arg.type <= Arg::LAST_INTEGER_TYPE) { template <typename Char, typename AF>
// Normalize type. void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
switch (spec.type_) { {
case 'i': case 'u': const Char *start = format_str.c_str();
spec.type_ = 'd'; const Char *s = start;
break; while (*s)
case 'c': {
// TODO: handle wchar_t Char c = *s++;
internal::CharConverter(arg).visit(arg); if (c != '%') continue;
break; if (*s == c)
} {
write(writer_, start, s);
start = ++s;
continue;
}
write(writer_, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.')
{
++s;
if ('0' <= *s && *s <= '9')
{
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
}
else if (*s == '*')
{
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
}
else
{
spec.precision_ = 0;
}
}
using internal::Arg;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0')
{
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::ArgConverter;
switch (*s++)
{
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (spec.type_ == 's')
{
// set the format type to the default if 's' is specified
spec.type_ = internal::DefaultType().visit(arg);
}
if (arg.type <= Arg::LAST_INTEGER_TYPE)
{
// Normalize type.
switch (spec.type_)
{
case 'i':
case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
internal::CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
AF(writer_, spec).visit(arg);
} }
write(writer_, start, s);
start = s;
// Format argument.
AF(writer_, spec).visit(arg);
}
write(writer_, start, s);
} }
inline void printf(Writer &w, CStringRef format, ArgList args) { inline void printf(Writer &w, CStringRef format, ArgList args)
PrintfFormatter<char>(args, w).format(format); {
PrintfFormatter<char>(args, w).format(format);
} }
FMT_VARIADIC(void, printf, Writer &, CStringRef) FMT_VARIADIC(void, printf, Writer &, CStringRef)
inline void printf(WWriter &w, WCStringRef format, ArgList args) { inline void printf(WWriter &w, WCStringRef format, ArgList args)
PrintfFormatter<wchar_t>(args, w).format(format); {
PrintfFormatter<wchar_t>(args, w).format(format);
} }
FMT_VARIADIC(void, printf, WWriter &, WCStringRef) FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
...@@ -538,17 +643,19 @@ FMT_VARIADIC(void, printf, WWriter &, WCStringRef) ...@@ -538,17 +643,19 @@ FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\endrst \endrst
*/ */
inline std::string sprintf(CStringRef format, ArgList args) { inline std::string sprintf(CStringRef format, ArgList args)
MemoryWriter w; {
printf(w, format, args); MemoryWriter w;
return w.str(); printf(w, format, args);
return w.str();
} }
FMT_VARIADIC(std::string, sprintf, CStringRef) FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args) { inline std::wstring sprintf(WCStringRef format, ArgList args)
WMemoryWriter w; {
printf(w, format, args); WMemoryWriter w;
return w.str(); printf(w, format, args);
return w.str();
} }
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
...@@ -573,8 +680,9 @@ FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) ...@@ -573,8 +680,9 @@ FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
inline int printf(CStringRef format, ArgList args) { inline int printf(CStringRef format, ArgList args)
return fprintf(stdout, format, args); {
return fprintf(stdout, format, args);
} }
FMT_VARIADIC(int, printf, CStringRef) FMT_VARIADIC(int, printf, CStringRef)
...@@ -587,11 +695,12 @@ FMT_VARIADIC(int, printf, CStringRef) ...@@ -587,11 +695,12 @@ FMT_VARIADIC(int, printf, CStringRef)
fprintf(cerr, "Don't %s!", "panic"); fprintf(cerr, "Don't %s!", "panic");
\endrst \endrst
*/ */
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) { inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
MemoryWriter w; {
printf(w, format_str, args); MemoryWriter w;
internal::write(os, w); printf(w, format_str, args);
return static_cast<int>(w.size()); internal::write(os, w);
return static_cast<int>(w.size());
} }
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt } // namespace fmt
......
...@@ -19,120 +19,160 @@ ...@@ -19,120 +19,160 @@
# pragma warning(disable: 4996) // "deprecated" functions # pragma warning(disable: 4996) // "deprecated" functions
#endif #endif
namespace fmt { namespace fmt
{
template <typename ArgFormatter> template <typename ArgFormatter>
void format_arg(BasicFormatter<char, ArgFormatter> &f, void format_arg(BasicFormatter<char, ArgFormatter> &f,
const char *&format_str, const std::tm &tm) { const char *&format_str, const std::tm &tm)
if (*format_str == ':') {
++format_str; if (*format_str == ':')
const char *end = format_str; ++format_str;
while (*end && *end != '}') const char *end = format_str;
++end; while (*end && *end != '}')
if (*end != '}') ++end;
FMT_THROW(FormatError("missing '}' in format string")); if (*end != '}')
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format; FMT_THROW(FormatError("missing '}' in format string"));
format.append(format_str, end + 1); internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
format[format.size() - 1] = '\0'; format.append(format_str, end + 1);
Buffer<char> &buffer = f.writer().buffer(); format[format.size() - 1] = '\0';
std::size_t start = buffer.size(); Buffer<char> &buffer = f.writer().buffer();
for (;;) { std::size_t start = buffer.size();
std::size_t size = buffer.capacity() - start; for (;;)
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); {
if (count != 0) { std::size_t size = buffer.capacity() - start;
buffer.resize(start + count); std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
break; if (count != 0)
{
buffer.resize(start + count);
break;
}
if (size >= format.size() * 256)
{
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
if (size >= format.size() * 256) { format_str = end + 1;
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
format_str = end + 1;
} }
namespace internal{ namespace internal
inline Null<> localtime_r(...) { return Null<>(); } {
inline Null<> localtime_s(...) { return Null<>(); } inline Null<> localtime_r(...)
inline Null<> gmtime_r(...) { return Null<>(); } {
inline Null<> gmtime_s(...) { return Null<>(); } return Null<>();
}
inline Null<> localtime_s(...)
{
return Null<>();
}
inline Null<> gmtime_r(...)
{
return Null<>();
}
inline Null<> gmtime_s(...)
{
return Null<>();
}
} }
// Thread-safe replacement for std::localtime // Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) { inline std::tm localtime(std::time_t time)
struct LocalTime { {
std::time_t time_; struct LocalTime
std::tm tm_; {
std::time_t time_;
LocalTime(std::time_t t): time_(t) {} std::tm tm_;
bool run() { LocalTime(std::time_t t): time_(t) {}
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_)); bool run()
} {
using namespace fmt::internal;
bool handle(std::tm *tm) { return tm != FMT_NULL; } return handle(localtime_r(&time_, &tm_));
}
bool handle(internal::Null<>) {
using namespace fmt::internal; bool handle(std::tm *tm)
return fallback(localtime_s(&tm_, &time_)); {
} return tm != FMT_NULL;
}
bool fallback(int res) { return res == 0; }
bool handle(internal::Null<>)
bool fallback(internal::Null<>) { {
using namespace fmt::internal; using namespace fmt::internal;
std::tm *tm = std::localtime(&time_); return fallback(localtime_s(&tm_, &time_));
if (tm) tm_ = *tm; }
return tm != FMT_NULL;
} bool fallback(int res)
}; {
LocalTime lt(time); return res == 0;
if (lt.run()) }
return lt.tm_;
// Too big time values may be unsupported. bool fallback(internal::Null<>)
FMT_THROW(fmt::FormatError("time_t value out of range")); {
return std::tm(); using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
};
LocalTime lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
} }
// Thread-safe replacement for std::gmtime // Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) { inline std::tm gmtime(std::time_t time)
struct GMTime { {
std::time_t time_; struct GMTime
std::tm tm_; {
std::time_t time_;
GMTime(std::time_t t): time_(t) {} std::tm tm_;
bool run() { GMTime(std::time_t t): time_(t) {}
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_)); bool run()
} {
using namespace fmt::internal;
bool handle(std::tm *tm) { return tm != FMT_NULL; } return handle(gmtime_r(&time_, &tm_));
}
bool handle(internal::Null<>) {
using namespace fmt::internal; bool handle(std::tm *tm)
return fallback(gmtime_s(&tm_, &time_)); {
} return tm != FMT_NULL;
}
bool fallback(int res) { return res == 0; }
bool handle(internal::Null<>)
bool fallback(internal::Null<>) { {
std::tm *tm = std::gmtime(&time_); using namespace fmt::internal;
if (tm != FMT_NULL) tm_ = *tm; return fallback(gmtime_s(&tm_, &time_));
return tm != FMT_NULL; }
}
}; bool fallback(int res)
GMTime gt(time); {
if (gt.run()) return res == 0;
return gt.tm_; }
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range")); bool fallback(internal::Null<>)
return std::tm(); {
std::tm *tm = std::gmtime(&time_);
if (tm != FMT_NULL) tm_ = *tm;
return tm != FMT_NULL;
}
};
GMTime gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
} }
} //namespace fmt } //namespace fmt
......
...@@ -169,11 +169,11 @@ void drop_all(); ...@@ -169,11 +169,11 @@ void drop_all();
#define SPDLOG_STR_H(x) #x #define SPDLOG_STR_H(x) #x
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) #define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#ifdef _MSC_VER #ifdef _MSC_VER
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__) #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__) #define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
#else #else
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__) #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__) #define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
#endif #endif
#else #else
#define SPDLOG_TRACE(logger, ...) #define SPDLOG_TRACE(logger, ...)
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
#include "catch.hpp" #include "catch.hpp"
#include "utils.h" #include "utils.h"
#define SPDLOG_TRACE_ON #define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON #define SPDLOG_DEBUG_ON
#include "../include/spdlog/spdlog.h" #include "../include/spdlog/spdlog.h"
#include "../include/spdlog/sinks/null_sink.h" #include "../include/spdlog/sinks/null_sink.h"
......
...@@ -6,45 +6,45 @@ ...@@ -6,45 +6,45 @@
TEST_CASE("debug and trace w/o format string", "[macros]]") TEST_CASE("debug and trace w/o format string", "[macros]]")
{ {
prepare_logdir(); prepare_logdir();
std::string filename = "logs/simple_log"; std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v"); logger->set_pattern("%v");
logger->set_level(spdlog::level::trace); logger->set_level(spdlog::level::trace);
SPDLOG_TRACE(logger, "Test message 1"); SPDLOG_TRACE(logger, "Test message 1");
//SPDLOG_DEBUG(logger, "Test message 2"); //SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message 2"); SPDLOG_DEBUG(logger, "Test message 2");
logger->flush(); logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 2\n")); REQUIRE(ends_with(file_contents(filename), "Test message 2\n"));
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 2);
} }
TEST_CASE("debug and trace with format strings", "[macros]]") TEST_CASE("debug and trace with format strings", "[macros]]")
{ {
prepare_logdir(); prepare_logdir();
std::string filename = "logs/simple_log"; std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v"); logger->set_pattern("%v");
logger->set_level(spdlog::level::trace); logger->set_level(spdlog::level::trace);
#if !defined(SPDLOG_FMT_PRINTF) #if !defined(SPDLOG_FMT_PRINTF)
SPDLOG_TRACE(logger, "Test message {}", 1); SPDLOG_TRACE(logger, "Test message {}", 1);
//SPDLOG_DEBUG(logger, "Test message 2"); //SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message {}", 222); SPDLOG_DEBUG(logger, "Test message {}", 222);
#else #else
SPDLOG_TRACE(logger, "Test message %d", 1); SPDLOG_TRACE(logger, "Test message %d", 1);
//SPDLOG_DEBUG(logger, "Test message 2"); //SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message %d", 222); SPDLOG_DEBUG(logger, "Test message %d", 222);
#endif #endif
logger->flush(); logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 222\n")); REQUIRE(ends_with(file_contents(filename), "Test message 222\n"));
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 2);
} }
...@@ -49,8 +49,8 @@ std::size_t get_filesize(const std::string& filename) ...@@ -49,8 +49,8 @@ std::size_t get_filesize(const std::string& filename)
// source: https://stackoverflow.com/a/2072890/192001 // source: https://stackoverflow.com/a/2072890/192001
bool ends_with(std::string const & value, std::string const & ending) bool ends_with(std::string const & value, std::string const & ending)
{ {
if (ending.size() > value.size()) return false; if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment