pax_global_header00006660000000000000000000000064144703626500014521gustar00rootroot0000000000000052 comment=e21baecd4753f14da64ede979c5a19302618b752 plog-1.1.10/000077500000000000000000000000001447036265000125425ustar00rootroot00000000000000plog-1.1.10/.appveyor.yml000066400000000000000000000022271447036265000152130ustar00rootroot00000000000000configuration: - Release environment: matrix: - generator: "Visual Studio 14 2015" - generator: "Visual Studio 14 2015 Win64" - generator: "Visual Studio 10 2010" - generator: "Visual Studio 10 2010 Win64" - generator: "MinGW Makefiles" dialect: mingw - generator: "MinGW Makefiles" dialect: mingw-w64 matrix: fast_finish: true shallow_clone: true skip_commits: files: - .circleci/ - .github/ - .editorconfig - .gitignore - .cirrus.yml - LICENSE - plog.nuspec - plog.targets - README.md before_build: # Workaround for CMake not wanting sh.exe on PATH for MinGW - set PATH=%PATH:C:\Program Files\Git\usr\bin;=% - if "%dialect%"=="mingw" set PATH=c:\MinGW\bin;%PATH% - if "%dialect%"=="mingw-w64" set PATH=c:\msys64\mingw64\bin;%PATH% - cmake -H. -Bbuild -G"%generator%" -DCMAKE_BUILD_TYPE=%configuration% -DPLOG_BUILD_TESTS=1 build_script: - if "%generator:~0,6%"=="Visual" set CMAKE_BUILD_FLAGS=--config %configuration% -- /m /v:m - if "%generator:~0,5%"=="MinGW" set CMAKE_BUILD_FLAGS=-- -j - cmake --build build %CMAKE_BUILD_FLAGS% test_script: - cd build && ctest -V plog-1.1.10/.circleci/000077500000000000000000000000001447036265000143755ustar00rootroot00000000000000plog-1.1.10/.circleci/config.yml000066400000000000000000000014041447036265000163640ustar00rootroot00000000000000version: 2.0 jobs: rtems: docker: - image: sergiusthebest/rtems-arm-rasberrypi-ci:latest steps: - checkout - run: cmake -H. -Bbuild -DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE && cd build && make -j platformio: docker: - image: ghcr.io/sergiusthebest/platformio-ci:latest steps: - checkout - run: pio run -d samples/Arduino android-ndk: docker: - image: sergiusthebest/android-ndk-ci:latest steps: - checkout - run: cmake -H. -Bbuild -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-30 -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake && cd build && make -j workflows: version: 2 workflow: jobs: - rtems - platformio - android-ndk plog-1.1.10/.cirrus.yml000066400000000000000000000006731447036265000146600ustar00rootroot00000000000000task: name: FreeBSD freebsd_instance: matrix: - image_family: freebsd-12-3 - image_family: freebsd-13-2 matrix: env: BUILD_TYPE: Debug env: BUILD_TYPE: Release install_script: pkg install -y cmake compile_script: cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DPLOG_BUILD_TESTS=1 && cmake --build build -- -j4 test_script: cd build && ctest -V plog-1.1.10/.editorconfig000066400000000000000000000004721447036265000152220ustar00rootroot00000000000000# EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 4 [*.md] trim_trailing_whitespace = false [*.yml] indent_size = 2 plog-1.1.10/.github/000077500000000000000000000000001447036265000141025ustar00rootroot00000000000000plog-1.1.10/.github/workflows/000077500000000000000000000000001447036265000161375ustar00rootroot00000000000000plog-1.1.10/.github/workflows/ci.yml000066400000000000000000000050271447036265000172610ustar00rootroot00000000000000name: CI on: push: paths-ignore: - .circleci/ - .appveyor.yml - .cirrus.yml - .editorconfig - .gitignore - LICENSE - plog.nuspec - plog.targets - README.md pull_request: paths-ignore: - .circleci/ - .appveyor.yml - .cirrus.yml - .editorconfig - .gitignore - LICENSE - plog.nuspec - plog.targets - README.md permissions: contents: read jobs: build: name: ${{ matrix.cxx }}, ${{ matrix.os }} strategy: fail-fast: true matrix: include: [ # You can access the following values via ${{ matrix.??? }} # # pkgs : apt-get package names separated by space # cxx : C++ compiler executable # os : GitHub Actions YAML workflow label. See https://github.com/actions/virtual-environments#available-environments # gcc { os: ubuntu-22.04, cxx: g++-12, pkgs: '' }, { os: ubuntu-22.04, cxx: g++-11, pkgs: '' }, # (default on Jammy 22.04) { os: ubuntu-22.04, cxx: g++-9, pkgs: '' }, # (default on Focal 20.04) { os: ubuntu-20.04, cxx: g++-7, pkgs: 'g++-7' }, # (default on Bionic 18.04) { os: ubuntu-20.04, cxx: g++-5, pkgs: 'g++-5', repo: 'xenial' }, # (default on Xenial 16.04) { os: ubuntu-20.04, cxx: g++-4.8, pkgs: 'g++-4.8', repo: 'trusty' }, # (default on Trusty 14.04) # clang { os: ubuntu-22.04, cxx: clang++-14, pkgs: '' }, { os: ubuntu-20.04, cxx: clang++-10, pkgs: '' }, { os: ubuntu-20.04, cxx: clang++-6.0, pkgs: 'clang-6.0' }, # msvc { os: windows-2019, cxx: 'vs2019' }, { os: windows-2022, cxx: 'vs2022' } ] runs-on: ${{ matrix.os }} env: CXX: ${{ matrix.cxx }} steps: - name: checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: apt/sources.list.d if: ${{ matrix.repo != '' }} run: | echo "deb http://archive.ubuntu.com/ubuntu ${{ matrix.repo }} main" | sudo tee /etc/apt/sources.list.d/${{ matrix.repo }}.list - name: apt-get install if: ${{ matrix.pkgs != '' }} run: | sudo apt-get update sudo apt-get install ${{ matrix.pkgs }} - name: build run: | cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DPLOG_BUILD_TESTS=1 . cmake --build build --parallel - name: test run: cd build && ctest -V plog-1.1.10/.github/workflows/nuget.yml000066400000000000000000000010521447036265000200020ustar00rootroot00000000000000name: nuget on: push: tags: - '[0-9]*' jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup NuGet uses: nuget/setup-nuget@v1 with: nuget-version: 'latest' - name: Pack NuGet run: nuget pack plog.nuspec -p version=${{github.ref_name}} - name: Publish run: nuget push *.nupkg -Source "https://nuget.pkg.github.com/SergiusTheBest/index.json" -SkipDuplicate -ApiKey "${{secrets.GITHUB_TOKEN}}" plog-1.1.10/.gitignore000066400000000000000000000000161447036265000145270ustar00rootroot00000000000000build .vscode plog-1.1.10/CMakeLists.txt000066400000000000000000000044171447036265000153100ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) project(plog VERSION 1.1.10 LANGUAGES CXX) # check if building as a stand-alone project if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(IS_TOPLEVEL_PROJECT TRUE) else() set(IS_TOPLEVEL_PROJECT FALSE) endif() # options option(PLOG_BUILD_SAMPLES "Build ${PROJECT_NAME} samples" ${IS_TOPLEVEL_PROJECT}) option(PLOG_INSTALL "Generate ${PROJECT_NAME} install target" ${IS_TOPLEVEL_PROJECT}) option(PLOG_BUILD_TESTS "Build tests" OFF) # make sure install paths work on all platforms include(GNUInstallDirs) add_library(${PROJECT_NAME} INTERFACE) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} INTERFACE $ $ ) if(ANDROID) target_link_libraries(${PROJECT_NAME} INTERFACE log) endif() if(PLOG_BUILD_SAMPLES) # add a pseudo-project to make plog headers visible in IDE file(GLOB_RECURSE ${PROJECT_NAME}_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) add_custom_target(${PROJECT_NAME}-headers SOURCES ${${PROJECT_NAME}_HEADERS}) set_target_properties(${PROJECT_NAME}-headers PROPERTIES FOLDER Include) # add samples add_subdirectory(samples) endif() if(PLOG_BUILD_TESTS) enable_testing() add_subdirectory(test) endif() if(PLOG_INSTALL) install( TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Config ) install( EXPORT ${PROJECT_NAME}Config DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} NAMESPACE ${PROJECT_NAME}:: ) install( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR} ) include(CMakePackageConfigHelpers) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake COMPATIBILITY AnyNewerVersion ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} ) endif() plog-1.1.10/LICENSE000066400000000000000000000020571447036265000135530ustar00rootroot00000000000000MIT License Copyright (c) 2022 Sergey Podobry Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. plog-1.1.10/README.md000066400000000000000000001323661447036265000140340ustar00rootroot00000000000000# Plog - portable, simple and extensible C++ logging library Pretty powerful logging library in about 1000 lines of code [![CI](https://github.com/SergiusTheBest/plog/actions/workflows/ci.yml/badge.svg)](https://github.com/SergiusTheBest/plog/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/rna5gwhqjb13wovr/branch/master?svg=true)](https://ci.appveyor.com/project/SergiusTheBest/plog/branch/master) [![CircleCI](https://circleci.com/gh/SergiusTheBest/plog.svg?style=svg)](https://circleci.com/gh/SergiusTheBest/plog) [![Build Status](https://api.cirrus-ci.com/github/SergiusTheBest/plog.svg)](https://cirrus-ci.com/github/SergiusTheBest/plog) ![image](doc/color-console.png) - [Introduction](#introduction) - [Hello log!](#hello-log) - [Features](#features) - [Usage](#usage) - [Step 1: Adding includes](#step-1-adding-includes) - [Step 2: Initialization](#step-2-initialization) - [Step 3: Logging](#step-3-logging) - [Basic logging macros](#basic-logging-macros) - [Conditional logging macros](#conditional-logging-macros) - [Logger severity checker](#logger-severity-checker) - [Advanced usage](#advanced-usage) - [Changing severity at runtime](#changing-severity-at-runtime) - [Custom initialization](#custom-initialization) - [Multiple appenders](#multiple-appenders) - [Multiple loggers](#multiple-loggers) - [Share log instances across modules (exe, dll, so, dylib)](#share-log-instances-across-modules-exe-dll-so-dylib) - [Chained loggers](#chained-loggers) - [Architecture](#architecture) - [Overview](#overview) - [Logger](#logger) - [Record](#record) - [Formatter](#formatter) - [TxtFormatter](#txtformatter) - [TxtFormatterUtcTime](#txtformatterutctime) - [CsvFormatter](#csvformatter) - [CsvFormatterUtcTime](#csvformatterutctime) - [FuncMessageFormatter](#funcmessageformatter) - [MessageOnlyFormatter](#messageonlyformatter) - [Converter](#converter) - [UTF8Converter](#utf8converter) - [NativeEOLConverter](#nativeeolconverter) - [Appender](#appender) - [RollingFileAppender](#rollingfileappender) - [ConsoleAppender](#consoleappender) - [ColorConsoleAppender](#colorconsoleappender) - [AndroidAppender](#androidappender) - [EventLogAppender](#eventlogappender) - [DebugOutputAppender](#debugoutputappender) - [DynamicAppender](#dynamicappender) - [Miscellaneous notes](#miscellaneous-notes) - [Lazy stream evaluation](#lazy-stream-evaluation) - [Stream improvements over std::ostream](#stream-improvements-over-stdostream) - [Automatic 'this' pointer capture](#automatic-this-pointer-capture) - [Headers to include](#headers-to-include) - [Unicode](#unicode) - [Wide string support](#wide-string-support) - [Performance](#performance) - [Printf style formatting](#printf-style-formatting) - [LOG_XXX macro name clashes](#log_xxx-macro-name-clashes) - [Disable logging to reduce binary size](#disable-logging-to-reduce-binary-size) - [Extending](#extending) - [Custom data type](#custom-data-type) - [Custom appender](#custom-appender) - [Custom formatter](#custom-formatter) - [Custom converter](#custom-converter) - [Samples](#samples) - [References](#references) - [Competing C++ log libraries](#competing-c-log-libraries) - [Tools and useful info](#tools-and-useful-info) - [License](#license) - [Version history](#version-history) # Introduction ## Hello log! Plog is a C++ logging library that is designed to be as simple, small and flexible as possible. It is created as an alternative to existing large libraries and provides some unique features as [CSV log format]((#csvformatter)) and [wide string support](#wide-string-support). Here is a minimal hello log sample: ```cpp #include // Step1: include the headers #include "plog/Initializers/RollingFileInitializer.h" int main() { plog::init(plog::debug, "Hello.txt"); // Step2: initialize the logger // Step3: write log messages using a special macro // There are several log macros, use the macro you liked the most PLOGD << "Hello log!"; // short macro PLOG_DEBUG << "Hello log!"; // long macro PLOG(plog::debug) << "Hello log!"; // function-style macro // Also you can use LOG_XXX macro but it may clash with other logging libraries LOGD << "Hello log!"; // short macro LOG_DEBUG << "Hello log!"; // long macro LOG(plog::debug) << "Hello log!"; // function-style macro return 0; } ``` And its output: ``` 2015-05-18 23:12:43.921 DEBUG [21428] [main@13] Hello log! 2015-05-18 23:12:43.968 DEBUG [21428] [main@14] Hello log! 2015-05-18 23:12:43.968 DEBUG [21428] [main@15] Hello log! ``` ## Features - Very small (slightly more than 1000 LOC) - Easy to use - Headers only - No 3rd-party dependencies - Cross-platform: Windows, Linux, FreeBSD, macOS, Android, RTEMS (gcc, clang, msvc, mingw, mingw-w64, icc, c++builder) - Thread and type safe - Formatters: [TXT](#txtformatter), [CSV](#csvformatter), [FuncMessage](#funcmessageformatter), [MessageOnly](#messageonlyformatter) - Appenders: [RollingFile](#rollingfileappender), [Console](#consoleappender), [ColorConsole](#colorconsoleappender), [Android](#androidappender), [EventLog](#eventlogappender), [DebugOutput](#debugoutputappender), [DynamicAppender](#dynamicappender) - [Automatic 'this' pointer capture](#automatic-this-pointer-capture) (supported only on msvc) - [Lazy stream evaluation](#lazy-stream-evaluation) - [Unicode aware](#unicode), files are stored in UTF-8, supports [Utf8Everywhere](http://utf8everywhere.org) - Doesn't require C++11 - [Extendable](#extending) - No `windows.h` dependency - Can use UTC or local time - Can print buffers in HEX or ASCII - Can print `std` containers - Uses modern CMake # Usage To start using plog you need to make 3 simple steps. ## Step 1: Adding includes At first your project needs to know about plog. For that you have to: 1. Add `plog/include` to the project include paths 2. Add `#include ` into your cpp/h files (if you have precompiled headers it is a good place to add this include there) ## Step 2: Initialization The next step is to initialize the [Logger](#logger). This is done by the following `plog::init` function: ```cpp Logger& init(Severity maxSeverity, const char/wchar_t* fileName, size_t maxFileSize = 0, int maxFiles = 0); ``` `maxSeverity` is the logger severity upper limit. All log messages have its own severity and if it is higher than the limit those messages are dropped. Plog defines the following severity levels: ```cpp enum Severity { none = 0, fatal = 1, error = 2, warning = 3, info = 4, debug = 5, verbose = 6 }; ``` > **Note** Messages with severity level `none` will always be printed. The log format is determined automatically by `fileName` file extension: - .csv => [CSV format](#csvformatter) - anything else => [TXT format](#txtformatter) The rolling behavior is controlled by `maxFileSize` and `maxFiles` parameters: - `maxFileSize` - the maximum log file size in bytes - `maxFiles` - a number of log files to keep If one of them is zero then log rolling is disabled. Sample: ```cpp plog::init(plog::warning, "c:\\logs\\log.csv", 1000000, 5); ``` Here the logger is initialized to write all messages with up to warning severity to a file in csv format. Maximum log file size is set to 1'000'000 bytes and 5 log files are kept. > **Note** See [Custom initialization](#custom-initialization) for advanced usage. ## Step 3: Logging Logging is performed with the help of special macros. A log message is constructed using stream output operators `<<`. Thus it is type-safe and extendable in contrast to a format string output. ### Basic logging macros This is the most used type of logging macros. They do unconditional logging. #### Long macros: ```cpp PLOG_VERBOSE << "verbose"; PLOG_DEBUG << "debug"; PLOG_INFO << "info"; PLOG_WARNING << "warning"; PLOG_ERROR << "error"; PLOG_FATAL << "fatal"; PLOG_NONE << "none"; ``` #### Short macros: ```cpp PLOGV << "verbose"; PLOGD << "debug"; PLOGI << "info"; PLOGW << "warning"; PLOGE << "error"; PLOGF << "fatal"; PLOGN << "none"; ``` #### Function-style macros: ```cpp PLOG(severity) << "msg"; ``` ### Conditional logging macros These macros are used to do conditional logging. They accept a condition as a parameter and perform logging if the condition is true. #### Long macros: ```cpp PLOG_VERBOSE_IF(cond) << "verbose"; PLOG_DEBUG_IF(cond) << "debug"; PLOG_INFO_IF(cond) << "info"; PLOG_WARNING_IF(cond) << "warning"; PLOG_ERROR_IF(cond) << "error"; PLOG_FATAL_IF(cond) << "fatal"; PLOG_NONE_IF(cond) << "none"; ``` #### Short macros: ```cpp PLOGV_IF(cond) << "verbose"; PLOGD_IF(cond) << "debug"; PLOGI_IF(cond) << "info"; PLOGW_IF(cond) << "warning"; PLOGE_IF(cond) << "error"; PLOGF_IF(cond) << "fatal"; PLOGN_IF(cond) << "none"; ``` #### Function-style macros: ```cpp PLOG_IF(severity, cond) << "msg"; ``` ### Logger severity checker In some cases there is a need to perform a group of actions depending on the current logger severity level. There is a special macro for that. It helps to minimize performance penalty when the logger is inactive. ```cpp IF_PLOG(severity) ``` Sample: ```cpp IF_PLOG(plog::debug) // we want to execute the following statements only at debug severity (and higher) { for (int i = 0; i < vec.size(); ++i) { PLOGD << "vec[" << i << "]: " << vec[i]; } } ``` # Advanced usage ## Changing severity at runtime It is possible to set the maximum severity not only at the logger initialization time but at any time later. There are special accessor methods: ```cpp Severity Logger::getMaxSeverity() const; Logger::setMaxSeverity(Severity severity); ``` To get the logger use `plog::get` function: ```cpp Logger* get(); ``` Sample: ```cpp plog::get()->setMaxSeverity(plog::debug); ``` ## Custom initialization Non-typical log cases require the use of custom initialization. It is done by the following `plog::init` function: ```cpp Logger& init(Severity maxSeverity = none, IAppender* appender = NULL); ``` You have to construct an [Appender](#appender) parameterized with a [Formatter](#formatter) and pass it to the `plog::init` function. > **Note** The appender lifetime should be static! Sample: ```cpp static plog::ConsoleAppender consoleAppender; plog::init(plog::debug, &consoleAppender); ``` ## Multiple appenders It is possible to have multiple [Appenders](#appender) within a single [Logger](#logger). In such case log message will be written to all of them. Use the following method to accomplish that: ```cpp Logger& Logger::addAppender(IAppender* appender); ``` Sample: ```cpp static plog::RollingFileAppender fileAppender("MultiAppender.csv", 8000, 3); // Create the 1st appender. static plog::ConsoleAppender consoleAppender; // Create the 2nd appender. plog::init(plog::debug, &fileAppender).addAppender(&consoleAppender); // Initialize the logger with the both appenders. ``` Here the logger is initialized in the way when log messages are written to both a file and a console. *Refer to [MultiAppender](samples/MultiAppender) for a complete sample.* ## Multiple loggers Multiple [Loggers](#logger) can be used simultaneously each with their own separate configuration. The [Loggers](#logger) differ by their instanceId (that is implemented as a template parameter). The default instanceId is zero. Initialization is done by the appropriate template `plog::init` functions: ```cpp Logger& init(...); ``` To get a logger use `plog::get` function (returns `NULL` if the logger is not initialized): ```cpp Logger* get(); ``` All logging macros have their special versions that accept an instanceId parameter. These kind of macros have an underscore at the end: ```cpp PLOGD_(instanceId) << "debug"; PLOGD_IF_(instanceId, condition) << "conditional debug"; IF_PLOG_(instanceId, severity) ``` Sample: ```cpp enum // Define log instanceIds. Default is 0 and is omitted from this enum. { SecondLog = 1 }; int main() { plog::init(plog::debug, "MultiInstance-default.txt"); // Initialize the default logger instance. plog::init(plog::debug, "MultiInstance-second.txt"); // Initialize the 2nd logger instance. // Write some messages to the default log. PLOGD << "Hello default log!"; // Write some messages to the 2nd log. PLOGD_(SecondLog) << "Hello second log!"; return 0; } ``` *Refer to [MultiInstance](samples/MultiInstance) for a complete sample.* ## Share log instances across modules (exe, dll, so, dylib) For applications that consist of several binary modules, plog instances can be local (each module has its own instance) or shared (all modules use the same instance). In case of shared you have to initialize plog only in one module, other modules will reuse that instance. Sharing behavior is controlled by the following macros and is OS-dependent: |Macro|OS|Behavior| |--|--|--| |PLOG_GLOBAL|Linux/Unix|Shared| |PLOG_LOCAL|Linux/Unix|Local| |PLOG_EXPORT|Linux/Unix|n/a| |PLOG_IMPORT|Linux/Unix|n/a| ||Linux/Unix|According to compiler settings| |PLOG_GLOBAL|Windows|n/a| |PLOG_LOCAL|Windows|Local| |PLOG_EXPORT|Windows|Shared (exports)| |PLOG_IMPORT|Windows|Shared (imports)| ||Windows|Local| For sharing on Windows one module should use `PLOG_EXPORT` and others should use `PLOG_IMPORT`. Also be careful on Linux/Unix: if you don't specify sharing behavior it will be determined by compiler settings (`-fvisibility`). *Refer to [Shared](samples/Shared) for a complete sample.* ## Chained loggers A [Logger](#logger) can work as an [Appender](#appender) for another [Logger](#logger). So you can chain several loggers together. This is useful for streaming log messages from a shared library to the main application binary. *Important: don't forget to specify `PLOG_LOCAL` sharing mode on Linux/Unix systems for this sample.* Sample: ```cpp // shared library // Function that initializes the logger in the shared library. extern "C" void EXPORT initialize(plog::Severity severity, plog::IAppender* appender) { plog::init(severity, appender); // Initialize the shared library logger. } // Function that produces a log message. extern "C" void EXPORT foo() { PLOGI << "Hello from shared lib!"; } ``` ```cpp // main app // Functions imported from the shared library. extern "C" void initialize(plog::Severity severity, plog::IAppender* appender); extern "C" void foo(); int main() { plog::init(plog::debug, "ChainedApp.txt"); // Initialize the main logger. PLOGD << "Hello from app!"; // Write a log message. initialize(plog::debug, plog::get()); // Initialize the logger in the shared library. Note that it has its own severity. foo(); // Call a function from the shared library that produces a log message. return 0; } ``` *Refer to [Chained](samples/Chained) for a complete sample.* # Architecture ## Overview Plog is designed to be small but flexible, so it prefers templates to interface inheritance. All main entities are shown on the following UML diagram: ```mermaid classDiagram class Logger~instanceId~ { <> +addAppender() +getMaxSeverity() +setMaxSeverity() +checkSeverity() -maxSeverity -appenders } class IAppender { <> +write() } Logger --|> IAppender Logger "1" o-- "*" IAppender IAppender <|-- RollingFileAppender~Formatter, Converter~ IAppender <|-- ConsoleAppender~Formatter~ IAppender <|-- AndroidAppender~Formatter~ IAppender <|-- EventLogAppender~Formatter~ IAppender <|-- DebugOutputAppender~Formatter~ IAppender <|-- DynamicAppender ConsoleAppender <|-- ColorConsoleAppender~Formatter~ DynamicAppender "1" o-- "*" IAppender ``` ```mermaid classDiagram class Severity { <> none, fatal, error, warning, info, debug, verbose } class Record { +operator<<() +printf() -time -severity -tid -object -line -message -func -file -instanceId } ``` ```mermaid classDiagram class CsvFormatter { +header()$ +format()$ } class TxtFormatter { +header()$ +format()$ } class FuncMessageFormatter { +header()$ +format()$ } class MessageOnlyFormatter { +header()$ +format()$ } ``` ```mermaid classDiagram class UTF8Converter { +header()$ +convert()$ } class NativeEOLConverter~NextConverter~{ +header()$ +convert()$ } ``` There are 5 functional parts: - [Logger](#logger) - the main object, implemented as singleton - [Record](#record) - keeps log data: time, message, etc - [Appender](#appender) - represents a log data destination: file, console, etc - [Formatter](#formatter) - formats log data into a string - [Converter](#converter) - converts formatter output into a raw buffer The log data flow is shown below: ```mermaid flowchart LR; ST((start)) --> P[PLOG macro] --> R[Record] --> L[Logger] --> A[Appender] A -->|record| F[Formatter] -->|text| C[Converter] -->|binary| A A --> FIN(((finish))) ``` ## Logger [Logger](#logger) is a center object of the whole logging system. It is a singleton and thus it forms a known single entry point for configuration and processing log data. [Logger](#logger) can act as [Appender](#appender) for another [Logger](#logger) because it implements `IAppender` interface. Also there can be several independent loggers that are parameterized by an integer instanceId number. The default instanceId is 0. ```cpp template class Logger : public util::Singleton >, public IAppender { public: Logger(Severity maxSeverity = none); Logger& addAppender(IAppender* appender); Severity getMaxSeverity() const; void setMaxSeverity(Severity severity); bool checkSeverity(Severity severity) const; virtual void write(const Record& record); void operator+=(const Record& record); }; ``` ## Record [Record](#record) stores all log data. It includes: - time - severity - thread id - 'this' pointer (if a log message is written from within an object) - source line - source file name - function name - message - instance id > **Note** Source file name isn't captured by default. To enable it define PLOG_CAPTURE_FILE. Also [Record](#record) has a number of overloaded stream output operators to construct a message. ```cpp class Record { public: Record(Severity severity, const char* func, size_t line, const char* file, const void* object, int instanceId); ////////////////////////////////////////////////////////////////////////// // Stream output operators Record& operator<<(char data); Record& operator<<(wchar_t data); template Record& operator<<(const T& data); ////////////////////////////////////////////////////////////////////////// // Getters virtual const util::Time& getTime() const; virtual Severity getSeverity() const; virtual unsigned int getTid() const; virtual const void* getObject() const; virtual size_t getLine() const; virtual const util::nchar* getMessage() const; virtual const char* getFunc() const; virtual const char* getFile() const; virtual int getInstanceId() const; }; ``` *See [Stream improvements over std::ostream](#stream-improvements-over-stdostream).* *Refer to [Demo](samples/Demo) sample to see what can be written to the log stream.* ## Formatter [Formatter](#formatter) is responsible for formatting log data from [Record](#record) into various string representations (binary forms can be used too). There is no base class for formatters, they are implemented as classes with static functions `format` and `header`: ```cpp class Formatter { public: static util::nstring header(); static util::nstring format(const Record& record); }; ``` *See [How to implement a custom formatter](#custom-formatter).* ### TxtFormatter This is a classic log format available in almost any log library. It is good for console output and it is easy to read without any tools. ``` 2014-11-11 00:29:06.245 FATAL [4460] [main@22] fatal 2014-11-11 00:29:06.261 ERROR [4460] [main@23] error 2014-11-11 00:29:06.261 INFO [4460] [main@24] info 2014-11-11 00:29:06.261 WARN [4460] [main@25] warning 2014-11-11 00:29:06.261 DEBUG [4460] [main@26] debug 2014-11-11 00:29:06.261 INFO [4460] [main@32] This is a message with "quotes"! 2014-11-11 00:29:06.261 DEBUG [4460] [Object::Object@8] 2014-11-11 00:29:06.261 DEBUG [4460] [Object::~Object@13] ``` ### TxtFormatterUtcTime This is a variant of [TxtFormatter](#txtformatter) that uses UTC time instead of local time. ### CsvFormatter This is the most powerful log format. It can be easily read without any tools (but slighlty harder than [TXT format](#txtformatter)) and can be heavily analyzed if it is opened with a CSV-aware tool (like Excel). One rows can be highlighted according to their cell values, another rows can be hidden, columns can be manipulated and you can even run SQL queries on log data! This is a recommended format if logs are big and require heavy analysis. Also 'this' pointer is shown so object instances can be told apart. ``` Date;Time;Severity;TID;This;Function;Message 2014/11/14;15:22:25.033;FATAL;4188;00000000;main@22;"fatal" 2014/11/14;15:22:25.033;ERROR;4188;00000000;main@23;"error" 2014/11/14;15:22:25.033;INFO;4188;00000000;main@24;"info" 2014/11/14;15:22:25.033;WARN;4188;00000000;main@25;"warning" 2014/11/14;15:22:25.048;DEBUG;4188;00000000;main@26;"debug" 2014/11/14;15:22:25.048;INFO;4188;00000000;main@32;"This is a message with ""quotes""!" 2014/11/14;15:22:25.048;DEBUG;4188;002EF4E3;Object::Object@8; 2014/11/14;15:22:25.048;DEBUG;4188;002EF4E3;Object::~Object@13; ``` > **Note** Message size is limited to 32000 chars. ### CsvFormatterUtcTime This is a variant of [CsvFormatter](#csvformatter) that uses UTC time instead of local time. ### FuncMessageFormatter This format is designed to be used with appenders that provide their own timestamps (like [AndroidAppender](#androidappender) or linux syslog facility). ``` main@22: fatal main@23: error main@24: info main@25: warning main@26: debug main@32: This is a message with "quotes"! Object::Object@8: Object::~Object@13: ``` ### MessageOnlyFormatter Use this formatter when you're interested only in a log message. ``` fatal error info warning debug This is a message with "quotes"! ``` ## Converter [Converter](#converter) is responsible for conversion of [Formatter](#formatter) output data to a raw buffer (represented as `std::string`). It is used by [RollingFileAppender](#rollingfileappender) to perform a conversion before writing to a file. There is no base class for converters, they are implemented as classes with static functions `convert` and `header`: ```cpp class Converter { public: static std::string header(const util::nstring& str); static std::string convert(const util::nstring& str); }; ``` *See [How to implement a custom converter](#custom-converter).* ### UTF8Converter [UTF8Converter](#utf8converter) is a default converter in plog. It converts string data to UTF-8 with BOM. ### NativeEOLConverter This converter converts `` line endings to `` on Windows and does nothing on everything else. As a template parameter it accepts another converter that is called next (by default [UTF8Converter](#utf8converter)). Sample: ```cpp plog::RollingFileAppender > fileAppender("NativeEOL.log"); ``` *Refer to [NativeEOL](samples/NativeEOL) for a complete sample.* ## Appender [Appender](#appender) uses [Formatter](#formatter) and [Converter](#converter) to get a desired representation of log data and outputs (appends) it to a file/console/etc. All appenders must implement `IAppender` interface (the only interface in plog): ```cpp class IAppender { public: virtual ~IAppender(); virtual void write(const Record& record) = 0; }; ``` *See [How to implement a custom appender](#custom-appender).* ### RollingFileAppender This appender outputs log data to a file with rolling behavior. As template parameters it accepts both [Formatter](#formatter) and [Converter](#converter). ```cpp RollingFileAppender::RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0); ``` - `fileName` - a log file name - `maxFileSize` - the maximum log file size in bytes - `maxFiles` - a number of log files to keep If `maxFileSize` or `maxFiles` is 0 then rolling behavior is turned off. The sample file names produced by this appender: - mylog.log <== current log file (size < maxFileSize) - mylog.1.log <== previous log file (size >= maxFileSize) - mylog.2.log <== previous log file (size >= maxFileSize) A file name can be changed at an arbitrary moment by calling `setFileName` as well as `maxFiles` and `maxFileSize` can be changed by calling `setMaxFiles` and `setMaxFileSize`. > **Note** The lowest `maxFileSize` is 1000 bytes. > **Note** A log file is created on the first log message. ### ConsoleAppender This appender outputs log data to `stdout` or `stderr`. As a template parameter it accepts [Formatter](#formatter). ```cpp ConsoleAppender::ConsoleAppender(OutputStream outStream = streamStdOut); ``` ### ColorConsoleAppender This appender outputs log data to `stdout` or `stderr` using colors that depend on a log message severity level. As a template parameter it accepts [Formatter](#formatter). ```cpp ColorConsoleAppender::ColorConsoleAppender(OutputStream outStream = streamStdOut); ``` ### AndroidAppender [AndroidAppender](#androidappender) uses Android logging system to output log data. It can be viewed with [logcat](http://developer.android.com/tools/help/logcat.html) or in a log window of Android IDEs. As a template parameter this appender accepts [Formatter](#formatter) (usually [FuncMessageFormatter](#funcmessageformatter)). ```cpp AndroidAppender::AndroidAppender(const char* tag); ``` ### EventLogAppender This appender outputs log data to the windows event log. It can be viewed with the windows event log viewer. As a template parameter it accepts [Formatter](#formatter). The constructor parameter is the event source name - typically it is the name of the application or a subcomponent of the application. It must be unique for the whole system. ```cpp EventLogAppender::EventLogAppender(const wchar_t* sourceName); ``` [EventLogAppender](#eventlogappender) must be registered in the windows registry before use (before calling the constructor). There is a helper class for that: ```cpp bool EventLogAppenderRegistry::add(const wchar_t* sourceName, const wchar_t* logName = L"Application"); bool EventLogAppenderRegistry::exists(const wchar_t* sourceName, const wchar_t* logName = L"Application"); void EventLogAppenderRegistry::remove(const wchar_t* sourceName, const wchar_t* logName = L"Application"); ``` Registry operations are system-wide and require administrator rights. Also they are persistent so can be performed only once (when the application is installed/uninstalled). ### DebugOutputAppender [DebugOutputAppender](#debugoutputappender) sends log data to the debugger (works only on Windows). As a template parameter this appender accepts [Formatter](#formatter). ```cpp DebugOutputAppender::DebugOutputAppender(); ``` ### DynamicAppender [DynamicAppender](#dynamicappender) is a wrapper that can add/remove appenders dynamically (at any point of time) in a thread-safe manner. ```cpp DynamicAppender& DynamicAppender::addAppender(IAppender* appender); DynamicAppender& DynamicAppender::removeAppender(IAppender* appender); ``` *Refer to [DynamicAppender sample](samples/DynamicAppender) for a complete sample.* # Miscellaneous notes ## Lazy stream evaluation Log messages are constructed using lazy stream evaluation. It means that if a log message will be dropped (because of its severity) then stream output operators are not executed. Thus performance penalty of unprinted log messages is negligible. ```cpp PLOGD << /* the following statements will be executed only when the logger severity is debug or higher */ ... ``` ## Stream improvements over std::ostream Stream output in plog has several improvements over the standard `std::ostream`: - handles wide chars/strings: `wchar_t`, `wchar_t*`, `std::wstring` - handles `NULL` values for C-strings: `char*` and `wchar_t*` - implicitly casts objects to: `std::string` and `std::wstring` (if they have an appropriate cast operator) - supports `QString` and `QStringRef` (you need to include Qt headers before plog) - supports `std::filesystem::path` - supports managed C++ `System::String^` ## Automatic 'this' pointer capture 'This' pointer is captured automatically to log data and can be printed by [CsvFormatter](#csvformatter). Unfortunately this feature is supported only on msvc 2010 and higher. It's disabled by default (due to some compatibility issues with `__if_exists` C++ extension), to enable it define `PLOG_ENABLE_GET_THIS`. ## Headers to include The core plog functionality is provided by inclusion of `plog/Log.h` file. Extra components require inclusion of corresponding extra headers after `plog/Log.h`. Core components are: - [TxtFormatter](#txtformatter)/[TxtFormatterUtcTime](#txtformatterutctime) - [CsvFormatter](#csvformatter)/[CsvFormatterUtcTime](#csvformatterutctime) - [UTF8Converter](#utf8converter) - [NativeEOLConverter](#nativeeolconverter) - [RollingFileAppender](#rollingfileappender) ## Unicode Plog is unicode aware and wide string friendly. All messages are converted to a system native char type: - Windows - `wchar_t` - by default - `char` - if compiling with `/utf-8` switch or set `PLOG_CHAR_IS_UTF8` to 1 - all other systems - `char` Also `char` is treated as: - Windows - active code page - be default - UTF-8 - if compiling with `/utf-8` switch or set `PLOG_CHAR_IS_UTF8` to 1 - all other systems - UTF-8 Internally plog uses `nstring`, `nstringstream` and `nchar` ('n' for native) that are defined as: ```cpp #if PLOG_CHAR_IS_UTF8 typedef std::string nstring; typedef std::ostringstream nostringstream; typedef std::istringstream nistringstream; typedef std::ostream nostream; typedef char nchar; #else typedef std::wstring nstring; typedef std::wostringstream nostringstream; typedef std::wistringstream nistringstream; typedef std::wostream nostream; typedef wchar_t nchar; #endif ``` By default all log files are stored in UTF-8 with BOM thanks to [UTF8Converter](#utf8converter). ## Wide string support Whether `wchar_t`, `wchar_t*`, `std::wstring` can be streamed to log messages or not is controlled by the `PLOG_ENABLE_WCHAR_INPUT` macro. Set it to a non-zero value to enable wide string support. By default wide string support is enabled for Windows and disabled for all non-Windows systems. > **Note** Wide string support requires linking to `iconv` on macOS. ## Performance Plog is not using any asynchronous techniques so it may slow down your application on large volumes of log messages. Producing a single log message takes the following amount of time: |CPU|OS|Time per a log call, microsec| |----|----|:----:| |AMD Phenom II 1055T @3.5GHz|Windows 2008 R2|12| |AMD Phenom II 1055T @3.5GHz|Linux Mint 17.1|8| |Intel Core i3-3120M @2.5GHz|Windows 2012 R2|25| |Intel Core i5-2500K @4.2GHz|Windows 2008 R2|8| |Intel Atom N270 @1.6GHz|Windows 2003|68| Assume 20 microsec per a log call then 500 log calls per a second will slow down an application by 1%. It is acceptable for most use cases. *Refer to [Performance](samples/Performance) for a complete sample.* ## Printf style formatting Plog supports printf style formatting: ```cpp PLOGI.printf("%d %s", 42, "test"); PLOGI.printf(L"%d %S", 42, "test"); // wchar_t version ``` ## LOG_XXX macro name clashes `LOG_XXX` macro names may be in conflict with other libraries (for example [syslog](https://linux.die.net/man/3/syslog)). In such cases you can disable the `LOG_XXX` macro by defining `PLOG_OMIT_LOG_DEFINES` and use `PLOG_XXX`. *Define `PLOG_OMIT_LOG_DEFINES` before `#include ` or in the project settings!* ## Disable logging to reduce binary size Logging code makes binary files larger. If you use it for debugging you can remove all logging code from release builds by defining the macro `PLOG_DISABLE_LOGGING`. # Extending Plog can be easily extended to support new: - [custom data type](#custom-data-type) - [custom appender](#custom-appender) - [custom formatter](#custom-formatter) - [custom converter](#custom-converter) ## Custom data type To output a custom data type to a log message implement the following function: ```cpp namespace plog { Record& operator<<(Record& record, const MyType& t); } ``` *Refer to [CustomType](samples/CustomType) for a complete sample.* ## Custom appender A custom appender must implement the `IAppender` interface. Also it may accept [Formatter](#formatter) and [Converter](#converter) as template parameters however this is optional. ```cpp namespace plog { template class MyAppender : public IAppender { public: virtual void write(const Record& record); }; } ``` *Refer to [CustomAppender](samples/CustomAppender) for a complete sample.* ## Custom formatter A formatter that is compatible with existing appenders must be a class with 2 static methods: - `header` - returns a header for a new log - `format` - formats [Record](#record) to a string ```cpp namespace plog { class MyFormatter { public: static util::nstring header(); static util::nstring format(const Record& record); }; } ``` *Refer to [CustomFormatter](samples/CustomFormatter) for a complete sample.* ## Custom converter A converter must be a class with 2 static methods: - `header` - converts a header for a new log - `convert` - converts log messages ```cpp namespace plog { class MyConverter { public: static std::string header(const util::nstring& str); static std::string convert(const util::nstring& str); }; } ``` *Refer to [CustomConverter](samples/CustomConverter) for a complete sample.* # Samples There are a number of samples that demonstrate various aspects of using plog. They can be found in the [samples](samples) folder: |Sample|Description| |------|-----------| |[Android](samples/Android)|Shows how to use [AndroidAppender](#androidappender).| |[Arduino](samples/Arduino)|Arduino sample - not finished yet!| |[AscDump](samples/AscDump)|Shows how to use `plog::ascdump` to dump binary buffers into ASCII.| |[Chained](samples/Chained)|Shows how to chain a logger in a shared library with the main logger (route messages).| |[ColorConsole](samples/ColorConsole)|Shows how to use [ColorConsoleAppender](#colorconsoleappender).| |[CustomAppender](samples/CustomAppender)|Shows how to implement a custom appender that stores log messages in memory.| |[CustomConverter](samples/CustomConverter)|Shows how to implement a custom converter that encrypts log messages.| |[CustomFormatter](samples/CustomFormatter)|Shows how to implement a custom formatter.| |[CustomType](samples/CustomType)|Shows how to print a custom type to the log stream.| |[CXX11](samples/CXX11)|Demonstrates log stream abilities for C++11 features.| |[CXX17](samples/CXX17)|Demonstrates log stream abilities for C++17 features.| |[DebugOutput](samples/DebugOutput)|Shows how to use [DebugOutputAppender](#debugoutputappender) to write to the windows debug output.| |[Demo](samples/Demo)|Demonstrates log stream abilities, prints various types of messages.| |[DisableLogging](samples/DisableLogging)|Shows how to disable logging (so it will be stripped from the binary).| |[DynamicAppender](samples/DynamicAppender)|Shows how to add/remove appenders dynamically).| |[EventLog](samples/EventLog)|Shows how to use [EventLogAppender](#eventlogappender) to write to the windows event log.| |[Facilities](samples/Facilities)|Shows how to use logging per facilities via multiple logger instances (useful for big projects).| |[Hello](samples/Hello)|A minimal introduction sample, shows the basic 3 steps to start using plog.| |[HexDump](samples/HexDump)|Shows how to use `plog::hexdump` to dump binary buffers into hex.| |[Library](samples/Library)|Shows plog usage in static libraries.| |[MultiAppender](samples/MultiAppender)|Shows how to use multiple appenders with the same logger.| |[MultiInstance](samples/MultiInstance)|Shows how to use multiple logger instances, each instance has its own independent configuration.| |[NotShared](samples/NotShared)|Shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful).| |[ObjectiveC](samples/ObjectiveC)|Shows that plog can be used in ObjectiveC++.| |[Path](samples/Path)|A test sample to check that `std::filesystem::path` can be logged.| |[Performance](samples/Performance)|Measures time per a log call.| |[PrintVar](samples/PrintVar)|Shows how to use `PLOG_PRINT_VAR` to print variables.| |[SetFileName](samples/SetFileName)|Shows how to change a log file name at arbitrary moment.| |[Shared](samples/Shared)|Shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful)| |[SkipNativeEOL](samples/SkipNativeEOL)|Shows how to skip [NativeEOLConverter](#nativeeolconverter).| |[UtcTime](samples/UtcTime)|Shows how to use UTC time instead of local time.| |[Utf8Everywhere](samples/Utf8Everywhere)|Demonstrates how to use http://utf8everywhere.org on Windows.| # References ## Competing C++ log libraries - [Boost::Log](http://www.boost.org/doc/libs/release/libs/log/) - [EasyLogging++](https://github.com/easylogging/easyloggingpp) - [g2log](http://www.codeproject.com/Articles/288827/g-log-An-efficient-asynchronous-logger-using-Cplus) - [g3log](https://github.com/KjellKod/g3log) - [glog](https://code.google.com/p/google-glog/) - [Log4cplus](http://sourceforge.net/projects/log4cplus/) - [Log4cpp](http://log4cpp.sourceforge.net/) - [Log4cxx](http://logging.apache.org/log4cxx/) - [Pantheios](http://pantheios.sourceforge.net/) - [spdlog](https://github.com/gabime/spdlog/) - [reckless](https://github.com/mattiasflodin/reckless) - [loguru](https://github.com/emilk/loguru) - [blackhole](https://github.com/3Hren/blackhole) ## Tools and useful info - [__if_exists Statement](https://msdn.microsoft.com/en-us/library/x7wy9xh3.aspx) - [Controlling Symbol Visibility](https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/SymbolVisibility.html) - [Mermaid](https://mermaid-js.github.io/mermaid/) - [DocToc](https://github.com/thlorenz/doctoc) - [CMake](http://www.cmake.org) - [Compiler support for C++11](https://en.cppreference.com/w/cpp/compiler_support/11) - [Guide to predefined macros in C++ compilers (gcc, clang, msvc etc.)](https://blog.kowalczyk.info/article/j/guide-to-predefined-macros-in-c-compilers-gcc-clang-msvc-etc..html) # License This version of plog is licensed under the [MIT license](https://choosealicense.com/licenses/mit). You can freely use it in your commercial or opensource software. # Version history ## Version 1.1.10 (20 Aug 2023) - New: Add support for UTF-8 char encoding on Windows (#76, #69, #238, #239)\ *This allows to use [Utf8Everywhere](http://utf8everywhere.org) approach* - New: Add ArduinoAppender - New: Publish on [PlatformIO Registry](https://registry.platformio.org) for embedded development (#244) - New: Add support for `char8_t` strings - New: Add tests - Enh: Add rudimentary support of VS2005 (#232) - Enh: Implementation of `vasprintf` emulation (#243) - Fix: Parsing of templated classes (#251) - Fix: Compiling with MSVC using C++20 (#236) - Fix: No newline error with '-Wnewline-eof' build flag (#263) ## Version 1.1.9 (16 Dec 2022) - New: Add ability to truncate log file using `>` in shell (#155) - New: Add override specifier (to be able to build with `-Wsuggest-override`) (#231) - New: Add nuget specs (#86) - New: Add ability to add/remove appenders (#226) - Fix: Printing `boost::filesystem::path` (#227) - Fix: Building on C++ Builder 10.4 (#225) - Fix: `PLOG_LOCAL` mode if symbol visibility set to default (#219) ## Version 1.1.8 (10 Jun 2022) - Fix: 'operator <<' is ambiguous for string_view on Windows (#217) - Fix: CMake + vcpkg: find_package (#211) ## Version 1.1.7 (09 Jun 2022) - New: Add hex dumper (#111) - New: Add ASCII dumper (#213) - New: Add support for printing std containers (#207) - New: Add console initializer - New: Add PrintVar helper - New: Add CMake find_package support (#171) - Enh: Change license to MIT (#212) - Fix: Specify calling convention for std stream manipulators (#210) - Fix: Compilation on VS2010 (#207) - Fix: Use add_custom_target for pseudo-project with headers (#216) ## Version 1.1.6 (06 Feb 2022) - New: Ability to disable logging to reduce binary size (#130) - New: Ability to change `maxFiles`/`maxFileSize` after initialization - New: Logging `std::filesystem::path` without explicit conversion to `std::string` (#168, #185, #183) - New: Allow to choose `stdout`/`stderr` for console appender (#162, #117) - New: Ability to change log file name at runtime (#62) - New: Ability to control sharing across modules (#96, #152, #20) - New: Building on platforms without thread support (#161, #113) - Enh: Change color functions from private to protected (#163) - Enh: Do not include `plog/Init.h` in `plog/Log.h` (#127, #89) - Fix: WideCharToMultiByte bug (#202) - Fix: Building with Qt6 (#190) - Fix: Compiling on GCC 4.4-4.7 (#176) - Fix: Suppress UBSan false positive (#90) - Fix: Don't share handle/fd to child process (#170) - Fix: MSVC analyzer warnings (#148) - Fix: File size truncation > 2GB on Windows (#160) - Fix: [RTEMS](https://www.rtems.org) build on newer toolchain (#158, #159) ## Version 1.1.5 (21 Oct 2019) - New: Use `NativeEOLConverter` by default (#145) - New: Add logger `instanceId` into `Record` (#141) - New: Add support for the printf style formatting (#139) - New: Make `severityFromString` case-insensitive - New: Define macro names with "PLOG" instead of "LOG" in order to avoid conflicts with "LOG" names defined in other packages or in system headers (#25, #129) - New: Add option for building samples (ON per default) (#125, #126) - New: Add CMake installer (#121, #122) - New: Add support for `QStringRef` - New: Modernize CMake (#106) - New: Allow rollLogFiles to be called manually (#100, #103) - New: Add ability to use UTC time (#101) - Fix: Disable `PLOG_GET_THIS()` by default (#120, #132) - Fix: Change `RegSetValueExW` prototype to match windows native declaration (void* -> BYTE*) - Fix: Move `System::String^` handler to a free function (#131) - Fix: Making sure we can build standalone under Windows (#123) - Fix: Parse error by ReSharper (#116) - Fix: Parse error by Clang Code Model in Qt Creator (#114) - Fix: Printing CustomType at begin of the stream (#94) - Fix: Make `RollingFileAppender` work with maxFiles set to 1 (#70) - Fix: Clang-tidy nullable issue ## Version 1.1.4 (26 Mar 2018) - New: Add `-Wundef` support - New: Add [RTEMS](https://www.rtems.org) support (#87) - New: Add Intel C++ Compiler support (#84) - New: Add FreeBSD support (#83) - New: Add `-Wnon-virtual-dtor` support (#79) - New: Support `ostream` operator<< on Windows as well as `wostream` (#66) - Fix: Fix compilation for Android (#68) - Fix: Fix compiling with CMake 2.8 ## Version 1.1.3 (09 Aug 2017) - New: Introduce `LOG_ENABLE_WCHAR_INPUT` macro to control wide string support - New: Add support for managed C++ `System::String^` (#63) - New: Add missing macros for logging with severity NONE (#61) - Fix: Unable to build [NativeEOLConverter](#nativeeolconverter)/[UTF8Converter](#utf8converter) using Visual Studio (#59) - Fix: Use `WriteConsoleW` instead of global `setlocale` for writing unicode into Windows console (#58) - Fix: Mention about linking to `iconv` on macOS (#55) - Fix: `IF_LOG` macro didn't work for curly braces blocks ## Version 1.1.2 (02 May 2017) - New: Add [NativeEOLConverter](#nativeeolconverter) - New: Add [MessageOnlyFormatter](#messageonlyformatter) - New: Slightly increase log performance on Windows (about 9%). ## Version 1.1.1 (17 Apr 2017) - New: Ability to check whether event log registry entry exists (#36) - Fix: Update includes (#47) - Fix: Get rid of `windows.h` dependency (#45, #13) - Fix: Signed unsigned assignment warning (#40) - Fix: Build warning on macOS 10.12 Sierra (#39) ## Version 1.1.0 (20 Nov 2016) - Fix: Introduce binary compatible interface to `Record` (WARNING: this is not compatible with 1.0.x version in [Chained mode](#chained-loggers), so don't mix 1.1.x and 1.0.x) (#34) ## Version 1.0.2 (19 Nov 2016) - New: Default instanceId can be set via `LOG_DEFAULT_INSTANCE` (#11) - New: Support for `QString` (#30) - New: Support for C++Builder - New: `severityFromString` function (#15) - New: Capture source file name (disabled by default) (#21) - New: Add [DebugOutputAppender](#debugoutputappender) (#33) - New: Add [EventLogAppender](#eventlogappender) (#32) - Fix: Crash on processing Obj-C function name (#12) - Fix: Compatibility with [MinGW](http://www.mingw.org/) (#17) - Fix: `IF_LOG_` macro in if/else leads to miss else branch (#27) - Fix: Thread safety for [ConsoleAppender](#consoleappender)/[ColorConsoleAppender](#colorconsoleappender) (#18, #29) - Fix: Support for stream manipulators like `std::endl` (#31) - Fix: Compatibility with old Visual Studio versions ## Version 1.0.1 (01 Nov 2015) - New: Add [ColorConsoleAppender](#colorconsoleappender) - Fix: Compatibility with [Mingw-w64](http://mingw-w64.org/) (#6) - Fix: Log file not created if file name contains Unicode characters in Windows (#7) - Fix: Flush stdout (#4) - Fix: IntelliSense error: expected an identifier (#3) ## Version 1.0.0 (19 May 2015) - Initial public release plog-1.1.10/doc/000077500000000000000000000000001447036265000133075ustar00rootroot00000000000000plog-1.1.10/doc/color-console.png000066400000000000000000001063161447036265000166020ustar00rootroot00000000000000‰PNG  IHDRNiÌo¬RÂzTXtRaw profile type exifxÚmPQà ý÷;Z8Ž]]²ìøC¡IÝúøÐ'’úçýJÌœ¸lRµV0°²b³DÀÑ&gàÉÜC]ôT5.¡Id‘|+Õc>õ¸pÆÜ,+#yFa_ Êá/?FÞÐèhäGiz!‡AóoAUÙ®_Ø;¬_iËÚöß~³éÅÞ!ÄN™À˜ˆ½‹5KhrA—›u%‡™ änN'Ò>YDÆÅfù„iCCPICC profilexœ}‘=HÃ@Å_SK‹Tí â¡:µ *â¨U(B…P+´ê`rý„& IŠ‹£àZpðc±êà⬫ƒ« ~€¸º8)ºH‰ÿK -b<8îÇ»{»w€Ð¬2ÕìTÍ2ÒÉ„˜Í­ŠÁWøÀBˆÉÌÔç$)Ïñu_ïâ<ËûÜŸ£/_0à‰g™nXÄÄÓ›–ÎyŸ8ÂÊržøœ8fЉ¹®¸üƹä°À3#F&=O!K]¬t1+*ñq4¯j”/d]ÎsÞâ¬Vë¬}OþÂpA[Yæ:Í$±ˆ%H¡ Ž ª°§U#ÅDšöþaÇ/‘K!WŒ ¨A…ìøÁÿàw·fqrÂM '€À‹mŒÁ] Õ°íïcÛnþgàJëøkM`æ“ôFG‹ýÛÀÅuGSö€Ë`èI— Ù‘ü4…bx?£oʃ·@ïšÛ[{§@†ºJ݇ÀX‰²×=Þêîíß3íþ~4XrŽŽ72Ý xiTXtXML:com.adobe.xmp ÞßNëbKGD«¬®¸? pHYs  šœtIMEç jà2½ IDATxÚìwxTÕÖÆÓBÊÄ) ¡:¤J•f¥*Š‚Ti*E©…«‚Šà¥ˆTAª:"ÕÐ !\ZHA銀 ¤BH[ß™0%3™™.úÍû<2³Ï^{íwíµÎ:ûœÙGxðwCH;:(÷°óÏôõÀ<ðàþAáIœ<ðÀ<ðÀ\ƒÒCxàxà'qòÀƒÿ§hŠ"êÆóç XÞío¦»/|{ãùóc§yü†¥/•Œš½Ö¢ŸßñäO‹‹Ê_˜…w/­óãúoÀ¸°SÉ óå߃ÿŸ‰Sñh6ïG‡þàf|F4³9@ Ãàw4ýñ]ø¾Ý<_{¯-Mí7¡ÒÈÚ©‡ÎÁ{O$ú_~Ah >cZ»©²—¹ÿc;ÑŒjbS7×BþfÔ#š»)_£¿E{,ÖŽ|`jlnPÊ+gAm7Ä÷fót1qÂ7¡¶âdžßc;ÑŒnæ¦þ¦öÇb1ß…÷è&ŽÓÂ’O/q/}®ÞåœUø‰Ãxz9xÙÔ[Ù·0üAýCú¸t»W@‹ºÉÑóßëc¼·:šNðêz Û†Ø|i„ ±ø Êý87κÿóç1ÆÍ1Ûu\ž^g0DîÀw¼åÉ#ÂsOèÆsg1DGà»àЗ€ggŸ%þñjÜê·ÞÂÛ» 9}>¿þ'&[L‹6°hº˜ÓèoC=¼iñÁ¿ÎìƒFà_¤Ákµ‰o5•ô¬"Ž¿M5n,™È÷#‰aç ×¶ÍÔëö£ŠB·s1´±HDª÷B5oÙ¿¼ùÇaôq1è÷Øø¯’¿±ù熩Ìïàž¬Ó»¸½÷\ɹâ~$qÿ¥ëlÚo]áã‡"üÐÞŽf\ëøqð¬“ø¡‡qËÐÁpb/~ŸØ&¯5aò´Çc1œŒ@»æ+T­ýÌÕÆ7l<-¯„ó' [ •+ mߟãqâ?¤¬ù˜î+D1O¨©jôõÁhñïg0Õ×NE·'…ê•„§ßïc±âózS½¯Ðc€Ð¢®P©’Ðf°hƉ~D±Ö£€ÒÓ²ÿ¢:%¾}ôfù=-ê ƒ…–¯Šú@¬ø-ïºün+D1_õË µzšäÌõS£Åÿ“§„ÀÀÜ w]v¿/´BB„Žï‹÷±Xñ\Ö¦ÿyBm£ÒCT¢EÛ?Àõ>z¬´Ð?—ï^ºüÇu](>+ÖˆìA‰ëò¼*ʡ݅^SE»\ð²¬W]û ÍëÊ -ºÏ㉢=±UÝ› * ž*ZÔO>)Z+þ-lCmaAÞü«,<36—ßAe\ë»ÒHñ‹Y&h,¿V41{„ʦÏscÄÿËNæþ…2yüvuL„¨Ú e„&}D½'Nô50Û?­„ªÕ…Æ6þ‘WÊsŸ?Ñ4§Ë ³£Ä°°gîç r‚N! D‹Oÿס”V¸ø”ÇgQÇDˆâi£àoêwå®XñŸPßìcÅûõê¹ñ£BaÖ1ü0ØÜߨíb›&Ô juUXœhß©aª÷–œÿÅC„JF¡LU¡ó(Q÷ªbn?/Nô3: ÁÁB¹2¹ú½<¶Í_œðþ! XÓÇô¹’°%N|Íõ½×‰aûPÇíßÙ/†U=×Ú#ÆÝ\T¶’°%Öºÿþĸã Çm†íý²®.Ê6ç—o5¾©Ñ¢\|gÉonÿV'z‡ãSØù®¢°õ´è[´ïµVô¶'æÀÞâµá]¡þñ‹q”8y *gI†À^$þ½„å'Å»—Öñ1“OŠŸ;ü;$kz¹x|-aß Qµ±ø®é¢ ÿÐüynŒø¿_Ó~{õ‹¢Ž9 êÚXÙ/`ß[‰S¬h»XØ®éÑÅÎ*f§~R”'vˆ¢‚³ãš#Å»¥é³_ñŽÙ&”µ8¦Ó"ÑoäÄ¿-ø#ú1UÜò/»ó»H‰SîøÕUl“þ‰¢Û¹HT¡‡D·w–ðÒÛâs$Zü—ô1Ól¤¨Öìÿ¨8ÑÝ-^㟵–Ñr†èÏãùó°ð¹üówaœøë#,Ú)º“Ñ¢_÷¡àoª×õÍž©ùõ}q‰ø©l“ø ·ã_aeŒø÷78ç òñ‹ž^ø94é„è¿laó½FX+þ£Ÿfí]LŒø¯}_P[Ólª?ìÈ&¬íñX1DGŠßšiBzµþʹüV‰öDœcŽˆè sRêJi1JÔ¡a¢‹>mßþ–êE‹tuÌ¡VñÛ6Æ"qНÎJs}ýO,âG]áP¬h:[Ä—ç‰þÈ$ ùÛ…rŽôW óãÄ8¾NáæŒ#ÿZÚÏâ8•0h¦ø„G‰!&R|ç ³I4Íòùë-ãœÿzô‚µŽŠ:Â좋‰ÿÕ£–‹Þ*ÿð†ÍßCÑbˆ‰¿ù¶úåç7i¼•d_ûÃô¡ªª·¹s,Þ\ôYÁ ·×–Bn\s »1<]Žœ¨S..ŽÕEU5ÙºÿˆÃd>\ßü†Ð¶™'Oº(¿>ªjùå[O‰æÕPt±qè÷‡¢ìR´õ¾R–üæöŸzøº“ñùCèi ØÞGCU%‰¤‹öÇŽ’]©¡õ-º)¯ñÙTHÌq ”?„žÂ:À±Þ™9ÎÇæ6ÿÍPÖ¹DzÒó(×íÇÿð¼§ô±9F…—;ü—R’}íª‹ýÿûÓñmSËâ¶J38°ÛµæYwrÿ»w‹¤"öýË¡ÿqüYwíT•Î"{d7’Î?aÀî´Dr𠨞wgEÈž3‚ä–õIlÿÕ?F;Îb>CbõjÄxŒììlá êNá_HjØŒÄë/à?ÒÔ¾KÔk§çþ]¥7Ê5;ð !wQ– ra\uPÖ>Gò_ ðÒÓœâþÁt&dƒªgwX9¤'¬èÿ»üyׂ;ýwŸ…¾åiRÚ7 ¡QRgEº÷¨ÀÏ£Ë,–”võ‰´=É3# Ó ùº²¾~“¤ÇêÙ·8W´ø‘¡Cu¸“f}‹(;Ç?žonŽÚzx뮑mÁKì)2üê žìô2hZ0ÈÉ!3«÷ÈíùWÅ×ÐÔ1ÕÛˆ®ãuîômEB“—Hû£º%=Ìí{Ï.x~tÿ2½¥Úøoz•ñÖöû|!:Ã’Ú4&ù‹D¼ú7ͽKš‡a+Ñ=ýi¯´ ¡ÉK¤^¶ÑÏá3NVçÏnx=›ÄùL'Â@4$@’ ¾ØöËÇ!)žlÀÏ…öyxn1ú3g1Æ|‡î—‘øåY׌¢p­ÿ{òWà7šÔé\“Ÿ7¾Û˜zÝ´'óËß=™„Á}Ijÿ‰ó.áóÁZ¼ºj 7Ɋʯ#ý jßu~·&ñ¼ù[Xþ½Ê¡ö®„nTkr&ö ¹ë‡¤¿ƒnÖ3æcv}î:ÿŽæ_AØò±¼çîtÐBOÊž#Ö‰óà­ž0ljQ]ã¶3ΜÅxn/Æ)ÜžñÕsxÚqââ0œýCûs$¾ºèôLF{dójóç”HÒÿ¬ïëy‰LUØTиÈoAö)êü.ä\Š€?¯ÁÑ«dEý I‘p΀ªœé€³áÀ¯šñQ°ðgÔµr§27Ï€;°å$ª¦ —&Ü9ø'ÐÅ’þä|ó©C¿ƒGC¥RÜdÿøˆünƒïŽ™Çç ."áfd&BøZø³•+ªü¢Ú¿0ñcûÅ?Œ¨H„$‹Ã“ÈÁ`Jœþ‹|ø^B·{ªCŸ|)Añ­0þuÖˆ¦@S¼Iz{2\Lô«ðÙLRéföÿ‡+ ¸X€ýœÙ·@û=‰ªcI“æBBDÍ%c£ÅEœ+ú™SVG¨ +ÞYÝà—|®7.“A"…j¿m8‰‘P·3^ýï.GHßlaíqž3©¶} £÷™Rí,×úßö‰P«#ꉣ~áY[,äO‹Æð|©\‘?"ñ_6'VÉ„›WÉâ6¨”yóRÝ+ÌÇ}?–Ô ú¿LƆ¥nzxQøM†îµHpÚì·÷{ ͈RÛ‡;ÑÍ™|'pÆ¿#((ÞÜY4Ne×aÒzrB»?å³wµ‹üÄoßEêÃ=Aµ²[ ®EÖ^›ã†$Où¯ÅÕýM‹ê¿HÖ9¥…¾âu÷K²÷¤[]µ§~ñënÁC•aègè¾NR—ûü¦æE¼Ú\'e²eR{ÞœNæ¢Õ_W iHüzé k@Ž‹ó×%û¸?Jé¦e¨»w‘¼¿3@QÊT_ö9ø|þ« ñÓ€äÂ%7:²®Þ0¼“ÞZÀŒ7á:P»~æ‘òÓUà*LßAvwŠÇ?J>õ$óÂïæ·“MãsëW“¾i&þ¡a$8»Ö¯ŧ^QåÕþ…‰YI÷VÓÉù‹¤‘/’}Ò^q7~(ì(¤°^"Ù9–ÔŸÁãOC›öø­…,ëNÚŒ_\‹o®úWfÜ5-õe)sý+ .Þ†jø„·9]]$¾´éüº)”ôµØÏ™} ²_`uJiΑö«Eß§/"y?^1º _Á+NÁ°|¾?!㻫Vfb0½/ÓOΈŠ[VB¶7 ‚›W`ß2>Âû7­«ßjFBóÇHhÑ’„qû,²ç?]ì?âÿ„CKÉúüÚ±6òßyŒ„FHhÔˆÄ1ìojOR§‡€r¹ò÷cÏ!AåÝôî¢òëhÒ:iß° Ú‡Ûc8eºbÙ9œRÞ­1Äœ@U¬­Nøw¨gwåOn]€›7Í%ÅzìòÛ ¸q¦#ãéÙù~õ$×/AJ2\†&“ü:Ц÷9èÕßKkáŠÍ÷g“ùDâk×!¡Qgä°uÖ ëåpwøµ´OQçwqâÞ#ž–ç—°êS¼OŽ#¹A-â«W#¾÷:D¡p+±È·¥°ÂôRaþû®Å}ÄŒŒ’óHœ°½è=¿/$½Ñã$Ï;Aðßø3êvêâS¯Hò‹Ãþ…ˆ‰¦Cíd¢¼r®"~Äß$}®Ý»õˆ’TV1*l€ÏÞ uÀZ”¯t#¾¹… µRež Sº‘±øjÕ¬KH[ÈËMÏ~ͦH^ð³}ûhßBØOasΙ~ާ˜¿ ¿ßÆ’öe´MÝ)²/øãÓÈ`þªQcT¿E¹ØÞAÓÚx½¤@B<ܺ iîöoùä§BJJn'òë<’+ßÑ#=µª¡øóŠ3ª¨ü´Xè¤}øÛ$4jBB³f¹eð*2î'á…6dÿäŠüBB«uQÿ8²~× ©hq_¨LÔÜrì¼ùøwsþåC쾌o«:Ъ1Ù?í-ÂÀoÀ7çÐïW`"¥PùßϬ¡<ô©EúêÐüÒä]Û¡:y¨ðüZÙ§þ{¿àý(^¥O’þµÅ3y5«»•8F Ü‚`àôRkô… ä®D l‹Ê¥N á$â!,&¼BòÂÛh;6{0ä‹ý‹?LÏHfÞ-düH‰&=¹šz×Á+5šlG×7\&[ñPþmeŠŠ¬,2³2ïý}÷.p+šôô:Î/³®ÀžµØÏ}ÙïæîfV‡š–ñ©²¹>ÞEýò'NÞ0c#ú M¤Î;¹å^P¿ ËORjè§PCU»¢^‹ôåß»Ö^ÑÆõ‡&5¡\hó:ê?Cöží.ZÃIÿ%-Ÿ†ðAhå«@·Ïñ}#ˆ”ïB]ÌEä7þcçápgíÅœ0¦¤Àï ˆ¤Á…å›Rô€@ B¡ô…Ze À×MþÉ¿ ßÿŠïè©P] Æð~7”6¸È¿3~]ÄO{ÉiÕekiav¨j–\îï\Âíà!ÐÈÌŸ¢LåÜd²L ó6y ëÈ}/Î"¡íW¹B’Xv.~ßÂKq•”ï‘þ}’kíå/xh¾ÓGᤅċ¤nKògá®s_Pÿ%-Ÿ¨ø2Ú…ï¡Ñ*Èùý8·?î†lNsQxQù-ªþÅ€cñÿq(ÓÜ4n<™‘Ä?Úîÿßõ#Ñ8 ¿U‡ñòMánØ|’Fïv‘øuWv“fØŒA½’„sù¯54]gcìjñUv ñ-ºÀ-{Â"‘ ÙèßêAbß“€Ú ?ÁxAî&‘FRßq÷wÅà•—Pÿü±u°Ìƒ¦*ÞŸ|ˆwo²Ï$yðóåêüuÁ?Jz~ Çᡨ§ÂO“LöÅ}¤-ÞNνü>¶ÂXGuãy ãñ úÙçÓk“¾|ÛN/"»Ë¢{fOuÉ¿œùÇÿ.ð“iDóÞ|«•Ey÷R7#í›?ùÎìÿ7ˆ3z“àûÚï¢QÜàΆ·IýòŒ©ò ÷Åë­¯ñ©„2óéW’ÐkQôss»xg*>³Â;²~!uãWæ:ÎìW`½ öן¤Y_£‹ANÌ*’WC𿏮ŸÍÍ2OñOùÇ”¦¢8¾](ÿ7C‘öqz€Çßìcñ ["ñÜ 'á·ìShèm}ÌÜÓÖýŸ?1.ï=s!pðlîwçÎbˆŽÀwÁ;poµgPÇD¢il!¯îücæ[÷ñôÛ¨·Bsÿm‹¡S9÷9Ì>K|›j$ Üdáí]М>Ÿ_ÿ“ÍÇ<‹v ‹9þð6ÔÛºçßNýÃÙüx½6ñ­¦’^„—´Ûq"îGÃήm›á¨B÷¢;…nçbh£uÍ¿\±_Qø››_vÞ‹^ç¶Í=F¥B­Ñ8—u>ŒÔðßJîÌUþK ùâÇçÐLg?âŠ?¨k!¯Æ»%?

bžPS/Tï&ªQâÝKçzû®ý„æu„ å…E} V|‡–w}…ùû÷ëg4Õû ½^Zׂ )¿Àñ!L>)ÚOžsK€ÞýLü¼ÐJ©.´ë&¿Å ^éµTüV­ÿØ%¢ðsc‹Z½…AÝ„žSE»\ð²©/ª}O퉭¢èÞT¨PQhôŒPùwaþT¼º‰Wì~¡µQ¨P[4W´1ûEÑÜâ˜yqb˜ÑY(hÖÁèeîÿ`¬x ¬,hý…ò „YÄðÃ`Sý3¢Ž‰e} yu'ˆÌ|óçvsÄ?j‹Ð¹Ž` tF¶-¾}ŒŒz–¦FˆnZ+S}EasŒh§¿(ø©„ŠD±ã¸{þíÌ?,çgí>¢v4?ÿ©û8u_"ÚuU¹Ÿ+¼$ª°-B°‹þU ýŠÈŸÆh’[N˜%†…=Í}y! ÆŠ¶ŸÎ³ÿKñcžhcö‹W+‹cæÆˆÿ—,lè~ü ®…¼ï–@üðŠ“ÞÙ/†U=MŸ…ͱâ3Àhq^+úmC\lo§ Û!úe]]T¶lþþûoãö¡Å$ß…ñM>)~#‚‹Ï…æWQ8ýAÐ÷¯Mㄦï‹_ÌAcG/•¯ ,@ïòÃÅ/ÆN`/ÿ^Âò“âÝKëøwùw6ÿl_ÌnëDmì1lèoþ<ÿ´?¬í81>#šNßÕÿDt±³>«Ä©¦°?V¼ûéïÓ˜u…‡EÑÄôÙ¿·xÇlÊZÓi‘è· rßícg~öu0¿K"qj0Qt;‰*ôèöÎ^z[|ŽD‹ÿ’>TŽÕšÝâ'ú£»Åkü³6XNý¹ób<^ ó;æï{~´ø¼ÓCX°MüOD‰.ôÁßTçÓU4{¦æoóâñS¹þec¿âÜsÒ ÑçÛÐKX#¾ÃÛ 3~]LŒø¯}_PÛçǸðY;²„ +D{£Ãš(6 ,ÚØ|ºÉs”u.‘žô<Êuûñ?¼ß/ûç¾—èÞí µ5ÿ}BÜà·ØANÍ6w¶…œœÇw¾• ¼¼ò&‹/¼ðŠó§\ë«|[üÏ‘¾-±ˆ‹È~( ço­oÞ¶"G-¿Ì´~uHf&Šªõ\çWéE©×Ö¡‹Ãp`šþ5óù×Ý(‹ùy2¶àøQ„ñ«KÙ©*Eö¿útñiŒÒ¹Ói8)•‡ É{¥U =gÉ-ë“Øþ2ªŒö‹÷]y—ÄêÕˆÿ脃¾Ux¿ÐÞ~ŽäG[‘t³Æ™ÚwïƒzÝÌÜ¿«÷F¹f~¡³ ä.Ê2AÖbJ¹ðÖU»ö³xÖ©(¯'vøFy5¥ú¾‚jãp’:½A²êôïÕÊÏχÇìïsÓ}ú–§Ii߀„FIs |ÜÐË%ù¿Ò¹ -_$uÁIðuC~iÈœ7ÜÚþãÜÜ ÔòEfŽ[Äw¡†ç›£¸maS‡Óî^üÈü©ðñC³ÈÙ¤óO`p‡;í‘<ª›¶]‡k¤ |’„Ö½H»ÒÝ’ìÛÔY‘Ö‚8«°ã–üO^†.`+IÏ´&ñ?·ñêßÔš•aÑu¼Î¾­Hhò2)×:bø¦ç}xÆÉ†žxuºE‚0Óù: ¤‚/ö£ö¤'“M€ýÉéß ¯ç“HXfý}×åèÏœÅxb!Ú¸‘ôåY×´õ´êßúÓp;>·?‹ãž[œ+?j~q£I~ÁMùjø2ýô§ Í4¾¼Çv~FÂà¾$µÄy—ðy-þ½…c?°^Ïß&qþ«þ%ɾ G7íqH²3>GðõGCé·¼Mú?™¿}×YøÝšLvd nßõÂ’\þ£W ýÅ þ}Ê¢ö®„nTkr&õ&¹÷§¤U| ÃŒ¶æcöþ‡„¡ýÌü°¯n:ûò‚úámÉoapë:YŠ ÈsÊA?Z?ÿ±ý¼‡ Gƒ×”h qqÎžÂØþ‰¯.r1qz W /¯è»Ù3â6ƒ[)ÜETÎEé×Ù²Öü99’ô?kàûz}ó3¯¶•ÁþÉÍ¿›õüû‚ø!ýHêô" ß\ÆëÝ5h{ú[Íïœx L=ˆ~ÆÓð§ãøQhd "dÙyÆ'çR\¹'¯!1pëò« 0ðãl8ð+¤fA|, GóXK;‰……ÊÞ‰,‡ÌÍsLïKmQH]SâÙ$€;û¯­Q.êOÎ7¯‘:ô;xô1T*U>9N‚ ìˆrËjûÕ/EùéˆÃ';dÇ4²ü—†eGQÔoèºÜ‡ËÁ…H¸™™ p`ç£P—Gqé(\M…äk¶.¹Ñ~Ët;maÿŸQ?ÖÊ=,/Dÿºa?rcûÉ•& IDATˆf°MüØ6ÔòA)´3âLñã íÏ‘8pažQdL¦ébŠÙ×óâǺ{ñCYÁEÝ€?¯ÁÑ«dEý I‘p΀ª@S¼Iz{2œ¹‰áÓ¯Hy¤›ùübeßD_ :²¿ú|þgÉÿ“¨:f‘4i.\O‚#³ÈØhqf©ßÅH¿ Ï!¹vW×Ο…ÌmP–þ fu#'Ú¼ÚrofÜøƒlÕmó,±õsUCXñÌê¶ JFñTyõÄãÛýi¡7ÍõÓ¢1’ ×/“¡JÉq-Op ™VRô»¦Üò.¤›^>œ!懰+w…ãߨ ?Mn\ùíw;gßSÝ6±ÈºzÃü1-ñÖ~`¼ ׆íñ¿4Ÿ¤Ÿ®æÎñ»î û‰‹£äÇû¼[ÿIÊ.J”‚ä”ÄÅQwÏZd" ·MãsëW“¾i&þ¡û¸q”¬ßClz!VÄ|¿a 雦¡[÷,IûÀ®µðk†ërƒ_€OÞÄ¿±ÙþrÁÍÌNìü­°H¬mãGúM«¹{/~`‡gõ垃ÏFØðošoÕ)¥9GÚ¯}ÿrÉûñ‰+ú•ìŠSeX¾ߟ‡ñÝU‹óé21€˜þ w¾<~:TܲQª:,›‡øPëö÷×.á¥d}C© ¶®~ç15"¡qsÞ³š`æþû’þŸãàóPnÿwlgüŸ&ù§ÐŽ}Ó¾üFHxËü/{’:õ˜Y¾#Òã.SÚÝ_-T„sÐE %Ý’Sÿ ]6LíÉi§@g,¸«Io¡ÿúpç?QÖí¶Aûp{ §LW4[†PÊ»5†˜¨ž)Δ ·.þdN‰ÉÏ¿CýãÉÊI'ó’…#]»Læ?[ÄžCJ—·~¹ó÷à`î,¹Z4)]µüEnj:SÞº7ošKb¦E–%ÈõK’ —Áøi$?ü:Ц¹gr±{0eåWÿ “²`È º pæ² «{A¿ªêÕßKëò_Ÿ]Læuˆ¯]‡„FÉ<˜ƒ:ë†M+àgì!ê4ÙåòÏÏi}H›z  p}~'$ÛÖõ`éGøžGrƒZÄW¯F|ï ˆ½ÛV"ˆÝU™ë“X–˜d+@iº}*ŠÜÄÍÒlW¯ò’G·0»÷Äçâ:²Ós2KŽ8Û„Ì;‚¿/$½Ñã$sˆ¬Òð=€ºÚ}5ñòÛ|î4mOÒÂCPæYü7þì†üºðÝ'x[Ù¢*ÂÒ]`Pnü°ÌlãGŠÍŠ^^ü¸ M&9Ø?$7~XÑ­V勪{w}ÓáÜõÂűIƦ`Ÿ±øjÕ¬KH[sɳ#Ô)?ÿÖׇ•Ÿæç_¡pñüã‚~%—8U„…+ñûm,i_Ú."û‚?> æ¯?Šê·(‹c`Á2t—ß%yê)×,äçoóU*¤¤@r¤9é¿aýÜþ šZ­}ù))6';ò5¶Ÿ jUFq㊔ÀüÕø_KÒä(üºÐ¿½`pžì‹¡m`¿}øÛ$4jBB³f$´~’„ákɸ{œ„Úý“(/û+î^ùÝ«Wäç@áe§íi²~× ©d±@Åj¨¹Ž.JkUCñç6ów þ¼ggþm[¡:—šæ$R(…Ê ì»^(-oKùø¡Ì¸m |a¤Þ Á»½Ÿ ·LŠ‚òЧë68¿UórG4чmøýÝåwIu…ßZÕPüy¥ÿjT°•hîdí_C¼O6Û♼šUíÛ  s©½•RàFQa¤Ôè ¡ïã¹Ï¤Y)—ãÄ~5¹º¾€ÛEò›´âa÷*דäE·ÑvlVˆ§ uv~ãû¼ä=û˜k2ýêàUú$é_[Ú¿zQžƒgš£>÷sd ’?ÉÉ(…Ú*~ø¢²Š5ñm§/!³E“ž^Çtèľa¡0á’Ú³¯ƒz}Âù¿y†»™ÕÁò±ÉÊæz—õ+öÄI ³¾ÇPz#©óÎ@``n¹—×Ü„å')5ôS¨a„Ú}P­IúòïóÖaÆFôe6ôÍ%(kjo4-`ÜhY*U‚6¯£þ÷S°w‡‹êZö¯ƒª]Q ¯eî_ÑÆõ‡¦u J xz0ê?CvØN÷å×.µ{¢f!Ÿ†ðAhå«@)ø½@ú²P囸 Ú@ò‚KP¡œ]~½O„ºAPÝf|÷à¡1vÿ VÅ <)·}H7›öbNãoÀåDDÒàBŠ=¬:N@è@;cð…re!$…ÒjU„ ¦¡ª|0šÖ‚ áñA¨ß’ì=Ûóë¿ö†õ¶ú_5¿â;j2Ô „²Mà­Î¨n2%¸áÃФf.ÿÝ>Ç÷ R¾ 5ëöÕj ¥7¼ð7†²Aüº½ ¢Ê׆AsÐT“6m™Mî[ÕìP&À*“T”©œ›,–©cGñ× ²ŽDAxþ£F@°‚[ÁÈv(Žì¿—825‚Rï-…µ!¨t~‚RJŠ÷’!}Ñ%mKBþºú/A«Z\†.BÛ;‹”YÍüÎ\‰¡ô’œ³ˆy‰BckÿÈg ÿ 1Å7jÚ™ßÿ#¤ž'#¥^mLaQÛåëuÌ·À­®[ÔùW‰L!U©vpR?t¿ö5½d¿uõܽøïžƒ:)ŠÌ”LsŒ¨ Õíø—­ý6%¸—Ã?è8ZV?(ßZ—&ã× îËQ(PØKiòäkØZÉ>çâ­¶Ôsd¦TGikŠ?ú*HýÏwÇ­ƒøQƼm?þ‹í葹ñ£bkÕ"-âÇ´HTc¡ìXüйU1Æ#È·7ÑMý êÁ/š¾Ÿö³Ã?`lmlì[P}ây'ü {‡Ý„7Àà †¡y^ïX?/=4í“û4¶Y¶h„ò™2(¼ßÄxÀ|{E.Î"¡íW¹B’Xv.~ߤ”òé+†ö}Ò½À©l_¥÷›w[ÜžùmñOOù Šï”Qx•öƒÄ‹¤þð> Ÿºñðî½þ᥸JêòÁ¤çõ/ÁCCð6 ¯ -ФK¤nKògÝ”?¿•{)¥¸JÊÒAfù¤@Å—Ñ.|VAÎï'¸óy/26¤¹(<Ÿa÷ sÌoùh—íCÃR¾³ì߬~…„Òóð[¶RŠ«¤-s³½ÓÕì±øoìÆ4·k÷Bf$ñû@êuð„ï´‘xisí»u,ÉŸ…».i? ³Ñ~·w2aóH—ø&Ap7´ß¼kâÿ8·?î†lÎãÿQ”mËæÎ_Ëùwqñyü:½”xãÒH$õ§ö’òÚóp$ÛêĨé:cWË«û’žêBö/´~‚ñ‚ÜM"'îIÞ5/ÇêCÒÔOñß:µ*žÌŸæ’ø–EbÿÃ`’2ßG󝥸UÖ¢¸v’Ä #sŸ).¼ÒMÄ'`oZhªâýɇxWðF.„“2ü%²ïýjëQ”íÊ£ð~ãž7,æïlÚΰãǹ=ÑÒ>þ»:7~ÜY>˜;Å9?‹„CäŒÙŸÂÿ“d²/î#mñvrîÅÝ@Ørc¼Dq6Æó@Æ!âôË}L¡@åh‰bíbÒ÷O€ïúÂɹdu™K²©*ùÞ Ý;øo}5¿5êc^}å%Ô?bÑèA-?1žÿÚšŸL#š÷–à[­,ªŒ«ÜÙ<Ž´oþtQ~EØúÆG”Žù·¯¼û©›Þ#iÎeåC†‡¢žz?…ý_ug)Â^ü§ñ#¾Er*µŽ™1a$õgŠY0z S&à¿u8ju<™»¿&qŒEüØü‰2¯Ñßá;îE‘6iLñŹ]H¼3ŸÙ?áòG,)fšoÜäã߯¾Ö‡##BQO±áß2ïן¤Y_£ûyŠØÕ$®<†4Íq¨_Îå8înIz ÌvÏfVžâ)ÿ¨bÚÇ(øo<†ê˜Í>ß°¥¢lWݼú)WÄ}œ<ÅSþeÐVÑåÛo¬ä‹ç]uxðƒ¨†agÆÅ·wÕ)aþI ac(¥,ìOíÔ@ {Àñù˜´¾kÈyþ ´‘Q¢~ÆgT­bìÀFcø©(üyàÁŠr-¡¶éö\Ù({’¼)âaÅc <ðÀ<ðàFÍ¡x͈o%Ф_Iž?–¬ïÎx'<ðÀ<ðÀƒž[uxàxàžÄÉ<ðÀ<ðÀ“8yࢊàèÉ"æwøÛiÿÌìý=ÁÑÈh\( õù üGOFph^ÉŒ_Óu>ûg<ùåÏ1Ÿ±‡]}þçü<Èü{ðÿ0qªôâh¦­ZÏžÃûÙ·c1“Ô±ÙfLIíASY³o/÷¯búÐzVõÎÚ‡ üœE›·qàèöïZƒ¢vSå‚ú/iùõ> ãÈÉÜ“ÒÑ“=¾…Q!®K/*¿EÕß :ÎÙÅ‘#ÓiçNå:ðÖ—sÝ»Ÿ#G¾¢“ ¹Eç¼[fÊÚ-ì cצY¼^×uþó[0šLÞg–m*áSšçV*Ú1óÈ!Âçw2¿ñA×o#æÐÙô®3°iôx(C*å5ÐÒrÌW¬ß¿—ð¡ÌÛÆ­ý9]C*’už¯;¶ Å{¼(Ú1óHD¾ñ9ø>õòŽñkÊÐY+Ùux?ö,gÊ únù·sÿp>?ñMÚÎá׬"P}¯;¶ å›;K$pfþÆúý ÕÖ§Å&- eWøvn˜ÆÐ>.ûÔ çç‹ø!ü‡¶°|j_j«Š‡?{sÿžLk}ßøyä—þñŰڟ ²ô›1Núvl$U+–•Jm†Èüýûdi_ã½c4/Ì’}»'K—êZñ®ü¬LÚ±Gæ¿äçrûê»Ê“MjH¹rAR¡I™²cŸ¬äòþ ÷_òòë}´[6Ð\ŒF½èŒzÑ´nìÿPT~‹®^ñznª|»pžl‹œ.íÜÙG¦FÚ¿“´|i¼lŽüJ:©­ë‹Ê? Þ‘M—Êû]êK…re¥BýVÒÈb?ž‚ùwίӢ҉Î$ÏÎØ+á_u–@£^´ycT´“™GÂ%2ò[ée0}§ë&ßFÌ‘Î^¹Ÿ½{,’ð #¤q I?£^tß{òË]!á['H‡êzÑVyZ>Ø´OÖ¨TÌûŒÔ—¬eóשôzƒäɉ[d÷¤&¦z½ô\¹W6ÞA*ø(DY¾¼»~—Ìïêº;ó—çw`_Yþ‘4.æñÿ¯‹¶Ë Ù´l„´® 0í·ÔQ&m]*ý˹æ_-¦ì”°…}%ÄÁ·ºôš»CöÍx²xøSéLó"P:|¹çÞü×õ¢ÕäóøŒ=² «gÿ Â–D|ñœPaÄz _ÜYÔ¦ÀÚkÕ>YÒÛÿ^½ú¥ùrpm1¸Ô> x}¥œßÑEeÝ￸å×ûh·„.Sl(*¿…âǯ“L_1LBê ’ÐÃn&N÷6( kçìEã_-æï–ùfwùw6ÿ÷&'6´þ^ÑNfF¬)3VÊÚa¶ƒ«{‰Î®Ü½kŸÌìhñÝ“_Èþ½c¥Î}Jœlõµ}«¼ßÀôÙë9™wx¹ô ´8¦í—r`u/Ñ»ÈoÁöqc~—DâT÷Ù¹áK™´t‹ìÙ:Iº>7Dï “ms_”Ó1ŠÆ¯Ê¤oWËŽðýrpßj™5ö)kÝ—ƒ'"äèÉ ·“°<>#LxN^Ÿ¹\¶Ü#Û— —†ª<~;ÊÌÍJ[}Ÿ!k†•wÁ¿‚eÈÖ½2£åü™,·¿!ÁÅÊRëÃ]ùç¿)qZ4¨¼8y™ì:f=>ø½<>v¶lØ¿OÂm“ußNvnèVÂòÙ¿@ûþÝã‹ þ éÿ‰,ݹGÂo“UÓûK•;ü\ïÔÿ¨$ݦ¬Ý‡Ãä‡Å#乡_ÉþÏŠÒeýîÓ˜~Z/2nÜ0m©^›úUS8u¼×ÖÉS$V¬K—ÚÛÀ«Ýž*ÇõèXÇÜì¿Dä«(ßw1{"÷³wÛ|Æw¯\¤å¾ÂñëËK˱¢;~nëïCÇ)½¹úå\Î$:ډ‘|7á6ÿÒü‘ß9v»–­gÇžµ,üäE*YãÿοÂÝ$ êû $<׋Úî6õ}„Gt׉±¤#.–+¾µ¨[Bïå,ø¾ÁË<«ØÍú¼wìª5(É Ã’¬ŒLTU!Äe~ ²ûñ£¸¡ È&üטq±cû¤3ñù·«ð /›Ô|H/[ô½Û>Åã/NäpÕwX2¦ŠYÀ±Oióh ZLŒr0§T<Ò¹ ç?ìÇsm:ñåÍg™4ºj®ù»¾„qÃ|ŽTìÂøoW²fÙ$׸‹oPi´¿Ì‘cÉ„<ó$¹oÿô¢Õ35¹µw?—ïÛ¤QS£GWÖá…gG±^ñŸšÆç ?Ê.Ÿ1©Ù>{éiZµéÉÈÇIrç^z ËwjÿìûOˆ/Îü#èõ%Ìnûß ~žV¿ÊÌ+O1cîsx»È¿³zgüןð5oìbT‡v¼<ý6z?jõ.zgú•Ø3NÖQñY†wJ!ôÛ#¦¡‰ÜJRÐøãõlžØ nÇ“€@Úç¡Ý4çHälÚþ2ž¾®¾KÈÕþKPþï?ÍâÝ‘ÃéõbF/þƒzo/dVgŸÂ±_T~ ¡¿WçÏx-~ÿ)ÉÒ–M¥KÓãfÄLL×¾Ÿsèá7Y4¥Õ½CÜâßÑü+ê½ísùþÌãôvðØ‡ºöh~²xá^ª7`ä6I‰'&„ƒáþçM-{>Aê9›÷EÚ1ŽÝ¨Æ ýó^?Lï>Q*uU®ñ[ }Š:¿‹Y—qðæM"NüIRL$¿¥œä¿tT(—[ŸôÓR6ºHÂ$ñ4«–þ}Ófn<'’Ãõ¾e_jîI0l{,Õ­‡ÐàQ#G#nuywnwÒ—ŽaÀ¨5dÖoŒ^éÚÙýäǃYPj {í'üØ>xè[^›zú¾Î›”ÝsY|ôéñ1,Z} Óø\A™ Apé8Çâs ;…k‘Û8r³øt+ª|çöwlßB|)Ø?ê3è•R¬ùp6‡~¿782í"kw¦£kü;«/˜ÿætoŸÅ¦)Ë8”Mzì2fþh±¥sýŠïòÁ!ª2xáhæbîÙüE¥üu+ܶzPÊåö»ÇÒá¿ 5žaÄ„w˜Ø©ïnO½WÝhRóž+@úÎ1<1îˆ{ý— ü¤Ÿ7‘÷ÊÚ›>eTéGØÐûy´?¬½÷W×P~ÓØØÿq6ðØšÝö>™24•¯_<êD7gòÀ ÿ¡¥¢g—ý›±Y@˜ÃµKWÌ“SLãË}Ç®3\Û¼³«'²uéÏì=t‚#a[øù\v±iWdùNíïȾ®ÿ/ù‡¾6!ºÊtÝÁ›V ]æ÷ÒÀïÎùwjŸ‚ø7V¡’úû-âÍù_~'«†éƒ ú•pâT–îógðøáwè·ú†Å²} è ÐÁ¯sF0  ÊÓHàÖÚ[8_Zâ-Òþ»–qSë±ü@*oŸÃ%Síñ ÏòĹW`Ù©w ÑIË7ãúéKät ¢4¸‘8•_pÖ¾YsšUx‚f‡ö›5 L<¼‹'ƶgüâ ®ñïXÿ$’ä.‰¿Y,Â_»Ê EJû½è`Ÿgó¯pd-»&¼ÍË5ÃòþîMÎÝL$ɶ"1DB§·8 ë èH$!áþæMÞÝ^¦Ño[ͦââÞ~nùsÈH~ȾÉõwüÛ}Š:¿K ‚é—}•¶ø}êý0šGœÎ}£zãÙ÷o¥[s?Gò_€…R…*oa)Ãâ4{7×Ní-9$€¯,çÔ5€ã|÷ùtY;v“ßb÷}É>sÈÎ;ãsWV3èñ¨ÚúqZ=ñ,ï¯Dô{Ï3n_1½W¯Hò]±¿#ûþƒã‹˜‡¨Ì8Ä{-Þæ§Âò_`}!üϦʩ~Å;éé8c>=þ˜H¿Y¶ï€‰ãÔE-õjÍ® ë¡ÿ=Ž8—ÚÛ[eP ðó³y–&ôÔ;¤§Þ!Óíþï¯üÀJ(o\ÇõÓsQù-ÀtÎÚù„'Ûtà™§;òÌÓi7r=×2N1«W;IS1mñe—GòÏwYÃÃ[Ô•)GiIàfª«ü»9ÿ 3¬ÚìÇ˽RÊÕÀ™Çéä2Ôµ| ç‘:THû…˜Äû™)ѽ{uâÖmuºŠRýù'x(ú' ɯµ} á¿÷^u©Ú¦  xW«‚¦XNŒw¸z#€‡ÎäHõnt/àM›¾óK—¹ztj¬Wÿ²³ÉVøàçÃßÉ\øyË?ÎïRiÙöÑC~‰Úÿ_á—»!4mXTþÔ;ã?þ—²*S«†YR•ÁæÕ—õ+öÄICËÉßò^陼ø<:£Qÿ½³^"ë¿¡Ê«ð\?¼*uäã!5ˆý~#ñ.µoDÿ1ÝhÝ  eË”¡R‹žL~¯ i{Ãpíñagý—´ü:ôz«­T&¨lê¼ð>_½Všÿ®ÜìâRiQù̓//-Ûgçámçí3M czêÒþH"Cîpó’íå¾#ù¹ð1èÑW ¤”²5è ^nÚבülXžúoþ›U|PékÑû­çð9ô#]âß¿.@¥Cg "X¯@©{˜@£­ƒuÙ›ßo ©åãTWº ×l¸FÓáãiWU_å§ø`Ôcüµ!”Øû“«¾L·‡£Ø¸ÅÎ2OŽtj^•2å*ѸßLï!lš»ÛÅùëÌ>®Îïÿ2~ãRjUkiúì׆÷ú‡ *&ñÇ"¯Òìé*ÀI>'‚G§…²eÃ4ZÝŽãJZ–sÿÊŠäç AtÛ“Ú5J}Mº}‘ò¿…s0íï‘2éŸ@ç¦ðSú¼ÐÒÈg{0ä—°ýÿþñ%ŠÅ+è4i"/ÔÖ¡PûP÷^ß• .ò_`½Sþ°n—šßëGˆ¨Cz3²“ÎbÁϹ~Å“5 ýÓåð/õ*ßlÕ|Qóû"º¼°˜?ÌÍ£Qæ >Zº¸Æ‘Õo1lCª‹ío’¨íÇ_ ¢bi?HüãÛ'ÒwÚQ—.¸ÿ’–ŸLNðó¼7k$Aþ Rþˆfׯ2u»«w·‹ÊoQõ/TÆÚõý(græá+·1<ë8·ÎEç?eõ0F>cÜ·?ò‘w ç|Ç›ït‘çü:C“O73¿SÞ/²# îîü­Þ‹´£ìFVEŽà‹–nÜI˜û&ïûNdÌÒÍ|¬ø‹S[>æõ¯/ÝדW­^1þ;ìý,I]‘>ÍeK‘z)’u£ú³(ÆÕùëÜ?J|~ 1L}{33&ýÀu27?Ä¢ea<ñŠyµ­×ê­¼S[}£'Œ#Lh9šíN~º™½i%§·½M·å#Xæ{>èó½›þu‹Õ¯¿…ßg£™þãpô$qñðfF¿º‚[FZ䔟´L#ÏŒùŠÑUƒð½û''øŒáßÝ|@ä;³Ññw/× `PÚxÞÿO(ïBÂåÓDl]ÄÕ¼E/'ü\ïœÿS“F²`êgÌ {•ôØ Ì]E£FÙ丨_q³™•§xÊ?ª<¸@º\þ¡`Òø-Yµuº lUÖ¼wÍÇŸ§xÊߣèú-“=vö+éâyWüã äP‘76ïgßWÏüí´rÆn· ¡š"»èãŸÕ®X*^ôûuÓé3xê&±.lmeňšxð€¢RsÚ„˜ž‘ÔµàÍnzn;yßÕPp ìxàxP(ÔÈìOºR7X2é,a‹'ññšKžÄÉ<ðÀ<ðàA…çVxàxàLÅJî IDAT'qòÀ<ðÀ<ð$Nxà,8ÓˆÌ+HY¥)Þ}hîz-o@æ•Fdž/G‡BIP³ðlã¿ÿ€’–iÈÚÓ‚¿ÎÅ,»ë’úìëçü8m¿ª$,QQR{G_­Aâ•ÇÑ=x0§†˳yg®Ÿk@±¬£Äöý€-FW$*ºI1!l}׺¾^¿rlÞY›ëçpëhuV ÇêW-MFsà`]kHâ±ê„¾­"ÈM¥-ûßüvÁò׎?7å·[•èûòŸœÙˆŒ+¹'¥Ì+ȼ\‡iu]—]·w ›vÔqÈy| ¹[“mï+ÑŠŸÜö?ŒÅᯊ¬n@ÆÅŠôv'þTôbÆ‚ÊÄD7 ãb%úÛ¼vÚŠÿã5ØðŽ­›úëŸ `}X=/ÔçêÊLhl®k=½a>þ§ÖµæwÃŽÚòk¦kÀ2{/ÖlQ–›Ñ4µøjJd’ÒÌê|íÏú ¹ze\nH≶LPQ.¯^åÅ‘êƒ1ï»rFÂÏVf°_qºµÊìT>mum‹×y˜úϼb]2N—áÞ°u &- áê¹$ž¬Áú·V/¸mlìñkÏ?¦ÚøG«·«ÓЮ|ß/ M“³œÊ†Âý.LY)|ü˜Íø‹É17Y–Ia^bxJÇŠ-µ¸úK}®ì«È¤§,*«ë˜µ¸ê=ÿ`;/,ì“eŸ¿5ý¢Ð´þƒè…[‰]Û9 òͼ’ö­{IJJô îÊ.±hoÇÞdáîëWƒ_­Ï·vãGynÅñ¸øÑÜ&áw%~¤„úšã‡Ñð³•yÝ×± oÏ7¥¯^þ¹í×zqo£y£?ág+3Üâ$ØÎÀú]uH8WŸßöTdJg<°«ý Æ--/tWJ£êHýF ‹i ‡ß0×û÷ª,·ŽWÁµ} oYy¤|Õ\ÿþÒròA¥4¬ŠÔmg°˜aѾQ?éÖ©ŒÔl¥•uGJô[®ïŸ íiÛ} ë[ürl¤ò{U•„Á2²¾}ùOÎl$ç¿PJ… ¤|y¤r0¢vcÿ‡w—”“zj¤i ~†ØŽ/XF6DÊÔö“•GêËÞ~îó3¤.R¶®NVÙèŸWt=«HÄúêòÛùŠÒ[åÆu|eÒÞò\Ÿrrö|%éïm]oÉݧdýÑr|„ò›É™Ó5dѵ4¨‚Ôj¢’g*™ë[Oo(¦ª¥J9¤lPnQZòûmÛM-Ík# ìð[P¸®„õÉÿ½ºGeIÜâ+ú¼ïª$jS€|ZS&×¶8Ví/ëÏÕ‘í nè- ÷7óMõ*/ùábCI¿P]F™¾+k”ð³•e¸±8÷ÑÈÂÓ5å“êùë|s9+_©\é>³Žüù•¹~Ô¶rv¶Fjj¿ª¾2o9`éßKÊʸi^ ©g‡ß'g6’ SrýÞ}´=+Ë­¹ó;ÀŽÝ+åü娙rÒ¶Pã÷‘…q!2.øÁÛw&¨W%9³5@ºT6}ì%+ÃkÈûMŸëdÊ(?éÒ7׿øY·¿gD¢•¹ûêI˜½øP9@Ž)'ÝÑÏ )[.w^ô_X_’—zK¥ ¤bE¤¢î1]—Ô—}ý<ûÙ+Bëÿ_{çU±þñÏ)›J¤JHÁ†í6š¨(r±£âõŠW®åÚ»^±"*ˆ ¢ˆ`P„€´HB¤‘FÚž=óûcO’ÝÍÖ.òÛï󜲳óÎÌ÷}ç=ï”k¦4ÿÜ4%YT¬è º8ñÏ °ý®*>Ïê'ÞðÂÜc|*ÖïM7„4é0.qÝ¢Á¢ÊÐ_—€†´°æþ'"Ìê:u“ îIóÆ#â"½‡¨âîY²èìׯããþ ©÷¥ŠŠÏƒD¨ñ÷]ߦ‰_olJ½¶·¨ø1\ôt—iˆˆt‘Þ}VQùI°ö²ÂÍÊŸÚS”¯é â\|?áŽQþQèàµüAbÓm®åš;XìœÕv pÆï¦ÞµOò‚ŸðiNò‡‰e+#Å™ÃcÅŽ,ד"»©{B¸ÈÈj89>=g¥Šò„ÉK>¦-(~˜æ:ý¬9ƒÅ®{Zί»gØ3ýÅžûœà`QóY°ÑóîQ4·é{ñwô³ÝoOw;N}ÅÓ)6µ=EÅúN¢OƒãÛÛG|öN‘y½ã»¹SÛvìùö÷'zúž"žßÔO¼>Ìø;8X¬ÉJ÷tµ±‰=Dùª"ÖK~GÍ,vÞí¾ÿn¼Å ûnUऊùΧ!1"ÿ§Dñþ²~¢ð×xq×Ô.b]æ@‘óa°H0¾0²“xiŠÈÛ“&Ê3SÄ·O‰Þ¶2Fvå sþ`Q½Ødbòâ4±þ¾ ñàÂd‘³s È])Æ„é!AbźØfmR&uŽ6hô¯›ÏœèGŸ(Êœé§{'±yOWqQ ígäsDùÜæŸO\8P¬ý‡"nyµ(È(.盜óS¹Pqê£&<–$víHU{ˆ]˺Š)1>Ômd\£üª…ŠÓïL~¢§Øµ#MTîé/þ\ë“ü£¢Ä_ö÷™)båcªËw‹[ÿñ™Õ„‚HžÝ_}Ejü^ÜÌ>"ý.«ÿȼÇ1pê+žrå?Ô±bo±taJ3ÿqCˆ}]FÍ,*_r¨cP˜X±·ø|¡­ÿ±œžÞ˜&ÖNoéá®FÿZÞ_mˆw\Ù¹YÿÄðÛãĆ?ŠÊ¬þ"}a'q~¸sûp¥¿‰éSmú¹]ÿÝž*V?$êyû}Äá¬"{i”¸áÞޢⳠd“>¦~ ;‰³ƒZpf‡P…ú#uw ëmaãoMéÇ~;JQB†Énò®u~%@°ÄS9œ^ƒw—£ËÍËÿ½œâ¤p‡)ÏÆ nHþÖZª¼–¯±qSsùMË4:Ý®ïOá¾4Š7õdñ ­›î³ç×Ú¾M¸+ߊËqìëPb=ðS¾©yþk_I ïá~)s=‘>cù`ª¾ #¡5ëÀu’¿µÞá2eW8o@5kKCxgE?ò3RY;7Û•I‚nÓXùߨƒ·¦ù¯{ØWGd‚uZ{Hª9 èž@î^›}cøuäüTJð…š¦Ò`Ú˜PÔ‹Mù:¼W@ñ$ûiz½Ö4„'™£â˜"fAƒ½¨*‚:eYêR¯Ž òß„ëú»ÐaŸ^ØwÓâEKàúî5Z°jÆfï‹æé©uÜqö6¾HèÎÍÆÅ¡aÝË»>8ƒèsw³ºW¾{ÌFÀ¯‡ï¶…ˆû«\Ø´Ä©:óç?÷Ò½ï6î)ŠçͲ§ÄõñaÖô‹à/SÈXǃ©:º:Y°ý×…~ôzéG³.cµýb–ÄÀ¿w£Ë‡{tÞ.Þ–âyëþæüt¾·Ì©‚¯êÁ‘•Ì_ÑrëP£«nßνû¢ynšÆ­gÙ÷¯>w÷eÙØZž¸j¶s_^ ¾B”£}\A̰íL~Ù^¦%[ÓÏmÒo©Mzcÿ’A·±Ù|—”Ìš›ÒÏ~¾Gbܰm ~BcòÔŽ˜lêŸ|w_¾h¨ß í<Ë'ïîi“"Bxt¼Æ;oJ$šz ËàüSÙ;WB-«£˜¢#œYaŽ×xóUûò„$ÊsQ¿w “vì`ÄSÞnÝp]~ŒmùãºòÓ˜°};#žóMþábýR²_QªÐ(&€xC~ö×{¹fúv†ŽÊ`òëi÷ dÕ•-´²F~„]ùGJ­åï{IF-¯§˜"½‰ÂBˆ¦ž’J£þóä&~ŒF®JâߥYüÃ&¸RœX,IHBxð8äÐÃÊf_&ffzÏP qAÁ̘ՙ?ÚÁi“ð}R~˜¯4nðÍù.›¿_—ÉÐQL|µ–¡ då®6K9ðë…YÕèñÁDE…2 ç ïì ã´Dè™(³_C0Ø™ I¥|õ;°£„uñÑL·yÉÉÁܽzµ¹ƒ¨?8˜¹gj,ýo%ù¶¯žì*ÞØË?.d+ùªÒ¶/7ŃÌñ×FR³¬„-wªYw$”¿ßbHeþõ÷pTÅD— gü7ãwߪý\wóvFžŸÁä×ëúÐ@¾›bß¿Ž”4ا‚É°ïøˆã³/Á| ”•‡àûßë(Ë('ý(üœe"©›5ýð×eÌÿ ª ¾^x³œˆÓ}@È|qˆÏË­}òU%á;ŒȺŸ­Ÿ¿úv4æwvsáŒÃ˜‡F­ºØFmùTV³¶°×ÝŽuŠIæ_Ó;¡*&¢7:é´Kà$I•+sx|=æÖóäâJ"Ò‚íöÁ¹C·„@Äþ£¬9µe°}M+µÐÆ¥æœÅuUÙå¬9¢ö®­á[äçQÈË«!»ª á…7J‰á8@uŽÒ½¶þ#Ÿ…û:2<’â`ïNãK!ÁŒïQÆ·é†ÿˆ‹æ²[ó æ_þãóç ÿaôgóîc¼¶3†™.»ì!×`¨á@qCÒ“ŠÜAÔf÷æîxúWü°ÅÌÑm•l/³í_&î™.xó¥|æXùÐ>Öô‹åj#rJH åüpªË`ÇöúëÚͰŸÂ&ý~cs‡scÿ­„ʼzž½„È3B‰1¸ùw®dS)”m)ãå¶ûðfO,ø§Q¿*Xñ`.ß÷ëÊ•¼ œ$…Ç>êE‡×v3{›ñY}S7¬8\ËÑõ›ì’|ôã^ÌÝÍýW3ëËÐwD:C®ÍaûˆÞÌŸ`Ÿ~þ<«áÔæ¢ì5çű|»Wã²zœšÎ€«öñç™)¼ã ô«ƒåW¼!;©=TMNŽ% Ð$¬i¹«±üÈɃårÅ[fΘêówwüX4(;RG^¡@ ´–ov¸GøËÒ ¹ø˜ýŵª5v6©Pq¤šìÝžŸ¨N¼§…'†_µ6*0°yÝ^»t3Ac«¼º×ú²ýô==ÁSö±ýŒæü»ñÊÈ’Læü|^Û‡²jyô±bŽËdcê!ç›2>ÿÍÊÿÚ÷1áõZÎü»þ™Ç>rίë!c»“2 Œ¨ôR>Ø"Ó¯ŸLØzöåû+/è̩ۊXedYµ5Œñ ?ý²˜A¯ãƒ™éô:+ƒ ¯—ñó¼LýÖ¾˜ºzXò^%g^Lçà€vxÃ!K&wâ€`n:»†—Øüà ûÈ»4Úœ4*·÷"ùÇÃlÕ$LŽñÊãKzò’=¿y+òé¯u~|·€+Þ²0rZ;ýŽ®%çˆÁv/zïçŽZˆZZ ®NPSg|TA Gœ‰çßïCÞžAÔŒùýHBBU~`¢SThÓÖŠzô ‰ [”™‚C@ÿH.ÌÚÏÌåPX óä‹eTÊÞýþì¡yä^”FYNUÛz“òC>›Í`Ñ9.eÈßßôw}…ŽQ¼ægï§ùdžÑŸ}Ëâ™7;œ+´´"’,7Û¿÷“\2FôeÏòx^™Εƒ|”›Ê‹öåà^CÿÅ¢âMÞýÇlüG ¤« JSé[ËŸgÒߺpê¶bë¬#°*½#ãþfk>u,¼)ø¡é\:¯˜ï_Üɵ ÎFhÎüÓwJ9cº‰`ٵꇣ¥Þ‡üP¶8›¤ ÷ñ«.!ëÞ餩éÔÔ:ô¯ØŽôá¾u¶›×rE¨‰.ÆÄÀž/ŠÈ<£YËã™{wãúÚ±óó¶ÑŸ}Ëâ˜3«y:q&žÿ •ƒ{Ûé/ KÉê12vÚÈû³¶ÉÇD…Ñ?<„{¶©ßÁT®è@\/gÂg~œÊ˜µ™ {ÍæÃšzŠ K'øý¹\HõuÈ¿¤cÙÁ°·œê”‚|(È/㊇B9úL¾(%ÓHþþÎtbŒé5K…wå.uˆþK ô—*®|¸¢™üU3·s¿{ù¿=“͹)¢¨'ÏÅõã»·Va™D,Ç(ô¡:åǦüMOíe@а¶¯Ò ¡eOÇ`ØðT6Øê§ 85˜³ã#9gkoJ  ^ÞÎù·frõª6ò®:@Asþ]¢¦žR]§$ÛÖ•Q u!<(ož%{{ úÕÍùÿÇÇý¸x}&CÞò¡ÞyÇØÖ•±g²s3l3#uxG;cÙŸÖ¯ŒÝÐ3úQ–muJŠª`ÖU¤O4¬a¼Î¡ÝŸ#È2›Þ+R˜óãnîÚbó2µßçðÉã=¹¶G!†ÙÜ–¯·t!¨u#³ÓÕñœ™uë³vU1þô ëJúG±ÿŽZö9ØßÌR½v;ø¯Éî­X&K…¶ýëé,»þ•_Æq…®[Ðêí—€žø4•aK3é?Õ0·‘±”<+špB×Ð-z³xÏbÑ©«o6£ë zÖY ê,M³¹U5Ôi^F>ÛJ7¼, Òº°ßRÇ¡6Ž0-.&i-Úz§œ.g>íp ’³OÙFÚAŒ¾0–g—%0é¶í\ù­oõ«¯7£[,Ô{ÿÌÒ&ú ÿ‰{2di&ý®¶Õ¿—¿Ntðæ z ï€Ü©šeÆŒõe—„vfgÿ!c¶¨HïiÆà_§ä”UÏå’º"…9ë ÿaKüºC|þlOn:Åù[G³èÅA‰õšËüµõÀ¡Mb`¼OKw×Réí6›ýõõºÅ¡iuHuÅLMÎe‰K‡QÈY} õ7÷ë\m«¿Ý¥œ}J©Ëô†þÛïïô§™ëÑu½q˜æ±~îfœ®]Ø—[ì`ä“ÍçÛ§pºÍ†„!áDåV±Mx“ßÅŒk¨j?Õ+ ¼ÜúT /Ê?PÁÿ#ùÝSC×RàC§÷™_嫸˜Ý!â¤ÓíÔ ºšA÷ë È©+ç?c2™ºÊù-œQwäßMý·äÈôH²ù(6ˆXQÏ‘rïùŸ¶¸?·åî`ľVVcgi(“ÆÀé@F9–¿Åpjõ1v kc&^ÇÜ«š8Œ¿*‡ƒÃ»0Ö…Ä—^«fÊÃ!8[i}e‰ÊÍÓà j¯ÃnÜàæ©¡lýðGÜ¿ƒ.¢óÖÖѲþm¯Ÿôßã‰à`†G—óÆœ¦½SŸ´ÑaM¹Gé•l/æÇÔnÜa¬ÿ¿!ŸW*-ÖYäÓ&F6ÓωWÔe¬©å¹`ôëfF_Ôöå¶H~«õÿ×òÍò畳ª$”qcÚÉŠ«È¨ cÔðÖëÏiº'ýV³G %Íf–ªoߦsÞ|¨_3“¸äÕ¾Ìér˜;^¨.ÛblvF-XXAÊ­ñ\—“CxóÎ02>('Ë›üÜû`ã†AÏxèn–<EÕªB~õ’{ÛòÃ{ñæØü®±ÜÞò;Ს”#¯Œaéíüüö1н”ï ¿ÎÊoÀŒåƒ¨øºùÚ»§üUåMAcézê……Cûš¯”4ÈrÒ†Î] kr Á²BÂ)Ð=Ñ0$øw%ÿqú¬®LíÁ‘0ëÁ®tøù0ËÝð¿þÝ&þǽ>€—ºâ®—u"œðë ;séÕ¹’@aé¡$æc/Àð.ÐJøì×&þZÂêÚH&žéBà7y,ëÞGOkž”ón>¥çDÓïxŸã—ÁÝËxËÙ°jPÓΆ‰pþ- |6 ¾`nœqñd¿žú‡·öý?AM {ªB9ÿãïp•oéÐ6`íºZÎ5^J·ÜRÂÈ©dýœÄèò T¹î_]ã··  fú¹š,3vfw>¾VØéçD¢(ÈŠÔìðÑ®‡sÙr\;*}»Û®ÜVÉoýÿÏýG€µOöê,!w ¢{èââ ;gùyò(§<ГgÇBl8¤Ž¢¯âÅM¼Ä³ ÌL™Çi‰CL<øl)^êÏmºGýé,X!sý£aœ¡ýÃx|œj7/ê©~.ý— &<¨«6öhÜȦÿedUï粸æžÆKrëßËä²·½Ì/ ¸c4¾FïXŽV³îËŒ|Ä{âíÊ—jùiá6.z¯i&©]åzR *òÊùìÁLn_êýô‹Oü:)¿µõo5R"ùcMwû‘¯óˆVÊðZyëù/|+‹ ™ûyoiüùýÆÞji ìšñÿP&3?iÈÊ„¿™èÔåë»;å×ví³PPÎïÆß2-\y°–cÀà‹# ù}'¿9äùn“Ä›¿¸Ù}*X÷ï ¸Æ!<-;Æ+ëuÞ?çø¾Ô†ÿ½+Ñëvñ~½“DS(ÓžîÃËq2Y%¼5}nöÞ~=õv·ÏVbæÍ‡X6§?MfgóôüB.µùåì]«ñ\ÿ†ñf'Ìù@} 7¤ä°¸Þ½ìêrز1‰ÛàÕíUL³ËsÿúÚÚ¿nx€å@p8*™—b¡2«„vúùßcÖwCx¶_Ã[vÇø©ÒLŒûO*Oö"¤¶†Kÿä’W½—ï‰ÿÖÊ÷¤ÿÝ\0'•ã"¡SÈÚ 5_m¥ã '‘“üÕËrbŽä»û³ë5…ºƒå,¹o/ÿ=Ü6ö‘=oç×teîü̉†âœ Ö|žCö9Oúó”îIkïÙÁcoôáë-=¨ÎÈç‘÷Ê8khÓ^JOõsX¸ò?þÇÿœLÏüôH¯ŸVãtâ¶_ÙElYŸ(î?Ƴͤ?ÿãþ?<1·ô…s}Ïç¿«Î?NBè„ðàÏi”,RÿrRNzg •ë{q ¢ÅWvœ¨í¿2øª”I=HOHÙž~ü<ëÄãÏ?NJÄÃ% FÈöooú‡'ûü(³™í­ñl.ÚßV¨·0o80™]ªÊN îocÿÐRþF9³}ãÉ?ŽüœòÛ 'ñNö lDg‚¦1V‡®ŠÊ7‘þ'2Ì·hœb“U>÷!¿^ÎÑ4Î×A’UVú8ÒpW~{ËÈ3›`6“j6“jÖyØù­å·-ê \¥E—øVY4òœ½¯ZÉ?ÀO ” s¾¦q‰€>ðï‰_oPh63°Q¾™T šn¤±°Øl¦‡ÙÌéA¡¤b[ûô$$Jð_‹Æ@‹ GRùÚ‡ü­ÅµšM»Ìf¦›-µÀ&#ýT.Gp‰¦ÑÛ…’âSÿö¤ŸÖÚ÷‰„‡['øøTM£¯€ÜyœüƒÛ~‡Öh·™uf3iÆßŽ?'’üöÂÉì_þ?ÁíAO½‘ÅAÁÆß·¡ŠïlÒƒAäˆõ2¿ãÓYäP½’Ø¡ª"O‘ħNê–g2‰b“Iä;‘ ªX ♊*¶ƒ8Ã&}‰„Õü0=±¾üCkø‚"ò\´ïkÓAìR›·Ï?€¸ÄoŠ*òUElÄwÿÓžò=éß“~Ovÿâ©ÿ`¼WVËVþ×ÒT¶·úq—îMÿ¼IìQU‘b ˆ>Ô¶83‰: Æø{ˆ*쎌¯JT8ÕËüŽ˜ŽL>x}×ÒU°Ù‡ò["ßYûl—2…,UeŸ óZµzæW¢D…Óò]‡JÐÙËúŸj·Dy@†›z¹’ï+|åÿEãàdþT$VË´Oöç_ï˼×ûh®DAMåŸ*[š]h{@‚Á^æok\Ž…wmþV Ùó²ŠËgü&**YÆRÓ¼Øg{B•átÑxZ‚Z4VHÓôÎÀçk=-‚ŸPFì`6“h6ãꆕ4v, ðªÍ]¤€ŸŒ¿ç!±ø—1Üþ·¥PÜô„þ2D§iï¢òš~z›'ˈÀküË¢¯Y˜”ùX7wü·V¾7úw¥ßö€§þ¼ý‹I_ÑyDâ'ý'‰%ÀsºFwÍ£À{@/§™]›UIDATõã)Ý“~F s‚)šÆ™À$Eµ³AOõkñR#PtÙü…Np62[ G[ tñ2ãt¾ÉD‘IåR4.ð¡ÂQèöX¾Ò*ùÎÚ×pí~4n°X—¢¦ê0DUù´Ã¿£PØ ˜nùõ¶þ]l–èîþÝÎ/¨–ò+)Ü(ÁftFã+›t_ùweî¦_»:ìA8 tµ±­˜L1ö <§ ¾ØÜƒ×<}9pÈHDpÔ¡Ì2d·òWÚäoKŒF¡R‡m6Ÿ­·¨L±ùûv#0Œò’ßýhÜhÑø›¦1͉~|õ-…$I9ùÜlï€uÊ„õø‹IlXFÁº¯ë0`^ÅBgpz;½ó€C¥H§ñRêe"$ë¾½¡È¬7>N–¨A0ÐP]Êo­ÿm)t7ÇûUèð<Ö%ûçÑèä$ðÓ°8•„.`m£½X—.Û ­•ïþÙé×B¸›À×ýË zzé?Zê_¢¼–ïf | ¬ÅB¹“þ3K·ÕÆ`ù{`Ò´Ö“~<¥{ÒÏtÞ6•À£{KöT¿6 œîE&À"xÜYçA'°Ø¼p|É?Àlf„Yc—$7±œ…ÊC¹9®:¯Ûò- icùfÅ} 䀫5 #±î]ðîø)ÃBŽM¹Žü.D#šuOü¼%Á^ÔÍ“|Op§_O#Ý?…uSy9ð_,Ô©0¶ü»ã×­ót؃ÐÏαXøÀl&ÅlæJ³™Ÿ4[lÏ1}†Wínžÿ*³…_5hÜ ê|ìðÙÓB#_V)TUòHÂBÎ7X;ã·X dc½³Ô•~¼ñí…: Öx0þ ´I ‰ªJ¡ñb •|‘êÍ_B’u¤Þ¸Éú|]p¯|¼ˆFy;øßÖÀ¼Üf-nþW×~**éXd2¶ëÝò=éßYÜBÿ’f63XXûKCÿÿÔøü*§þ£eþ¥­ò aݘ_mô#ÇþÓ_Vù·C`8A–7 {Ò7ús§ŸdI·îB·[íðT?¯g®]%Ü„ÂX,œïðy12ÑXHÆŸEÑ-nDæoèœçB~™ñ\/tö˪CÃ]áëÐèenê”Þ”_ädäPèF~?sÃÈÈ“|‰„Sǃ„P‰ëÈÏ[ÜŒ‰‹0sn Ûç ÅÈ$ºÉ–ªr¶hò輬ê\¤YÚôíN¿îPj̰Ù"èîÿþoBå"4—öçÞµ@›ôJ£m?}xÁ}6éU6é©H<…hüåT ^*ŒY¿cÍÿ=:©’Ì3BçÞ̸ºyýE¢0R³p›“´ktÍ®ãmw@ߌÊX4ÎñPº ]u«~Š‘éÚ ûn«—¿½T2B%…ÁÂÂpM£ÊHÏ–¬KvKHÈ.J°8†º,98\a7\°xå?=õoÛhM’„O3!Þ ldŸå눋-ýó€çT…k4 S}”a2.½PøpGù“5 ×{)÷aYe˜®qº¦5¾ÓöËÐÁ¥1IȲ ØËŸnʆ¦Ýù—#ÆŒÊa ÙÁ4ó/þ¡‰(;»”è„Þh˶þå ¯,󬮷z¢aaWfnÔàKô…fá:‡ô.ô÷C?çhÅFÉûe…AQ¿™ÍŒ¾ÂéŒÓTnÁbL#Ûc³&1Ìæï˜ˆ²`·®:3йÐÛÎhLeã`UFdë®ü RƒtåO™ù!(Dë®÷Å## «±{‹)(Ü€™ š9Yg퓉rQ¾âä³Í™4‡‘-?ýÍišF?M£¿f&O˜yA³p»SçÔFÎÖ ÿ®ä§[¤fAR,pÐ%ÿÖ5ú"û½Éš4Ú¶»?Þ@0 ˆsñâ|Áå@'ãïmBá4‡.ØøÝEYó…ÎD›üÞ E3Û²Å4Ea»ÑÜatÒ¬³G¶üތƯ–Nd$½éE±Y“ìÚï©ÿ¶çTR£¾šF¥§©¾ ‰ '“û]@ΑL’ñÿµ2ÜÑð¢:Ú|ï*dntá?·8ôï—ü „hšh»`T4΂·Û±î»\“=ßÎçõ\ïIl?NÓ› þÝáTYã]›‰€0¬º×ÝX‘ÔÆ~Fwð/—;ô»t!q9M{Q· Åa?“ I`·/ÎoèR‹ü‹+d …³|Ðÿ$Mvjç™ÀKÀåv[=†*‹müV'd¥¦¯=B¦ŸÍ÷Sí©LTÎlƒv6óKc0ñ”¬1ëôWç@ Ñ[†« £š£@º ñ§é£‘yB2s¡LÇüw—É@_àY¥ÊâúÅáˆE6åw^’U¶ÚLŸþ ¸È˜¡Hiùsd‰Lé3€1@0ø@lÔ½_#¾…'°p·á,£^ºÍÛ'“©7?ìí:Tòh¾÷d‘0ÓS†iFÚ\~ÊlÆ£X7çÑ´la+?ç›ÃñžýÄ;ÔáNczÕÿ®ä¿'4†É0Å(g& ¡oÆIü8±ßNX÷&Åù<ª¶æ‰wÒ>gæE±±iÿÿF‘GŒïX,œ)Y×Ô;s•Bͺìè °R•y°Û3¿FªçCiW‰À($ªÚØÇhž”4î5øuìß3 ý'#Tëè£6öÝSŒüŽý÷xN²M0ÐdîÑ;GýŒ¬¢J4 4cT/y¬³’õe÷ z㔳t,)l•à<,þe,&ž–t;ÿk#m¡Ñ¿¯6ø›ã¿¶8è‡y6É'ÅSº“%Ø×¢Ya‹Gù’1Hõå Ì=ºÌúWDÓ`Hv~ ÜR\ù—(›ÇÝ€ØÖ€½-êXXáà_Î0üK¬áÿY\û ¾Qeh#û˜kѹ\†›Œ >ë9|½ô`º~&K¶Ë”V=͘EU€ÉHvé{tÅ.ðyR–mø××`=ë¬3ð€"ÛõÑW5‰Ëk&£~÷ý[8]¦B˜bⓉ]Æó§DãH© ¸J‡;•ŠLg‹™klòSÂUŸºÈ_ Ü%+|¯ª¬R$Buß6WÛ”¿K‘è¨k\n“^Ü#+¬SUV·‰|³Ý!:ð´¢ò»IåC ~Ó,>m,»D• 7™øÒ†Ÿ­6ü8kßU:¡³úߪ˜ÈPe"øñ¼´à?›Lì0™H’LÜg2±K¥qª½˜Ý þK):Ü¡šØ©*LÄʯht2ª&6üÿ®Ù·¯Á~?3¸Í4™È›øõ]L&¶™LdÏ.N—´g…,2%׿@\`±#!@‚›ü[QÙ®*$[ê¹ØC}ÞÒT®¼ß ìlfÌCPéâfºújIa½ªòš$ø\Ó™m׿e:ª&>6¸uìß:ð”bbIe±líW:Øç‹ÄMª‰íNúoóŽöƒ³Y„,"d…?e‰ït*р܆u_DŽI&ÄØ`[lw¬ìÐ'­/Ö†_Æ¥ËÖ Q× § ¸Gé—ª¦šøÂÆ?l“iEÛúoøkHv†ÖÀO®ÉD¨ÉÄ!~ª€‹‘ùMUÙ§Ê f;ûð„&ù*!äç)CÐ|’?[׉”šô¿R·.Ùjnçà|;dµÁ¿ìj|ìý‹ãìæ†ÿhð/ª“ô+œø—-ªBo]ã/üKƒj-rŒÓ%&)*{U…UtƒÆ½ÄUHŒ•lô#ÌL¶É_ŒEf£ª’£H vÐß ‘²j¯ÑLn@ç$>RU~‘à+‹FÍ;. 3ã-0EV8 *¬’­ƒÄý-ð­?üðã¤Â\U戦óô_¸ ?©ðÖô _ÛXÓyæl×Ï2|«Ã+X—„ÂÁåÊÿ~øq2 +ë3^mÿ]u~øqÂ"dîPUöã|/܉ŒËPÉWUR[1ªÓQø‡ª’ïgsµ7ÎÑ­®w¢’«Êm~rúeµš??üø«b´ÍÿgË2«Û¡ ÿŒ“~øá‡~øqRàSIfˆ"£ê¾Ó7µCãœüðÃ?üðÃ?¼„Ý>³u~>üðÃ?üðÓ gµ¡,ÿ'?üðÃ?üðÃàä‡ÿ_1€¸ìä!å£KÚì Óã…Îïå’Zt„ÔܹvCžHí¸r)}žïö”â¿.nd/Ì"éÊÿ=?'2ÿ~ü? œB'ý‡ÄU›è“›CŸ­_sßfާÃÌ·é¹s?)»~&ñÎvéžòw¸å ’~Í$%?”ŒoévÇ`'§òº‡»òÛ[~Øóû¬N©á9²•Ø$ïe·–ßÖÖ¿ ì!õàû.Ïr%âbºÎÿŒžæzð£fwµÿ#î¤Ûé¤äe“¼~ 1©Þóï ¿nËžú)¶ò‹Žzx1]m¿•DìæRÒµ»Ãªã+9öùlž”·FÙ8yçùBïßâðݲ“’ävOYas`ÞE$tR¿¬gOv—¥aÄ,þ‰äÜ\R¶¯¡ÛÌ>õooú‡'ûQ íYÌá=-x±N|n×ï¢dúƒÔùbW~KäE£)*õ‚_Oý¯ðÖ~Û“ŸE~«pÒú?\Ì8åQtýlоÙ̱#%[÷"Eo&àÒ1F„LÇ[Gbþïí”ähÔ|Iá3Ûºõzã®Où¡êË8šžMõÑ2ª·,¦ä­lÎ;Ç˳f<•ßþòÄ‘]ÔU×XŸ͇mkùµyb=2^nAýei±3öP|ÿO.O w.,9Ÿrä­eT¬Þït´×:þ¡ãCSÐÍᥙT-¡zÇ:*Jì¿ãšÏöçj ¨«¬±)C·Óqàø‹`õÊ¿«$d|T#Gj<åÔ—êPz I†ùêkmÚUEàÙUoÿn8å1„ž²ŠVP Ô—üHáãºqš×ýÛ½~¼·ïvqv©“¼vñË·ÒgÓëDŽ›EÒŸûHþðªÆÛíMi3‰_¶ŽäìRv®£û#6¦ÈiÏ“RhÌ8Y*ê¼pI÷L$fÑ’³²H^~ŸÍ=—ó¯BŠ.y’²bëiÆÚÑyª°é1È^ðë©ÿµj’ÒKû%j‘¯¬&97Û¡}žù :=ò ½öä²?“žËæÐÙ‡›ˆÛ[¾'ýŸÌþÛþÐáÆyôHÏ"%7“žoßæ0ëå‰÷éžù#êõé“»ä/ âÎHùt¼ýª“ÛúµÓR]³/„!£ÁÁ¥VP»¾¾)òß°­×i.Ÿ}~Ç—s*/MÄüÛo^^ûá[ùí#_ÆtûOôÎÍ¡Ïˈ›’ЪiÑ–ñ+Óé«s {v'ûÔ©N™CÜIŽRϸ‡Ý]bÈž¹ÞEà¢8ål꯻€½½{S|äjºü§28í”ES¨±“‰[ö=¿zˆ~uHñ]“ý¶Þþo¾ õ­ ä¤^J7k´Ï~&.&fÔ6Šu'»Ç©~b-Þüí-ß“þOvÿá©„Üö-]Çæè˜Þd'žEñËèúéÄÆížø÷”î‰ÿ°ç¾&¢Ë§ôêEî¿‹ ›ae¯§ú—ÀI–ÇyE9Ïm0F¤]P(F+…Ž/l¢×Ü(¥…èD;QhžßPý˜E¤. ¹p%¡Ó9øÂ!/ƒwå·§|íËû82i4yi#8ô|AÏþ@üø’ßJ~[Rÿ€ñïÒ©ø~ж·£km1ÿñ¨AÉ„ÿ篓¤nˆ½¿P] f4ªÿ‚ŠÊ¶«[kå·^ÿmÿá¾ ¤Óí!T\ÿe‡ÁL?FÍ ©³Žžø÷”îžÿtœh¦bÖkT™¡n÷|J>.¶©½çúµ™¹NJ"zå#ðô(Šœ¬¡ê9h”º™.vßòít$v@0‘ˆWæÐåâá|Ý´Ç¡ãÜ}Ä]dUäç—“uû†f3îÊoOù5ë?¡q¹zÉÝXbO#aÆ4¿\l?í­áW§äÒJÜ97§ù/!öÞc”žn]š‘Ý8OòÝNù{àßÍx¤ êæÎ d@:Ew¿OÇŸ®!”µ”ùÄ¿{~ÝÂqBuMãT»é¢ nùبÃfªÿ8…Ž—AÉ—^•­Ìßv¸˜ð Sq¯mP{˜’+"ð‹ÕôºDõ*Ÿ|º¡ý¼¶_oõãÙ´jj¬—²ÖÖ¡×5ªÉXPÂ.$úõû Ù¥C€õˆà=û|ìt,7r*ªk 8 P¢ Ñ @NGðžÿ’»¶ 3e”<¼”NÓ}óÿ;h˜wg7¶O/«@íójkñê7¼C÷åßR½f5Ë>¢"‡6›Qk­üÖëÿ/î?Üô9hSÜqÄþE–,Ì1ÀÏü{JwÇ¿’B€i7e6ý¡.cô7^Ô¯§¢>YJÈ“É{·Ì&¨(ÂBjT½pU€œ0™"‡ÀÃUþ×uõU°y1õ÷œNÏ—güõÓ3Òý³Ùw]QŸËooù¶¨Ûº®·.Gx8µ–_WpŸ_>í|‚“.!8ï"b$)@¢KÞB¯éÃÁ6º Ôÿ®Q‚E¯Eßk#+o?fi(Š‹3îóïÞþ<{_c‚“¤Ð‰g Ÿ7ŠžyFW7búrµW{ÝZ›¿­8õ‚ö.âðQ‡×bÞ»äžön£cÐ{<@/KCݼç×^?­µï¶œz²±'q¡]<Ñ«_%è£qäLÝI= §=Gï×%ß Ñl<Ȳqq uµMæ\[ç¥8 @×´ÏKßP¼Üä>ó"B.¾†è_ Ã´þü±|O«ä·…þOÿá´H`^Å‘¸im!ÿîÓ[À¿äð‡‡úµœL:±p5³oæà3{¦ˆ7S»»#A#›&.ÕÓ‡£îÛlc$îò;k¸„ÖÑ®ý ^ëãsùÇW~ÀÀ(Èóa*½µü‘9Î6o»Ï¯ÿqû»&²?1‰ý‰I˜¸síFJNOáÈZoä·Nøw-uûM˜zÙh|wQ„ExË¿öç†zf>ecz4q8zæ‘=.ùÛ èxãêÞy×¥]kÆ2õRäß~¢¶…üÚë§ý÷¸b1¿QþÒÎF¿`ЩMn ˜uÅ”ú¯©9å&:ÃâŽw\Š,û=1 Sϱ_¾¤è¾ÉäÏ)'tüimú‰–ËoOýÿõý‡^›N]uÁ[Ç¿ët÷üëÕ;©7§ØÝÆ¿ôOö¹~í8uze-Q±Rüìä`C‚ ”¾XCÅë1Íz™ˆ…€®—}oµo¼ÝèøÜçLô×Òi@"!á þwb_¸}ÅRÇìvÑCùí-?•¨ÿ\K§þ „tŠ!lÂÓÄÞÝ•šW½_¦k-¿ js¾yÛsþÆ€QÌÙ%ª±ä ‡ÑŠûÍáÁÁžƒ$…aêJ`pÃÔ¥·ü»’_FÅ;Û z`ñ}ˆxz*ÊêŒÙ*Ïü»ç·uPN$Dû‘ªt­‘úŒÏ¨©EÈ6Ìß1‘€P£î!Á†µéæF9ñ&:öØ@Å'K}.!bhwB"âéxÃbnT<öu£}¸çד~¼µïÿö`®êKðk{dé\¢þ1¨Í¤×þ°àñÝ‘I§pÊ*‚–l ×ºÏ9ºsE×öëºÿø¾à6"‡Ä˜‚Òèxa õ;vŸ òÛWÿ}ÿ‘AÙ+G[ð*‘ÉA˜ NMÌ3Wâ%ÿîÓ=ñ¿‰Š¥&:¾x $èq‘WDúT¿¶‚C;Ð˺#Ï&nÏ즳ž$wÄ<ëO”—^É¡n ‰^M´œKÍë“(XÒ°ÅSþCèáwÓyѽÄv„£YÔ,¹™ƒÿîõÈÊ}ùí-¿zM%ré¨AìßDÕ¬s(\á-Ý­å·µõoƒ—nÂlºýq7&Ów^ŸMçúuÆ_Nið_÷î¥D.²Ö?´‚úoþKÁ­?/nOü{æ·UNyâhä w9{¨ù%€èñ©È›w¹åÙ›üҹϓxàyÛW.ÕWw'wuÛè0äækP¾ŸE…³y‘€>„Í{–è„`ô=ßS1q$E»¼å×sÿhoûlvPtÕ"º.ȤW@ÚÞU”Í[Jèm$jõ^¢5„ûgл¨ûÂnS(õÔ7?ŸGÝÏÓù¥+(9°ˆƒ[ä³pßÿþça‘G~,õ‘„>½ŒÈÔx¤ê\j?¾•ÃoVz©ÿö–ïIÿ~ÿQýêßÈ?ö"Qm#ª èû7sìƒ'VžøwŸî™ÿÊÙøÆÄåü±õmJ¬'è ÝëúµìvŽø/ùõÓˆÛ;óÐs)*ûk¶@»‰îý(éùO§Þ_µý¦´GH˜ß›ÊYwRök ¬ËÖæŠ??üøk ð†Utp?ûþ¹Ùã^@ÿ%¿~øá‡ 7™9ôY<æ¯wWÝ;{Iþó!$ý¤k¿9ãrǼƒ|õ¶ï%9;nwöhÓãZÏŸ~œ˜P‡Ð)IETÓD^ñ6÷PøgœüðÃ?üðÃ?pê~ÝßN`ÏH¤ÒmTýwGÞóîÇYm9ãäœüðÃ?üðÓí8ùá‡~øá‡~øáþ=N~øá‡~øá‡^âÿóHJÐN ¿IEND®B`‚plog-1.1.10/include/000077500000000000000000000000001447036265000141655ustar00rootroot00000000000000plog-1.1.10/include/plog/000077500000000000000000000000001447036265000151265ustar00rootroot00000000000000plog-1.1.10/include/plog/Appenders/000077500000000000000000000000001447036265000170475ustar00rootroot00000000000000plog-1.1.10/include/plog/Appenders/AndroidAppender.h000066400000000000000000000022511447036265000222570ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class PLOG_LINKAGE_HIDDEN AndroidAppender : public IAppender { public: AndroidAppender(const char* tag) : m_tag(tag) { } virtual void write(const Record& record) PLOG_OVERRIDE { std::string str = Formatter::format(record); __android_log_print(toPriority(record.getSeverity()), m_tag, "%s", str.c_str()); } private: static android_LogPriority toPriority(Severity severity) { switch (severity) { case fatal: return ANDROID_LOG_FATAL; case error: return ANDROID_LOG_ERROR; case warning: return ANDROID_LOG_WARN; case info: return ANDROID_LOG_INFO; case debug: return ANDROID_LOG_DEBUG; case verbose: return ANDROID_LOG_VERBOSE; default: return ANDROID_LOG_UNKNOWN; } } private: const char* const m_tag; }; } plog-1.1.10/include/plog/Appenders/ArduinoAppender.h000066400000000000000000000007361447036265000223060ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class PLOG_LINKAGE_HIDDEN ArduinoAppender : public IAppender { public: ArduinoAppender(Stream &stream) : m_stream(stream) { } virtual void write(const Record &record) PLOG_OVERRIDE { m_stream.print(Formatter::format(record).c_str()); } private: Stream &m_stream; }; } plog-1.1.10/include/plog/Appenders/ColorConsoleAppender.h000066400000000000000000000064471447036265000233130ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class PLOG_LINKAGE_HIDDEN ColorConsoleAppender : public ConsoleAppender { public: #ifdef _WIN32 # ifdef _MSC_VER # pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum' # endif ColorConsoleAppender(OutputStream outStream = streamStdOut) : ConsoleAppender(outStream) , m_originalAttr() { if (this->m_isatty) { CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo(this->m_outputHandle, &csbiInfo); m_originalAttr = csbiInfo.wAttributes; } } #else ColorConsoleAppender(OutputStream outStream = streamStdOut) : ConsoleAppender(outStream) {} #endif virtual void write(const Record& record) PLOG_OVERRIDE { util::nstring str = Formatter::format(record); util::MutexLock lock(this->m_mutex); setColor(record.getSeverity()); this->writestr(str); resetColor(); } protected: void setColor(Severity severity) { if (this->m_isatty) { switch (severity) { #ifdef _WIN32 case fatal: SetConsoleTextAttribute(this->m_outputHandle, foreground::kRed | foreground::kGreen | foreground::kBlue | foreground::kIntensity | background::kRed); // white on red background break; case error: SetConsoleTextAttribute(this->m_outputHandle, static_cast(foreground::kRed | foreground::kIntensity | (m_originalAttr & 0xf0))); // red break; case warning: SetConsoleTextAttribute(this->m_outputHandle, static_cast(foreground::kRed | foreground::kGreen | foreground::kIntensity | (m_originalAttr & 0xf0))); // yellow break; case debug: case verbose: SetConsoleTextAttribute(this->m_outputHandle, static_cast(foreground::kGreen | foreground::kBlue | foreground::kIntensity | (m_originalAttr & 0xf0))); // cyan break; #else case fatal: this->m_outputStream << "\x1B[97m\x1B[41m"; // white on red background break; case error: this->m_outputStream << "\x1B[91m"; // red break; case warning: this->m_outputStream << "\x1B[93m"; // yellow break; case debug: case verbose: this->m_outputStream << "\x1B[96m"; // cyan break; #endif default: break; } } } void resetColor() { if (this->m_isatty) { #ifdef _WIN32 SetConsoleTextAttribute(this->m_outputHandle, m_originalAttr); #else this->m_outputStream << "\x1B[0m\x1B[0K"; #endif } } private: #ifdef _WIN32 WORD m_originalAttr; #endif }; } plog-1.1.10/include/plog/Appenders/ConsoleAppender.h000066400000000000000000000043301447036265000223010ustar00rootroot00000000000000#pragma once #include #include #include #include namespace plog { enum OutputStream { streamStdOut, streamStdErr }; template class PLOG_LINKAGE_HIDDEN ConsoleAppender : public IAppender { public: #ifdef _WIN32 # ifdef _MSC_VER # pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum' # endif ConsoleAppender(OutputStream outStream = streamStdOut) : m_isatty(!!_isatty(_fileno(outStream == streamStdOut ? stdout : stderr))) , m_outputStream(outStream == streamStdOut ? std::cout : std::cerr) , m_outputHandle() { if (m_isatty) { m_outputHandle = GetStdHandle(outStream == streamStdOut ? stdHandle::kOutput : stdHandle::kErrorOutput); } } #else ConsoleAppender(OutputStream outStream = streamStdOut) : m_isatty(!!isatty(fileno(outStream == streamStdOut ? stdout : stderr))) , m_outputStream(outStream == streamStdOut ? std::cout : std::cerr) {} #endif virtual void write(const Record& record) PLOG_OVERRIDE { util::nstring str = Formatter::format(record); util::MutexLock lock(m_mutex); writestr(str); } protected: void writestr(const util::nstring& str) { #ifdef _WIN32 if (m_isatty) { const std::wstring& wstr = util::toWide(str); WriteConsoleW(m_outputHandle, wstr.c_str(), static_cast(wstr.size()), NULL, NULL); } else { # if PLOG_CHAR_IS_UTF8 m_outputStream << str << std::flush; # else m_outputStream << util::toNarrow(str, codePage::kActive) << std::flush; # endif } #else m_outputStream << str << std::flush; #endif } private: #ifdef __BORLANDC__ static int _isatty(int fd) { return ::isatty(fd); } #endif protected: util::Mutex m_mutex; const bool m_isatty; std::ostream& m_outputStream; #ifdef _WIN32 HANDLE m_outputHandle; #endif }; } plog-1.1.10/include/plog/Appenders/DebugOutputAppender.h000066400000000000000000000006001447036265000231420ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class PLOG_LINKAGE_HIDDEN DebugOutputAppender : public IAppender { public: virtual void write(const Record& record) PLOG_OVERRIDE { OutputDebugStringW(util::toWide(Formatter::format(record)).c_str()); } }; } plog-1.1.10/include/plog/Appenders/DynamicAppender.h000066400000000000000000000017451447036265000222720ustar00rootroot00000000000000#pragma once #include #include namespace plog { class PLOG_LINKAGE_HIDDEN DynamicAppender : public IAppender { public: DynamicAppender& addAppender(IAppender* appender) { assert(appender != this); util::MutexLock lock(m_mutex); m_appenders.insert(appender); return *this; } DynamicAppender& removeAppender(IAppender* appender) { util::MutexLock lock(m_mutex); m_appenders.erase(appender); return *this; } virtual void write(const Record& record) PLOG_OVERRIDE { util::MutexLock lock(m_mutex); for (std::set::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it) { (*it)->write(record); } } private: mutable util::Mutex m_mutex; std::set m_appenders; }; } plog-1.1.10/include/plog/Appenders/EventLogAppender.h000066400000000000000000000100751447036265000224250ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class PLOG_LINKAGE_HIDDEN EventLogAppender : public IAppender { public: EventLogAppender(const util::nchar* sourceName) : m_eventSource(RegisterEventSourceW(NULL, util::toWide(sourceName).c_str())) { } ~EventLogAppender() { DeregisterEventSource(m_eventSource); } virtual void write(const Record& record) PLOG_OVERRIDE { util::nstring str = Formatter::format(record); write(record.getSeverity(), util::toWide(str).c_str()); } private: void write(Severity severity, const wchar_t* str) { const wchar_t* logMessagePtr[] = { str }; ReportEventW(m_eventSource, logSeverityToType(severity), static_cast(severity), 0, NULL, 1, 0, logMessagePtr, NULL); } static WORD logSeverityToType(plog::Severity severity) { switch (severity) { case plog::fatal: case plog::error: return eventLog::kErrorType; case plog::warning: return eventLog::kWarningType; case plog::info: case plog::debug: case plog::verbose: default: return eventLog::kInformationType; } } private: HANDLE m_eventSource; }; class EventLogAppenderRegistry { public: static bool add(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application")) { std::wstring logKeyName; std::wstring sourceKeyName; getKeyNames(sourceName, logName, sourceKeyName, logKeyName); HKEY sourceKey; if (0 != RegCreateKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, NULL, 0, regSam::kSetValue, NULL, &sourceKey, NULL)) { return false; } const DWORD kTypesSupported = eventLog::kErrorType | eventLog::kWarningType | eventLog::kInformationType; RegSetValueExW(sourceKey, L"TypesSupported", 0, regType::kDword, reinterpret_cast(&kTypesSupported), sizeof(kTypesSupported)); const wchar_t kEventMessageFile[] = L"%windir%\\Microsoft.NET\\Framework\\v4.0.30319\\EventLogMessages.dll;%windir%\\Microsoft.NET\\Framework\\v2.0.50727\\EventLogMessages.dll"; RegSetValueExW(sourceKey, L"EventMessageFile", 0, regType::kExpandSz, reinterpret_cast(kEventMessageFile), sizeof(kEventMessageFile) - sizeof(*kEventMessageFile)); RegCloseKey(sourceKey); return true; } static bool exists(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application")) { std::wstring logKeyName; std::wstring sourceKeyName; getKeyNames(sourceName, logName, sourceKeyName, logKeyName); HKEY sourceKey; if (0 != RegOpenKeyExW(hkey::kLocalMachine, sourceKeyName.c_str(), 0, regSam::kQueryValue, &sourceKey)) { return false; } RegCloseKey(sourceKey); return true; } static void remove(const util::nchar* sourceName, const util::nchar* logName = PLOG_NSTR("Application")) { std::wstring logKeyName; std::wstring sourceKeyName; getKeyNames(sourceName, logName, sourceKeyName, logKeyName); RegDeleteKeyW(hkey::kLocalMachine, sourceKeyName.c_str()); RegDeleteKeyW(hkey::kLocalMachine, logKeyName.c_str()); } private: static void getKeyNames(const util::nchar* sourceName, const util::nchar* logName, std::wstring& sourceKeyName, std::wstring& logKeyName) { const std::wstring kPrefix = L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\"; logKeyName = kPrefix + util::toWide(logName); sourceKeyName = logKeyName + L"\\" + util::toWide(sourceName); } }; } plog-1.1.10/include/plog/Appenders/IAppender.h000066400000000000000000000003631447036265000210710ustar00rootroot00000000000000#pragma once #include #include namespace plog { class PLOG_LINKAGE IAppender { public: virtual ~IAppender() { } virtual void write(const Record& record) = 0; }; } plog-1.1.10/include/plog/Appenders/RollingFileAppender.h000066400000000000000000000100301447036265000230770ustar00rootroot00000000000000#pragma once #include #include #include #include #include namespace plog { template > class PLOG_LINKAGE_HIDDEN RollingFileAppender : public IAppender { public: RollingFileAppender(const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) : m_fileSize() , m_maxFileSize() , m_maxFiles(maxFiles) , m_firstWrite(true) { setFileName(fileName); setMaxFileSize(maxFileSize); } #if defined(_WIN32) && !PLOG_CHAR_IS_UTF8 RollingFileAppender(const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) : m_fileSize() , m_maxFileSize() , m_maxFiles(maxFiles) , m_firstWrite(true) { setFileName(fileName); setMaxFileSize(maxFileSize); } #endif virtual void write(const Record& record) PLOG_OVERRIDE { util::MutexLock lock(m_mutex); if (m_firstWrite) { openLogFile(); m_firstWrite = false; } else if (m_maxFiles > 0 && m_fileSize > m_maxFileSize && static_cast(-1) != m_fileSize) { rollLogFiles(); } size_t bytesWritten = m_file.write(Converter::convert(Formatter::format(record))); if (static_cast(-1) != bytesWritten) { m_fileSize += bytesWritten; } } void setFileName(const util::nchar* fileName) { util::MutexLock lock(m_mutex); util::splitFileName(fileName, m_fileNameNoExt, m_fileExt); m_file.close(); m_firstWrite = true; } #if defined(_WIN32) && !PLOG_CHAR_IS_UTF8 void setFileName(const char* fileName) { setFileName(util::toWide(fileName).c_str()); } #endif void setMaxFiles(int maxFiles) { m_maxFiles = maxFiles; } void setMaxFileSize(size_t maxFileSize) { m_maxFileSize = (std::max)(maxFileSize, static_cast(1000)); // set a lower limit for the maxFileSize } void rollLogFiles() { m_file.close(); util::nstring lastFileName = buildFileName(m_maxFiles - 1); util::File::unlink(lastFileName); for (int fileNumber = m_maxFiles - 2; fileNumber >= 0; --fileNumber) { util::nstring currentFileName = buildFileName(fileNumber); util::nstring nextFileName = buildFileName(fileNumber + 1); util::File::rename(currentFileName, nextFileName); } openLogFile(); m_firstWrite = false; } private: void openLogFile() { m_fileSize = m_file.open(buildFileName()); if (0 == m_fileSize) { size_t bytesWritten = m_file.write(Converter::header(Formatter::header())); if (static_cast(-1) != bytesWritten) { m_fileSize += bytesWritten; } } } util::nstring buildFileName(int fileNumber = 0) { util::nostringstream ss; ss << m_fileNameNoExt; if (fileNumber > 0) { ss << '.' << fileNumber; } if (!m_fileExt.empty()) { ss << '.' << m_fileExt; } return ss.str(); } private: util::Mutex m_mutex; util::File m_file; size_t m_fileSize; size_t m_maxFileSize; int m_maxFiles; util::nstring m_fileExt; util::nstring m_fileNameNoExt; bool m_firstWrite; }; } plog-1.1.10/include/plog/Converters/000077500000000000000000000000001447036265000172605ustar00rootroot00000000000000plog-1.1.10/include/plog/Converters/NativeEOLConverter.h000066400000000000000000000020701447036265000231060ustar00rootroot00000000000000#pragma once #include #include namespace plog { template class NativeEOLConverter : public NextConverter { #ifdef _WIN32 public: static std::string header(const util::nstring& str) { return NextConverter::header(fixLineEndings(str)); } static std::string convert(const util::nstring& str) { return NextConverter::convert(fixLineEndings(str)); } private: static util::nstring fixLineEndings(const util::nstring& str) { util::nstring output; output.reserve(str.length() * 2); // the worst case requires 2x chars for (size_t i = 0; i < str.size(); ++i) { util::nchar ch = str[i]; if (ch == PLOG_NSTR('\n')) { output.push_back(PLOG_NSTR('\r')); } output.push_back(ch); } return output; } #endif }; } plog-1.1.10/include/plog/Converters/UTF8Converter.h000066400000000000000000000010751447036265000220520ustar00rootroot00000000000000#pragma once #include namespace plog { class UTF8Converter { public: static std::string header(const util::nstring& str) { const char kBOM[] = "\xEF\xBB\xBF"; return std::string(kBOM) + convert(str); } #if PLOG_CHAR_IS_UTF8 static const std::string& convert(const util::nstring& str) { return str; } #else static std::string convert(const util::nstring& str) { return util::toNarrow(str, codePage::kUTF8); } #endif }; } plog-1.1.10/include/plog/Formatters/000077500000000000000000000000001447036265000172545ustar00rootroot00000000000000plog-1.1.10/include/plog/Formatters/CsvFormatter.h000066400000000000000000000042071447036265000220470ustar00rootroot00000000000000#pragma once #include #include #include namespace plog { template class CsvFormatterImpl { public: static util::nstring header() { return PLOG_NSTR("Date;Time;Severity;TID;This;Function;Message\n"); } static util::nstring format(const Record& record) { tm t; useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time); util::nostringstream ss; ss << t.tm_year + 1900 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("/") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(";"); ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast (record.getTime().millitm) << PLOG_NSTR(";"); ss << severityToString(record.getSeverity()) << PLOG_NSTR(";"); ss << record.getTid() << PLOG_NSTR(";"); ss << record.getObject() << PLOG_NSTR(";"); ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(";"); util::nstring message = record.getMessage(); if (message.size() > kMaxMessageSize) { message.resize(kMaxMessageSize); message.append(PLOG_NSTR("...")); } util::nistringstream split(message); util::nstring token; while (!split.eof()) { std::getline(split, token, PLOG_NSTR('"')); ss << PLOG_NSTR("\"") << token << PLOG_NSTR("\""); } ss << PLOG_NSTR("\n"); return ss.str(); } static const size_t kMaxMessageSize = 32000; }; class CsvFormatter : public CsvFormatterImpl {}; class CsvFormatterUtcTime : public CsvFormatterImpl {}; } plog-1.1.10/include/plog/Formatters/FuncMessageFormatter.h000066400000000000000000000010001447036265000235000ustar00rootroot00000000000000#pragma once #include #include namespace plog { class FuncMessageFormatter { public: static util::nstring header() { return util::nstring(); } static util::nstring format(const Record& record) { util::nostringstream ss; ss << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR(": ") << record.getMessage() << PLOG_NSTR("\n"); return ss.str(); } }; } plog-1.1.10/include/plog/Formatters/MessageOnlyFormatter.h000066400000000000000000000006631447036265000235440ustar00rootroot00000000000000#pragma once #include #include namespace plog { class MessageOnlyFormatter { public: static util::nstring header() { return util::nstring(); } static util::nstring format(const Record& record) { util::nostringstream ss; ss << record.getMessage() << PLOG_NSTR("\n"); return ss.str(); } }; } plog-1.1.10/include/plog/Formatters/TxtFormatter.h000066400000000000000000000031371447036265000220740ustar00rootroot00000000000000#pragma once #include #include #include namespace plog { template class TxtFormatterImpl { public: static util::nstring header() { return util::nstring(); } static util::nstring format(const Record& record) { tm t; useUtcTime ? util::gmtime_s(&t, &record.getTime().time) : util::localtime_s(&t, &record.getTime().time); util::nostringstream ss; ss << t.tm_year + 1900 << "-" << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mon + 1 << PLOG_NSTR("-") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_mday << PLOG_NSTR(" "); ss << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_hour << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_min << PLOG_NSTR(":") << std::setfill(PLOG_NSTR('0')) << std::setw(2) << t.tm_sec << PLOG_NSTR(".") << std::setfill(PLOG_NSTR('0')) << std::setw(3) << static_cast (record.getTime().millitm) << PLOG_NSTR(" "); ss << std::setfill(PLOG_NSTR(' ')) << std::setw(5) << std::left << severityToString(record.getSeverity()) << PLOG_NSTR(" "); ss << PLOG_NSTR("[") << record.getTid() << PLOG_NSTR("] "); ss << PLOG_NSTR("[") << record.getFunc() << PLOG_NSTR("@") << record.getLine() << PLOG_NSTR("] "); ss << record.getMessage() << PLOG_NSTR("\n"); return ss.str(); } }; class TxtFormatter : public TxtFormatterImpl {}; class TxtFormatterUtcTime : public TxtFormatterImpl {}; } plog-1.1.10/include/plog/Helpers/000077500000000000000000000000001447036265000165305ustar00rootroot00000000000000plog-1.1.10/include/plog/Helpers/AscDump.h000066400000000000000000000021121447036265000202310ustar00rootroot00000000000000#pragma once #include #include namespace plog { class AscDump { public: AscDump(const void* ptr, size_t size) : m_ptr(static_cast(ptr)) , m_size(size) { } friend util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump); private: const char* m_ptr; size_t m_size; }; inline util::nostringstream& operator<<(util::nostringstream& stream, const AscDump& ascDump) { for (size_t i = 0; i < ascDump.m_size; ++i) { stream << (std::isprint(ascDump.m_ptr[i]) ? ascDump.m_ptr[i] : '.'); } return stream; } inline AscDump ascdump(const void* ptr, size_t size) { return AscDump(ptr, size); } template inline AscDump ascdump(const Container& container) { return AscDump(container.data(), container.size() * sizeof(*container.data())); } template inline AscDump ascdump(const T (&arr)[N]) { return AscDump(arr, N * sizeof(*arr)); } } plog-1.1.10/include/plog/Helpers/HexDump.h000066400000000000000000000042101447036265000202500ustar00rootroot00000000000000#pragma once #include #include namespace plog { class HexDump { public: HexDump(const void* ptr, size_t size) : m_ptr(static_cast(ptr)) , m_size(size) , m_group(8) , m_digitSeparator(" ") , m_groupSeparator(" ") { } HexDump& group(size_t group) { m_group = group; return *this; } HexDump& separator(const char* digitSeparator) { m_digitSeparator = digitSeparator; return *this; } HexDump& separator(const char* digitSeparator, const char* groupSeparator) { m_digitSeparator = digitSeparator; m_groupSeparator = groupSeparator; return *this; } friend util::nostringstream& operator<<(util::nostringstream& stream, const HexDump&); private: const unsigned char* m_ptr; size_t m_size; size_t m_group; const char* m_digitSeparator; const char* m_groupSeparator; }; inline util::nostringstream& operator<<(util::nostringstream& stream, const HexDump& hexDump) { stream << std::hex << std::setfill(PLOG_NSTR('0')); for (size_t i = 0; i < hexDump.m_size;) { stream << std::setw(2) << static_cast(hexDump.m_ptr[i]); if (++i < hexDump.m_size) { if (hexDump.m_group > 0 && i % hexDump.m_group == 0) { stream << hexDump.m_groupSeparator; } else { stream << hexDump.m_digitSeparator; } } } return stream; } inline HexDump hexdump(const void* ptr, size_t size) { return HexDump(ptr, size); } template inline HexDump hexdump(const Container& container) { return HexDump(container.data(), container.size() * sizeof(*container.data())); } template inline HexDump hexdump(const T (&arr)[N]) { return HexDump(arr, N * sizeof(*arr)); } } plog-1.1.10/include/plog/Helpers/PrintVar.h000066400000000000000000000032121447036265000204440ustar00rootroot00000000000000#pragma once #define PLOG_IMPL_PRINT_VAR_1(a1) #a1 ": " << a1 #define PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_1(a1) PLOG_IMPL_PRINT_VAR_TAIL(a2) #define PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_2(a1, a2) PLOG_IMPL_PRINT_VAR_TAIL(a3) #define PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_3(a1, a2, a3) PLOG_IMPL_PRINT_VAR_TAIL(a4) #define PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_4(a1, a2, a3, a4) PLOG_IMPL_PRINT_VAR_TAIL(a5) #define PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_5(a1, a2, a3, a4, a5) PLOG_IMPL_PRINT_VAR_TAIL(a6) #define PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_6(a1, a2, a3, a4, a5, a6) PLOG_IMPL_PRINT_VAR_TAIL(a7) #define PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_7(a1, a2, a3, a4, a5, a6, a7) PLOG_IMPL_PRINT_VAR_TAIL(a8) #define PLOG_IMPL_PRINT_VAR_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) PLOG_IMPL_PRINT_VAR_8(a1, a2, a3, a4, a5, a6, a7, a8) PLOG_IMPL_PRINT_VAR_TAIL(a9) #define PLOG_IMPL_PRINT_VAR_TAIL(a) << ", " PLOG_IMPL_PRINT_VAR_1(a) #define PLOG_IMPL_PRINT_VAR_EXPAND(x) x #ifdef __GNUC__ #pragma GCC system_header // disable warning: variadic macros are a C99 feature #endif #define PLOG_IMPL_PRINT_VAR_GET_MACRO(x1, x2, x3, x4, x5, x6, x7, x8, x9, NAME, ...) NAME #define PLOG_PRINT_VAR(...) PLOG_IMPL_PRINT_VAR_EXPAND(PLOG_IMPL_PRINT_VAR_GET_MACRO(__VA_ARGS__,\ PLOG_IMPL_PRINT_VAR_9, PLOG_IMPL_PRINT_VAR_8, PLOG_IMPL_PRINT_VAR_7, PLOG_IMPL_PRINT_VAR_6, PLOG_IMPL_PRINT_VAR_5,\ PLOG_IMPL_PRINT_VAR_4, PLOG_IMPL_PRINT_VAR_3, PLOG_IMPL_PRINT_VAR_2, PLOG_IMPL_PRINT_VAR_1, UNUSED)(__VA_ARGS__)) plog-1.1.10/include/plog/Init.h000066400000000000000000000010121447036265000161740ustar00rootroot00000000000000#pragma once #include namespace plog { template PLOG_LINKAGE_HIDDEN inline Logger& init(Severity maxSeverity = none, IAppender* appender = NULL) { static Logger logger(maxSeverity); return appender ? logger.addAppender(appender) : logger; } inline Logger& init(Severity maxSeverity = none, IAppender* appender = NULL) { return init(maxSeverity, appender); } } plog-1.1.10/include/plog/Initializers/000077500000000000000000000000001447036265000175745ustar00rootroot00000000000000plog-1.1.10/include/plog/Initializers/ConsoleInitializer.h000066400000000000000000000013561447036265000235600ustar00rootroot00000000000000#pragma once #include #include namespace plog { ////////////////////////////////////////////////////////////////////////// // ColorConsoleAppender with any Formatter template PLOG_LINKAGE_HIDDEN inline Logger& init(Severity maxSeverity, OutputStream outputStream) { static ColorConsoleAppender appender(outputStream); return init(maxSeverity, &appender); } template inline Logger& init(Severity maxSeverity, OutputStream outputStream) { return init(maxSeverity, outputStream); } } plog-1.1.10/include/plog/Initializers/RollingFileInitializer.h000066400000000000000000000063711447036265000243660ustar00rootroot00000000000000#pragma once #include #include #include #include #include namespace plog { ////////////////////////////////////////////////////////////////////////// // RollingFileAppender with any Formatter template PLOG_LINKAGE_HIDDEN inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) { static RollingFileAppender rollingFileAppender(fileName, maxFileSize, maxFiles); return init(maxSeverity, &rollingFileAppender); } template inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, fileName, maxFileSize, maxFiles); } ////////////////////////////////////////////////////////////////////////// // RollingFileAppender with TXT/CSV chosen by file extension namespace { inline bool isCsv(const util::nchar* fileName) { const util::nchar* dot = util::findExtensionDot(fileName); #if PLOG_CHAR_IS_UTF8 return dot && 0 == std::strcmp(dot, ".csv"); #else return dot && 0 == std::wcscmp(dot, L".csv"); #endif } } template inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return isCsv(fileName) ? init(maxSeverity, fileName, maxFileSize, maxFiles) : init(maxSeverity, fileName, maxFileSize, maxFiles); } inline Logger& init(Severity maxSeverity, const util::nchar* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, fileName, maxFileSize, maxFiles); } ////////////////////////////////////////////////////////////////////////// // CHAR variants for Windows #if defined(_WIN32) && !PLOG_CHAR_IS_UTF8 template inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles); } template inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, fileName, maxFileSize, maxFiles); } template inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, util::toWide(fileName).c_str(), maxFileSize, maxFiles); } inline Logger& init(Severity maxSeverity, const char* fileName, size_t maxFileSize = 0, int maxFiles = 0) { return init(maxSeverity, fileName, maxFileSize, maxFiles); } #endif } plog-1.1.10/include/plog/Log.h000066400000000000000000000266001447036265000160240ustar00rootroot00000000000000////////////////////////////////////////////////////////////////////////// // Plog - portable and simple log for C++ // Documentation and sources: https://github.com/SergiusTheBest/plog // License: MIT, https://choosealicense.com/licenses/mit #pragma once #include ////////////////////////////////////////////////////////////////////////// // Helper macros that get context info #if defined(PLOG_ENABLE_GET_THIS) && defined(_MSC_VER) && _MSC_VER >= 1600 && !defined(__INTELLISENSE__) && !defined(__INTEL_COMPILER) && !defined(__llvm__) && !defined(__RESHARPER__) // >= Visual Studio 2010, skip IntelliSense, Intel Compiler, Clang Code Model and ReSharper # define PLOG_GET_THIS() __if_exists(this) { this } __if_not_exists(this) { 0 } #else # define PLOG_GET_THIS() reinterpret_cast(0) #endif #ifdef _MSC_VER # define PLOG_GET_FUNC() __FUNCTION__ #elif defined(__BORLANDC__) # define PLOG_GET_FUNC() __FUNC__ #else # define PLOG_GET_FUNC() __PRETTY_FUNCTION__ #endif #ifdef PLOG_CAPTURE_FILE # define PLOG_GET_FILE() __FILE__ #else # define PLOG_GET_FILE() "" #endif ////////////////////////////////////////////////////////////////////////// // Log severity level checker #ifdef PLOG_DISABLE_LOGGING # ifdef _MSC_VER # define IF_PLOG_(instanceId, severity) __pragma(warning(push)) __pragma(warning(disable:4127)) if (true) {;} else __pragma(warning(pop)) // conditional expression is constant # else # define IF_PLOG_(instanceId, severity) if (true) {;} else # endif #else # define IF_PLOG_(instanceId, severity) if (!plog::get() || !plog::get()->checkSeverity(severity)) {;} else #endif #define IF_PLOG(severity) IF_PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity) ////////////////////////////////////////////////////////////////////////// // Main logging macros #define PLOG_(instanceId, severity) IF_PLOG_(instanceId, severity) (*plog::get()) += plog::Record(severity, PLOG_GET_FUNC(), __LINE__, PLOG_GET_FILE(), PLOG_GET_THIS(), instanceId).ref() #define PLOG(severity) PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity) #define PLOG_VERBOSE PLOG(plog::verbose) #define PLOG_DEBUG PLOG(plog::debug) #define PLOG_INFO PLOG(plog::info) #define PLOG_WARNING PLOG(plog::warning) #define PLOG_ERROR PLOG(plog::error) #define PLOG_FATAL PLOG(plog::fatal) #define PLOG_NONE PLOG(plog::none) #define PLOG_VERBOSE_(instanceId) PLOG_(instanceId, plog::verbose) #define PLOG_DEBUG_(instanceId) PLOG_(instanceId, plog::debug) #define PLOG_INFO_(instanceId) PLOG_(instanceId, plog::info) #define PLOG_WARNING_(instanceId) PLOG_(instanceId, plog::warning) #define PLOG_ERROR_(instanceId) PLOG_(instanceId, plog::error) #define PLOG_FATAL_(instanceId) PLOG_(instanceId, plog::fatal) #define PLOG_NONE_(instanceId) PLOG_(instanceId, plog::none) #define PLOGV PLOG_VERBOSE #define PLOGD PLOG_DEBUG #define PLOGI PLOG_INFO #define PLOGW PLOG_WARNING #define PLOGE PLOG_ERROR #define PLOGF PLOG_FATAL #define PLOGN PLOG_NONE #define PLOGV_(instanceId) PLOG_VERBOSE_(instanceId) #define PLOGD_(instanceId) PLOG_DEBUG_(instanceId) #define PLOGI_(instanceId) PLOG_INFO_(instanceId) #define PLOGW_(instanceId) PLOG_WARNING_(instanceId) #define PLOGE_(instanceId) PLOG_ERROR_(instanceId) #define PLOGF_(instanceId) PLOG_FATAL_(instanceId) #define PLOGN_(instanceId) PLOG_NONE_(instanceId) ////////////////////////////////////////////////////////////////////////// // Conditional logging macros #define PLOG_IF_(instanceId, severity, condition) if (!(condition)) {;} else PLOG_(instanceId, severity) #define PLOG_IF(severity, condition) PLOG_IF_(PLOG_DEFAULT_INSTANCE_ID, severity, condition) #define PLOG_VERBOSE_IF(condition) PLOG_IF(plog::verbose, condition) #define PLOG_DEBUG_IF(condition) PLOG_IF(plog::debug, condition) #define PLOG_INFO_IF(condition) PLOG_IF(plog::info, condition) #define PLOG_WARNING_IF(condition) PLOG_IF(plog::warning, condition) #define PLOG_ERROR_IF(condition) PLOG_IF(plog::error, condition) #define PLOG_FATAL_IF(condition) PLOG_IF(plog::fatal, condition) #define PLOG_NONE_IF(condition) PLOG_IF(plog::none, condition) #define PLOG_VERBOSE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::verbose, condition) #define PLOG_DEBUG_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::debug, condition) #define PLOG_INFO_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::info, condition) #define PLOG_WARNING_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::warning, condition) #define PLOG_ERROR_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::error, condition) #define PLOG_FATAL_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::fatal, condition) #define PLOG_NONE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::none, condition) #define PLOGV_IF(condition) PLOG_VERBOSE_IF(condition) #define PLOGD_IF(condition) PLOG_DEBUG_IF(condition) #define PLOGI_IF(condition) PLOG_INFO_IF(condition) #define PLOGW_IF(condition) PLOG_WARNING_IF(condition) #define PLOGE_IF(condition) PLOG_ERROR_IF(condition) #define PLOGF_IF(condition) PLOG_FATAL_IF(condition) #define PLOGN_IF(condition) PLOG_NONE_IF(condition) #define PLOGV_IF_(instanceId, condition) PLOG_VERBOSE_IF_(instanceId, condition) #define PLOGD_IF_(instanceId, condition) PLOG_DEBUG_IF_(instanceId, condition) #define PLOGI_IF_(instanceId, condition) PLOG_INFO_IF_(instanceId, condition) #define PLOGW_IF_(instanceId, condition) PLOG_WARNING_IF_(instanceId, condition) #define PLOGE_IF_(instanceId, condition) PLOG_ERROR_IF_(instanceId, condition) #define PLOGF_IF_(instanceId, condition) PLOG_FATAL_IF_(instanceId, condition) #define PLOGN_IF_(instanceId, condition) PLOG_NONE_IF_(instanceId, condition) // Old macro names for downward compatibility. To bypass including these macro names, add // #define PLOG_OMIT_LOG_DEFINES before #include #ifndef PLOG_OMIT_LOG_DEFINES ////////////////////////////////////////////////////////////////////////// // Main logging macros - can be changed later to point at macros for a different logging package #define LOG_(instanceId, severity) IF_PLOG_(instanceId, severity) (*plog::get()) += plog::Record(severity, PLOG_GET_FUNC(), __LINE__, PLOG_GET_FILE(), PLOG_GET_THIS(), instanceId).ref() #define LOG(severity) PLOG_(PLOG_DEFAULT_INSTANCE_ID, severity) #define LOG_VERBOSE PLOG(plog::verbose) #define LOG_DEBUG PLOG(plog::debug) #define LOG_INFO PLOG(plog::info) #define LOG_WARNING PLOG(plog::warning) #define LOG_ERROR PLOG(plog::error) #define LOG_FATAL PLOG(plog::fatal) #define LOG_NONE PLOG(plog::none) #define LOG_VERBOSE_(instanceId) PLOG_(instanceId, plog::verbose) #define LOG_DEBUG_(instanceId) PLOG_(instanceId, plog::debug) #define LOG_INFO_(instanceId) PLOG_(instanceId, plog::info) #define LOG_WARNING_(instanceId) PLOG_(instanceId, plog::warning) #define LOG_ERROR_(instanceId) PLOG_(instanceId, plog::error) #define LOG_FATAL_(instanceId) PLOG_(instanceId, plog::fatal) #define LOG_NONE_(instanceId) PLOG_(instanceId, plog::none) #define LOGV PLOG_VERBOSE #define LOGD PLOG_DEBUG #define LOGI PLOG_INFO #define LOGW PLOG_WARNING #define LOGE PLOG_ERROR #define LOGF PLOG_FATAL #define LOGN PLOG_NONE #define LOGV_(instanceId) PLOG_VERBOSE_(instanceId) #define LOGD_(instanceId) PLOG_DEBUG_(instanceId) #define LOGI_(instanceId) PLOG_INFO_(instanceId) #define LOGW_(instanceId) PLOG_WARNING_(instanceId) #define LOGE_(instanceId) PLOG_ERROR_(instanceId) #define LOGF_(instanceId) PLOG_FATAL_(instanceId) #define LOGN_(instanceId) PLOG_NONE_(instanceId) ////////////////////////////////////////////////////////////////////////// // Conditional logging macros #define LOG_IF_(instanceId, severity, condition) if (!(condition)) {;} else PLOG_(instanceId, severity) #define LOG_IF(severity, condition) PLOG_IF_(PLOG_DEFAULT_INSTANCE_ID, severity, condition) #define LOG_VERBOSE_IF(condition) PLOG_IF(plog::verbose, condition) #define LOG_DEBUG_IF(condition) PLOG_IF(plog::debug, condition) #define LOG_INFO_IF(condition) PLOG_IF(plog::info, condition) #define LOG_WARNING_IF(condition) PLOG_IF(plog::warning, condition) #define LOG_ERROR_IF(condition) PLOG_IF(plog::error, condition) #define LOG_FATAL_IF(condition) PLOG_IF(plog::fatal, condition) #define LOG_NONE_IF(condition) PLOG_IF(plog::none, condition) #define LOG_VERBOSE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::verbose, condition) #define LOG_DEBUG_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::debug, condition) #define LOG_INFO_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::info, condition) #define LOG_WARNING_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::warning, condition) #define LOG_ERROR_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::error, condition) #define LOG_FATAL_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::fatal, condition) #define LOG_NONE_IF_(instanceId, condition) PLOG_IF_(instanceId, plog::none, condition) #define LOGV_IF(condition) PLOG_VERBOSE_IF(condition) #define LOGD_IF(condition) PLOG_DEBUG_IF(condition) #define LOGI_IF(condition) PLOG_INFO_IF(condition) #define LOGW_IF(condition) PLOG_WARNING_IF(condition) #define LOGE_IF(condition) PLOG_ERROR_IF(condition) #define LOGF_IF(condition) PLOG_FATAL_IF(condition) #define LOGN_IF(condition) PLOG_NONE_IF(condition) #define LOGV_IF_(instanceId, condition) PLOG_VERBOSE_IF_(instanceId, condition) #define LOGD_IF_(instanceId, condition) PLOG_DEBUG_IF_(instanceId, condition) #define LOGI_IF_(instanceId, condition) PLOG_INFO_IF_(instanceId, condition) #define LOGW_IF_(instanceId, condition) PLOG_WARNING_IF_(instanceId, condition) #define LOGE_IF_(instanceId, condition) PLOG_ERROR_IF_(instanceId, condition) #define LOGF_IF_(instanceId, condition) PLOG_FATAL_IF_(instanceId, condition) #define LOGN_IF_(instanceId, condition) PLOG_NONE_IF_(instanceId, condition) #endif plog-1.1.10/include/plog/Logger.h000066400000000000000000000037611447036265000165250ustar00rootroot00000000000000#pragma once #include #include #include #ifdef PLOG_DEFAULT_INSTANCE // for backward compatibility # define PLOG_DEFAULT_INSTANCE_ID PLOG_DEFAULT_INSTANCE #endif #ifndef PLOG_DEFAULT_INSTANCE_ID # define PLOG_DEFAULT_INSTANCE_ID 0 #endif namespace plog { template class PLOG_LINKAGE Logger : public util::Singleton >, public IAppender { public: Logger(Severity maxSeverity = none) : m_maxSeverity(maxSeverity) { } Logger& addAppender(IAppender* appender) { assert(appender != this); m_appenders.push_back(appender); return *this; } Severity getMaxSeverity() const { return m_maxSeverity; } void setMaxSeverity(Severity severity) { m_maxSeverity = severity; } bool checkSeverity(Severity severity) const { return severity <= m_maxSeverity; } virtual void write(const Record& record) PLOG_OVERRIDE { if (checkSeverity(record.getSeverity())) { *this += record; } } void operator+=(const Record& record) { for (std::vector::iterator it = m_appenders.begin(); it != m_appenders.end(); ++it) { (*it)->write(record); } } private: Severity m_maxSeverity; #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4251) // needs to have dll-interface to be used by clients of class #endif std::vector m_appenders; #ifdef _MSC_VER # pragma warning(pop) #endif }; template inline Logger* get() { return Logger::getInstance(); } inline Logger* get() { return Logger::getInstance(); } } plog-1.1.10/include/plog/Record.h000066400000000000000000000313651447036265000165250ustar00rootroot00000000000000#pragma once #include #include #include #ifdef __cplusplus_cli #include // For PtrToStringChars #endif namespace plog { namespace detail { #if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` namespace meta { template inline T& declval() { #ifdef __INTEL_COMPILER # pragma warning(suppress: 327) // NULL reference is not allowed #endif return *reinterpret_cast(0); } template struct enableIf {}; template struct enableIf { typedef T type; }; struct No { char a[1]; }; struct Yes { char a[2]; }; template struct isConvertible { // `+ sizeof(U*)` is required for GCC 4.5-4.7 template static typename enableIf(meta::declval())) + sizeof(U*)), Yes>::type test(int); template static No test(...); enum { value = sizeof(test(0)) == sizeof(Yes) }; }; template struct isConvertibleToNString : isConvertible {}; template struct isConvertibleToString : isConvertible {}; template struct isContainer { template static typename meta::enableIf().begin()) + sizeof(meta::declval().end() #else typename U::const_iterator #endif )), Yes>::type test(int); template static No test(...); enum { value = sizeof(test(0)) == sizeof(Yes) }; }; // Detects `std::filesystem::path` and `boost::filesystem::path`. They look like containers // but we don't want to treat them as containers, so we use this detector to filter them out. template struct isFilesystemPath { template static typename meta::enableIf().preferred_separator)), Yes>::type test(int); template static No test(...); enum { value = sizeof(test(0)) == sizeof(Yes) }; }; } #endif ////////////////////////////////////////////////////////////////////////// // Stream output operators as free functions #if PLOG_ENABLE_WCHAR_INPUT inline void operator<<(util::nostringstream& stream, const wchar_t* data) { data = data ? data : L"(null)"; # ifdef _WIN32 # if PLOG_CHAR_IS_UTF8 std::operator<<(stream, util::toNarrow(data, codePage::kUTF8)); # else std::operator<<(stream, data); # endif # else std::operator<<(stream, util::toNarrow(data)); # endif } inline void operator<<(util::nostringstream& stream, wchar_t* data) { plog::detail::operator<<(stream, const_cast(data)); } inline void operator<<(util::nostringstream& stream, const std::wstring& data) { plog::detail::operator<<(stream, data.c_str()); } #endif inline void operator<<(util::nostringstream& stream, const char* data) { data = data ? data : "(null)"; #if defined(_WIN32) && defined(__BORLANDC__) # if PLOG_CHAR_IS_UTF8 stream << data; # else stream << util::toWide(data); # endif #elif defined(_WIN32) # if PLOG_CHAR_IS_UTF8 std::operator<<(stream, data); # else std::operator<<(stream, util::toWide(data)); # endif #else std::operator<<(stream, data); #endif } inline void operator<<(util::nostringstream& stream, char* data) { plog::detail::operator<<(stream, const_cast(data)); } inline void operator<<(util::nostringstream& stream, const std::string& data) { plog::detail::operator<<(stream, data.c_str()); } #ifdef __cpp_char8_t inline void operator<<(util::nostringstream& stream, const char8_t* data) { # if PLOG_CHAR_IS_UTF8 plog::detail::operator<<(stream, reinterpret_cast(data)); # else plog::detail::operator<<(stream, util::toWide(reinterpret_cast(data), codePage::kUTF8)); # endif } #endif //__cpp_char8_t // Print `std::pair` template inline void operator<<(util::nostringstream& stream, const std::pair& data) { stream << data.first; stream << ":"; stream << data.second; } #if defined(__clang__) || !defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 // skip for GCC < 4.5 due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38600 #if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` // Print data that can be casted to `std::basic_string`. template inline typename meta::enableIf::value, void>::type operator<<(util::nostringstream& stream, const T& data) { plog::detail::operator<<(stream, static_cast(data)); } // Print std containers template inline typename meta::enableIf::value && !meta::isConvertibleToNString::value && !meta::isConvertibleToString::value && !meta::isFilesystemPath::value, void>::type operator<<(util::nostringstream& stream, const T& data) { stream << "["; for (typename T::const_iterator it = data.begin(); it != data.end();) { stream << *it; if (++it == data.end()) { break; } stream << ", "; } stream << "]"; } #endif #endif #ifdef __cplusplus_cli inline void operator<<(util::nostringstream& stream, System::String^ data) { cli::pin_ptr ptr = PtrToStringChars(data); plog::detail::operator<<(stream, static_cast(ptr)); } #endif #if defined(_WIN32) && (!defined(_MSC_VER) || _MSC_VER > 1400) // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` namespace meta { template struct valueType { enum { value = Value }; }; template inline No operator<<(Stream&, const T&); template struct isStreamable : valueType(), meta::declval())) != sizeof(No)> {}; template struct isStreamable : valueType {}; template struct isStreamable : valueType {}; template struct isStreamable : valueType {}; // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const wchar_t*) = delete` so exlicitly define it template<> struct isStreamable : valueType {}; # ifdef __cpp_char8_t // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const char8_t*) = delete` so exlicitly define it template struct isStreamable : valueType {}; template struct isStreamable : valueType {}; # endif //__cpp_char8_t } template inline typename meta::enableIf::value && !meta::isStreamable::value, void>::type operator<<(std::wostringstream& stream, const T& data) { std::ostringstream ss; ss << data; stream << ss.str(); } #endif } class Record { public: Record(Severity severity, const char* func, size_t line, const char* file, const void* object, int instanceId) : m_severity(severity), m_tid(util::gettid()), m_object(object), m_line(line), m_func(func), m_file(file), m_instanceId(instanceId) { util::ftime(&m_time); } Record& ref() { return *this; } ////////////////////////////////////////////////////////////////////////// // Stream output operators Record& operator<<(char data) { char str[] = { data, 0 }; return *this << str; } #if PLOG_ENABLE_WCHAR_INPUT Record& operator<<(wchar_t data) { wchar_t str[] = { data, 0 }; return *this << str; } #endif Record& operator<<(util::nostream& (PLOG_CDECL *data)(util::nostream&)) { m_message << data; return *this; } #ifdef QT_VERSION Record& operator<<(const QString& data) { # if PLOG_CHAR_IS_UTF8 return *this << data.toStdString(); # else return *this << data.toStdWString(); # endif } # if QT_VERSION < 0x060000 Record& operator<<(const QStringRef& data) { return *this << data.toString(); } # endif # ifdef QSTRINGVIEW_H Record& operator<<(QStringView data) { return *this << data.toString(); } # endif #endif template Record& operator<<(const T& data) { using namespace plog::detail; m_message << data; return *this; } #ifndef __cplusplus_cli Record& printf(const char* format, ...) { using namespace util; char* str = NULL; va_list ap; va_start(ap, format); int len = vasprintf(&str, format, ap); static_cast(len); va_end(ap); *this << str; free(str); return *this; } #ifdef _WIN32 Record& printf(const wchar_t* format, ...) { using namespace util; wchar_t* str = NULL; va_list ap; va_start(ap, format); int len = vaswprintf(&str, format, ap); static_cast(len); va_end(ap); *this << str; free(str); return *this; } #endif #endif //__cplusplus_cli ////////////////////////////////////////////////////////////////////////// // Getters virtual const util::Time& getTime() const { return m_time; } virtual Severity getSeverity() const { return m_severity; } virtual unsigned int getTid() const { return m_tid; } virtual const void* getObject() const { return m_object; } virtual size_t getLine() const { return m_line; } virtual const util::nchar* getMessage() const { m_messageStr = m_message.str(); return m_messageStr.c_str(); } virtual const char* getFunc() const { m_funcStr = util::processFuncName(m_func); return m_funcStr.c_str(); } virtual const char* getFile() const { return m_file; } virtual ~Record() // virtual destructor to satisfy -Wnon-virtual-dtor warning { } virtual int getInstanceId() const { return m_instanceId; } private: util::Time m_time; const Severity m_severity; const unsigned int m_tid; const void* const m_object; const size_t m_line; util::nostringstream m_message; const char* const m_func; const char* const m_file; const int m_instanceId; mutable std::string m_funcStr; mutable util::nstring m_messageStr; }; } plog-1.1.10/include/plog/Severity.h000066400000000000000000000022551447036265000171150ustar00rootroot00000000000000#pragma once #include namespace plog { enum Severity { none = 0, fatal = 1, error = 2, warning = 3, info = 4, debug = 5, verbose = 6 }; #ifdef _MSC_VER # pragma warning(suppress: 26812) // Prefer 'enum class' over 'enum' #endif inline const char* severityToString(Severity severity) { switch (severity) { case fatal: return "FATAL"; case error: return "ERROR"; case warning: return "WARN"; case info: return "INFO"; case debug: return "DEBUG"; case verbose: return "VERB"; default: return "NONE"; } } inline Severity severityFromString(const char* str) { switch (std::toupper(str[0])) { case 'F': return fatal; case 'E': return error; case 'W': return warning; case 'I': return info; case 'D': return debug; case 'V': return verbose; default: return none; } } } plog-1.1.10/include/plog/Util.h000066400000000000000000000404201447036265000162140ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #ifndef PLOG_ENABLE_WCHAR_INPUT # ifdef _WIN32 # define PLOG_ENABLE_WCHAR_INPUT 1 # else # define PLOG_ENABLE_WCHAR_INPUT 0 # endif #endif ////////////////////////////////////////////////////////////////////////// // PLOG_CHAR_IS_UTF8 specifies character encoding of `char` type. On *nix // systems it's set to UTF-8 while on Windows in can be ANSI or UTF-8. It // automatically detects `/utf-8` command line option in MSVC. Also it can // be set manually if required. // This option allows to support http://utf8everywhere.org approach. #ifndef PLOG_CHAR_IS_UTF8 # if defined(_WIN32) && !defined(_UTF8) # define PLOG_CHAR_IS_UTF8 0 # else # define PLOG_CHAR_IS_UTF8 1 # endif #endif #ifdef _WIN32 # if defined(PLOG_EXPORT) # define PLOG_LINKAGE __declspec(dllexport) # elif defined(PLOG_IMPORT) # define PLOG_LINKAGE __declspec(dllimport) # endif # if defined(PLOG_GLOBAL) # error "PLOG_GLOBAL isn't supported on Windows" # endif #else # if defined(PLOG_GLOBAL) # define PLOG_LINKAGE __attribute__ ((visibility ("default"))) # elif defined(PLOG_LOCAL) # define PLOG_LINKAGE __attribute__ ((visibility ("hidden"))) # define PLOG_LINKAGE_HIDDEN PLOG_LINKAGE # endif # if defined(PLOG_EXPORT) || defined(PLOG_IMPORT) # error "PLOG_EXPORT/PLOG_IMPORT is supported only on Windows" # endif #endif #ifndef PLOG_LINKAGE # define PLOG_LINKAGE #endif #ifndef PLOG_LINKAGE_HIDDEN # define PLOG_LINKAGE_HIDDEN #endif #ifdef _WIN32 # include # include # include # include # include #else # include # include # if defined(__linux__) || defined(__FreeBSD__) # include # elif defined(__rtems__) # include # endif # if defined(_POSIX_THREADS) # include # endif # if PLOG_ENABLE_WCHAR_INPUT # include # endif #endif #if PLOG_CHAR_IS_UTF8 # define PLOG_NSTR(x) x #else # define _PLOG_NSTR(x) L##x # define PLOG_NSTR(x) _PLOG_NSTR(x) #endif #ifdef _WIN32 # define PLOG_CDECL __cdecl #else # define PLOG_CDECL #endif #if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER >= 1700 # define PLOG_OVERRIDE override #else # define PLOG_OVERRIDE #endif namespace plog { namespace util { #if PLOG_CHAR_IS_UTF8 typedef std::string nstring; typedef std::ostringstream nostringstream; typedef std::istringstream nistringstream; typedef std::ostream nostream; typedef char nchar; #else typedef std::wstring nstring; typedef std::wostringstream nostringstream; typedef std::wistringstream nistringstream; typedef std::wostream nostream; typedef wchar_t nchar; #endif inline void localtime_s(struct tm* t, const time_t* time) { #if defined(_WIN32) && defined(__BORLANDC__) ::localtime_s(time, t); #elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) *t = *::localtime(time); #elif defined(_WIN32) ::localtime_s(t, time); #else ::localtime_r(time, t); #endif } inline void gmtime_s(struct tm* t, const time_t* time) { #if defined(_WIN32) && defined(__BORLANDC__) ::gmtime_s(time, t); #elif defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) *t = *::gmtime(time); #elif defined(_WIN32) ::gmtime_s(t, time); #else ::gmtime_r(time, t); #endif } #ifdef _WIN32 typedef timeb Time; inline void ftime(Time* t) { ::ftime(t); } #else struct Time { time_t time; unsigned short millitm; }; inline void ftime(Time* t) { timeval tv; ::gettimeofday(&tv, NULL); t->time = tv.tv_sec; t->millitm = static_cast(tv.tv_usec / 1000); } #endif inline unsigned int gettid() { #ifdef _WIN32 return GetCurrentThreadId(); #elif defined(__linux__) return static_cast(::syscall(__NR_gettid)); #elif defined(__FreeBSD__) long tid; syscall(SYS_thr_self, &tid); return static_cast(tid); #elif defined(__rtems__) return rtems_task_self(); #elif defined(__APPLE__) uint64_t tid64; pthread_threadid_np(NULL, &tid64); return static_cast(tid64); #else return 0; #endif } #ifndef _GNU_SOURCE inline int vasprintf(char** strp, const char* format, va_list ap) { va_list ap_copy; #if defined(_MSC_VER) && _MSC_VER <= 1600 ap_copy = ap; // there is no va_copy on Visual Studio 2010 #else va_copy(ap_copy, ap); #endif #ifndef __STDC_SECURE_LIB__ int charCount = vsnprintf(NULL, 0, format, ap_copy); #else int charCount = _vscprintf(format, ap_copy); #endif va_end(ap_copy); if (charCount < 0) { return -1; } size_t bufferCharCount = static_cast(charCount) + 1; char* str = static_cast(malloc(bufferCharCount)); if (!str) { return -1; } #ifndef __STDC_SECURE_LIB__ int retval = vsnprintf(str, bufferCharCount, format, ap); #else int retval = vsnprintf_s(str, bufferCharCount, static_cast(-1), format, ap); #endif if (retval < 0) { free(str); return -1; } *strp = str; return retval; } #endif #ifdef _WIN32 inline int vaswprintf(wchar_t** strp, const wchar_t* format, va_list ap) { #if defined(__BORLANDC__) int charCount = 0x1000; // there is no _vscwprintf on Borland/Embarcadero #else int charCount = _vscwprintf(format, ap); if (charCount < 0) { return -1; } #endif size_t bufferCharCount = static_cast(charCount) + 1; wchar_t* str = static_cast(malloc(bufferCharCount * sizeof(wchar_t))); if (!str) { return -1; } #if defined(__BORLANDC__) int retval = vsnwprintf_s(str, bufferCharCount, format, ap); #elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) int retval = _vsnwprintf(str, bufferCharCount, format, ap); #else int retval = _vsnwprintf_s(str, bufferCharCount, charCount, format, ap); #endif if (retval < 0) { free(str); return -1; } *strp = str; return retval; } #endif #ifdef _WIN32 inline std::wstring toWide(const char* str, UINT cp = codePage::kChar) { size_t len = ::strlen(str); std::wstring wstr(len, 0); if (!wstr.empty()) { int wlen = MultiByteToWideChar(cp, 0, str, static_cast(len), &wstr[0], static_cast(wstr.size())); wstr.resize(wlen); } return wstr; } inline std::wstring toWide(const std::string& str, UINT cp = codePage::kChar) { return toWide(str.c_str(), cp); } inline const std::wstring& toWide(const std::wstring& str) // do nothing for already wide string { return str; } inline std::string toNarrow(const std::wstring& wstr, long page) { int len = WideCharToMultiByte(page, 0, wstr.c_str(), static_cast(wstr.size()), 0, 0, 0, 0); std::string str(len, 0); if (!str.empty()) { WideCharToMultiByte(page, 0, wstr.c_str(), static_cast(wstr.size()), &str[0], len, 0, 0); } return str; } #elif PLOG_ENABLE_WCHAR_INPUT inline std::string toNarrow(const wchar_t* wstr) { size_t wlen = ::wcslen(wstr); std::string str(wlen * sizeof(wchar_t), 0); if (!str.empty()) { const char* in = reinterpret_cast(&wstr[0]); char* out = &str[0]; size_t inBytes = wlen * sizeof(wchar_t); size_t outBytes = str.size(); iconv_t cd = ::iconv_open("UTF-8", "WCHAR_T"); ::iconv(cd, const_cast(&in), &inBytes, &out, &outBytes); ::iconv_close(cd); str.resize(str.size() - outBytes); } return str; } #endif inline std::string processFuncName(const char* func) { #if (defined(_WIN32) && !defined(__MINGW32__)) || defined(__OBJC__) return std::string(func); #else const char* funcBegin = func; const char* funcEnd = ::strchr(funcBegin, '('); int foundTemplate = 0; if (!funcEnd) { return std::string(func); } for (const char* i = funcEnd - 1; i >= funcBegin; --i) // search backwards for the first space char { if (*i == '>') { foundTemplate++; } else if (*i == '<') { foundTemplate--; } else if (*i == ' ' && foundTemplate == 0) { funcBegin = i + 1; break; } } return std::string(funcBegin, funcEnd); #endif } inline const nchar* findExtensionDot(const nchar* fileName) { #if PLOG_CHAR_IS_UTF8 return std::strrchr(fileName, '.'); #else return std::wcsrchr(fileName, L'.'); #endif } inline void splitFileName(const nchar* fileName, nstring& fileNameNoExt, nstring& fileExt) { const nchar* dot = findExtensionDot(fileName); if (dot) { fileNameNoExt.assign(fileName, dot); fileExt.assign(dot + 1); } else { fileNameNoExt.assign(fileName); fileExt.clear(); } } class PLOG_LINKAGE NonCopyable { protected: NonCopyable() { } private: NonCopyable(const NonCopyable&); NonCopyable& operator=(const NonCopyable&); }; class PLOG_LINKAGE_HIDDEN File : NonCopyable { public: File() : m_file(-1) { } ~File() { close(); } size_t open(const nstring& fileName) { #if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__)) m_file = ::_wsopen(toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, SH_DENYWR, _S_IREAD | _S_IWRITE); #elif defined(_WIN32) ::_wsopen_s(&m_file, toWide(fileName).c_str(), _O_CREAT | _O_WRONLY | _O_BINARY | _O_NOINHERIT, _SH_DENYWR, _S_IREAD | _S_IWRITE); #elif defined(O_CLOEXEC) m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); #else m_file = ::open(fileName.c_str(), O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); #endif return seek(0, SEEK_END); } size_t write(const void* buf, size_t count) { return m_file != -1 ? static_cast( #ifdef _WIN32 ::_write(m_file, buf, static_cast(count)) #else ::write(m_file, buf, count) #endif ) : static_cast(-1); } template size_t write(const std::basic_string& str) { return write(str.data(), str.size() * sizeof(CharType)); } size_t seek(size_t offset, int whence) { return m_file != -1 ? static_cast( #if defined(_WIN32) && (defined(__BORLANDC__) || defined(__MINGW32__)) ::_lseek(m_file, static_cast(offset), whence) #elif defined(_WIN32) ::_lseeki64(m_file, static_cast(offset), whence) #else ::lseek(m_file, static_cast(offset), whence) #endif ) : static_cast(-1); } void close() { if (m_file != -1) { #ifdef _WIN32 ::_close(m_file); #else ::close(m_file); #endif m_file = -1; } } static int unlink(const nstring& fileName) { #ifdef _WIN32 return ::_wunlink(toWide(fileName).c_str()); #else return ::unlink(fileName.c_str()); #endif } static int rename(const nstring& oldFilename, const nstring& newFilename) { #ifdef _WIN32 return MoveFileW(toWide(oldFilename).c_str(), toWide(newFilename).c_str()); #else return ::rename(oldFilename.c_str(), newFilename.c_str()); #endif } private: int m_file; }; class PLOG_LINKAGE_HIDDEN Mutex : NonCopyable { public: Mutex() { #ifdef _WIN32 InitializeCriticalSection(&m_sync); #elif defined(__rtems__) rtems_semaphore_create(0, 1, RTEMS_PRIORITY | RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY, 1, &m_sync); #elif defined(_POSIX_THREADS) ::pthread_mutex_init(&m_sync, 0); #endif } ~Mutex() { #ifdef _WIN32 DeleteCriticalSection(&m_sync); #elif defined(__rtems__) rtems_semaphore_delete(m_sync); #elif defined(_POSIX_THREADS) ::pthread_mutex_destroy(&m_sync); #endif } friend class MutexLock; private: void lock() { #ifdef _WIN32 EnterCriticalSection(&m_sync); #elif defined(__rtems__) rtems_semaphore_obtain(m_sync, RTEMS_WAIT, RTEMS_NO_TIMEOUT); #elif defined(_POSIX_THREADS) ::pthread_mutex_lock(&m_sync); #endif } void unlock() { #ifdef _WIN32 LeaveCriticalSection(&m_sync); #elif defined(__rtems__) rtems_semaphore_release(m_sync); #elif defined(_POSIX_THREADS) ::pthread_mutex_unlock(&m_sync); #endif } private: #ifdef _WIN32 CRITICAL_SECTION m_sync; #elif defined(__rtems__) rtems_id m_sync; #elif defined(_POSIX_THREADS) pthread_mutex_t m_sync; #endif }; class PLOG_LINKAGE_HIDDEN MutexLock : NonCopyable { public: MutexLock(Mutex& mutex) : m_mutex(mutex) { m_mutex.lock(); } ~MutexLock() { m_mutex.unlock(); } private: Mutex& m_mutex; }; template #ifdef _WIN32 class Singleton : NonCopyable #else class PLOG_LINKAGE Singleton : NonCopyable #endif { public: #if (defined(__clang__) || defined(__GNUC__) && __GNUC__ >= 8) && !defined(__BORLANDC__) // This constructor is called before the `T` object is fully constructed, and // pointers are not dereferenced anyway, so UBSan shouldn't check vptrs. __attribute__((no_sanitize("vptr"))) #endif Singleton() { assert(!m_instance); m_instance = static_cast(this); } ~Singleton() { assert(m_instance); m_instance = 0; } static T* getInstance() { return m_instance; } private: static T* m_instance; }; template T* Singleton::m_instance = NULL; } } plog-1.1.10/include/plog/WinApi.h000066400000000000000000000141261447036265000164720ustar00rootroot00000000000000#pragma once #ifdef _WIN32 // These windows structs must be in a global namespace struct HKEY__; struct _SECURITY_ATTRIBUTES; struct _CONSOLE_SCREEN_BUFFER_INFO; struct _RTL_CRITICAL_SECTION; namespace plog { typedef unsigned long DWORD; typedef unsigned short WORD; typedef unsigned char BYTE; typedef unsigned int UINT; typedef int BOOL; typedef long LSTATUS; typedef char* LPSTR; typedef wchar_t* LPWSTR; typedef const char* LPCSTR; typedef const wchar_t* LPCWSTR; typedef void* HANDLE; typedef HKEY__* HKEY; typedef size_t ULONG_PTR; struct CRITICAL_SECTION { void* DebugInfo; long LockCount; long RecursionCount; HANDLE OwningThread; HANDLE LockSemaphore; ULONG_PTR SpinCount; }; struct COORD { short X; short Y; }; struct SMALL_RECT { short Left; short Top; short Right; short Bottom; }; struct CONSOLE_SCREEN_BUFFER_INFO { COORD dwSize; COORD dwCursorPosition; WORD wAttributes; SMALL_RECT srWindow; COORD dwMaximumWindowSize; }; namespace codePage { const UINT kActive = 0; const UINT kUTF8 = 65001; #if PLOG_CHAR_IS_UTF8 const UINT kChar = kUTF8; #else const UINT kChar = kActive; #endif } namespace eventLog { const WORD kErrorType = 0x0001; const WORD kWarningType = 0x0002; const WORD kInformationType = 0x0004; } namespace hkey { const HKEY kLocalMachine = reinterpret_cast(static_cast(0x80000002)); } namespace regSam { const DWORD kQueryValue = 0x0001; const DWORD kSetValue = 0x0002; } namespace regType { const DWORD kExpandSz = 2; const DWORD kDword = 4; } namespace stdHandle { const DWORD kOutput = static_cast(-11); const DWORD kErrorOutput = static_cast(-12); } namespace foreground { const WORD kBlue = 0x0001; const WORD kGreen = 0x0002; const WORD kRed = 0x0004; const WORD kIntensity = 0x0008; } namespace background { const WORD kBlue = 0x0010; const WORD kGreen = 0x0020; const WORD kRed = 0x0040; const WORD kIntensity = 0x0080; } extern "C" { __declspec(dllimport) int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar); __declspec(dllimport) int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, const char* lpDefaultChar, BOOL* lpUsedDefaultChar); __declspec(dllimport) DWORD __stdcall GetCurrentThreadId(); __declspec(dllimport) BOOL __stdcall MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName); __declspec(dllimport) void __stdcall InitializeCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); __declspec(dllimport) void __stdcall EnterCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); __declspec(dllimport) void __stdcall LeaveCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); __declspec(dllimport) void __stdcall DeleteCriticalSection(_RTL_CRITICAL_SECTION* lpCriticalSection); __declspec(dllimport) HANDLE __stdcall RegisterEventSourceW(LPCWSTR lpUNCServerName, LPCWSTR lpSourceName); __declspec(dllimport) BOOL __stdcall DeregisterEventSource(HANDLE hEventLog); __declspec(dllimport) BOOL __stdcall ReportEventW(HANDLE hEventLog, WORD wType, WORD wCategory, DWORD dwEventID, void* lpUserSid, WORD wNumStrings, DWORD dwDataSize, LPCWSTR* lpStrings, void* lpRawData); __declspec(dllimport) LSTATUS __stdcall RegCreateKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD Reserved, LPWSTR lpClass, DWORD dwOptions, DWORD samDesired, _SECURITY_ATTRIBUTES* lpSecurityAttributes, HKEY* phkResult, DWORD* lpdwDisposition); __declspec(dllimport) LSTATUS __stdcall RegSetValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD Reserved, DWORD dwType, const BYTE* lpData, DWORD cbData); __declspec(dllimport) LSTATUS __stdcall RegCloseKey(HKEY hKey); __declspec(dllimport) LSTATUS __stdcall RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); __declspec(dllimport) LSTATUS __stdcall RegDeleteKeyW(HKEY hKey, LPCWSTR lpSubKey); __declspec(dllimport) HANDLE __stdcall GetStdHandle(DWORD nStdHandle); __declspec(dllimport) BOOL __stdcall WriteConsoleW(HANDLE hConsoleOutput, const void* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD* lpNumberOfCharsWritten, void* lpReserved); __declspec(dllimport) BOOL __stdcall GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, _CONSOLE_SCREEN_BUFFER_INFO* lpConsoleScreenBufferInfo); __declspec(dllimport) BOOL __stdcall SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes); __declspec(dllimport) void __stdcall OutputDebugStringW(LPCWSTR lpOutputString); } inline void InitializeCriticalSection(CRITICAL_SECTION* criticalSection) { plog::InitializeCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); } inline void EnterCriticalSection(CRITICAL_SECTION* criticalSection) { plog::EnterCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); } inline void LeaveCriticalSection(CRITICAL_SECTION* criticalSection) { plog::LeaveCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); } inline void DeleteCriticalSection(CRITICAL_SECTION* criticalSection) { plog::DeleteCriticalSection(reinterpret_cast<_RTL_CRITICAL_SECTION*>(criticalSection)); } inline BOOL GetConsoleScreenBufferInfo(HANDLE consoleOutput, CONSOLE_SCREEN_BUFFER_INFO* consoleScreenBufferInfo) { return plog::GetConsoleScreenBufferInfo(consoleOutput, reinterpret_cast<_CONSOLE_SCREEN_BUFFER_INFO*>(consoleScreenBufferInfo)); } } #endif // _WIN32 plog-1.1.10/library.json000066400000000000000000000025531447036265000151060ustar00rootroot00000000000000{ "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", "name": "plog", "version": "1.1.10", "description": "Portable, simple and extensible C++ logging library", "keywords": [ "plog", "log", "logger", "logging" ], "repository": { "type": "git", "url": "https://github.com/SergiusTheBest/plog.git" }, "authors": { "name": "Sergey Podobry", "email": "sergey.podobry@gmail.com", "maintainer": true }, "license": "MIT", "frameworks": "*", "platforms": "*", "headers": [ "plog/Log.h", "plog/Init.h", "plog/Appenders/ArduinoAppender.h", "plog/Formatters/CsvFormatter.h", "plog/Formatters/FuncMessageFormatter.h", "plog/Formatters/MessageOnlyFormatter.h", "plog/Formatters/TxtFormatter.h" ], "examples": [ { "name": "Arduino", "base": "samples/Arduino", "files": [ "platformio.ini", "src/main.cpp" ] } ], "export": { "include": [ "doc", "include", "samples/Arduino", "LICENSE", "README.md" ] }, "build": { "includeDir": "include" } } plog-1.1.10/plog.nuspec000066400000000000000000000016331447036265000147250ustar00rootroot00000000000000 plog $version$ Pretty powerful logging library in about 1000 lines of code SergiusTheBest https://github.com/SergiusTheBest/plog MIT false native log logging c++ cpp plog-1.1.10/plog.targets000066400000000000000000000006061447036265000151000ustar00rootroot00000000000000 $(MSBuildThisFileDirectory)../../lib/native/include;%(AdditionalIncludeDirectories) plog-1.1.10/samples/000077500000000000000000000000001447036265000142065ustar00rootroot00000000000000plog-1.1.10/samples/Android/000077500000000000000000000000001447036265000155665ustar00rootroot00000000000000plog-1.1.10/samples/Android/AndroidJNI/000077500000000000000000000000001447036265000175075ustar00rootroot00000000000000plog-1.1.10/samples/Android/AndroidJNI/Main.cpp000066400000000000000000000011521447036265000210760ustar00rootroot00000000000000// // AndroidJNI - shows how to use the Android appender in JNI. // #include #include #include #include #include extern "C" jint JNI_Onload(JavaVM*, void*) { static plog::AndroidAppender appender("MyApp"); // Create an appender and set a log tag. plog::init(plog::debug, &appender); // Initialize the logger with the appender. return JNI_VERSION_1_6; } extern "C" void Java_com_github_sergius_myapp_Sample_foo(JNIEnv*, jobject) { PLOGD << "Hello Android!"; } plog-1.1.10/samples/Android/AndroidNative/000077500000000000000000000000001447036265000203155ustar00rootroot00000000000000plog-1.1.10/samples/Android/AndroidNative/Main.cpp000066400000000000000000000011671447036265000217120ustar00rootroot00000000000000// // AndroidNative - shows how to use an Android appender. // #include #include #include #include int main() { static plog::AndroidAppender androidAppender("app"); plog::init(plog::verbose, &androidAppender); PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.10/samples/Android/CMakeLists.txt000066400000000000000000000007631447036265000203340ustar00rootroot00000000000000if(ANDROID) add_library(AndroidJNI SHARED AndroidJNI/Main.cpp) target_link_libraries(AndroidJNI plog) add_executable(AndroidNative AndroidNative/Main.cpp) target_link_libraries(AndroidNative plog) else() add_custom_target(AndroidJNI SOURCES AndroidJNI/Main.cpp) add_custom_target(AndroidNative SOURCES AndroidNative/Main.cpp) endif() set_target_properties(AndroidJNI PROPERTIES FOLDER Samples/Android) set_target_properties(AndroidNative PROPERTIES FOLDER Samples/Android) plog-1.1.10/samples/Arduino/000077500000000000000000000000001447036265000156075ustar00rootroot00000000000000plog-1.1.10/samples/Arduino/platformio.ini000066400000000000000000000011611447036265000204630ustar00rootroot00000000000000; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [platformio] include_dir = ../../include [env:esp8266] platform = espressif8266 board = nodemcuv2 framework = arduino [env:esp32] platform = espressif32 board = esp32dev framework = arduino [env:pico] platform = raspberrypi board = pico framework = arduino plog-1.1.10/samples/Arduino/src/000077500000000000000000000000001447036265000163765ustar00rootroot00000000000000plog-1.1.10/samples/Arduino/src/main.cpp000066400000000000000000000010071447036265000200240ustar00rootroot00000000000000#include #include #include #include #include static plog::ArduinoAppender arduinoAppender(Serial); void setup() { plog::init(plog::verbose, &arduinoAppender); PLOG_VERBOSE << "verbose"; PLOG_DEBUG << "debug"; PLOG_INFO << "info"; PLOG_WARNING << "warning"; PLOG_ERROR << "error"; PLOG_FATAL << "fatal"; } void loop() { delay(1000); // Wait for a second } plog-1.1.10/samples/AscDump/000077500000000000000000000000001447036265000155425ustar00rootroot00000000000000plog-1.1.10/samples/AscDump/CMakeLists.txt000066400000000000000000000001761447036265000203060ustar00rootroot00000000000000add_executable(AscDump Main.cpp) target_link_libraries(AscDump plog) set_target_properties(AscDump PROPERTIES FOLDER Samples) plog-1.1.10/samples/AscDump/Main.cpp000066400000000000000000000013271447036265000171350ustar00rootroot00000000000000// // AscDump - shows how to use plog::ascdump to dump binary buffers into ASCII. // #include #include #include #include #include int main() { plog::init(plog::verbose, plog::streamStdOut); std::vector v; v.push_back(0x6548); v.push_back(0x6c6c); v.push_back(0x216f); v.push_back(0); v.push_back(-1); PLOGI << "v: " << plog::ascdump(v); unsigned char arr[] = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0xff}; PLOGI << "arr: " << plog::ascdump(arr); void* p = malloc(100); PLOGI << "p: " << plog::ascdump(p, 100); return 0; } plog-1.1.10/samples/CMakeLists.txt000066400000000000000000000045571447036265000167610ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) if(POLICY CMP0063) #Honor visibility properties for all target types cmake_policy(SET CMP0063 NEW) endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) macro(checkObjCXX) file(WRITE "${CMAKE_BINARY_DIR}/CMakeFiles/dummy.mm" "int main(){return 0;}\n") execute_process( WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/CMakeFiles COMMAND ${CMAKE_CXX_COMPILER} dummy.mm RESULT_VARIABLE result ERROR_QUIET OUTPUT_QUIET ) file(REMOVE "${CMAKE_BINARY_DIR}/CMakeFiles/dummy.mm") if("${result}" STREQUAL "0") set(CMAKE_OBJCXX_AVAILABLE 1) message("-- ObjectiveC++ support is detected") endif() endmacro() project(Samples) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_COMPILER_IS_CLANGXX 1) endif() if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX") string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") elseif(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wconversion -Wnon-virtual-dtor -Wundef -pedantic -Werror") set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) checkObjCXX() if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsuggest-override") endif() if(CMAKE_COMPILER_IS_CLANGXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wnewline-eof") endif() endif() add_subdirectory(Android) add_subdirectory(AscDump) add_subdirectory(Chained) add_subdirectory(ColorConsole) add_subdirectory(CustomAppender) add_subdirectory(CustomConverter) add_subdirectory(CustomFormatter) add_subdirectory(CustomType) add_subdirectory(CXX11) add_subdirectory(CXX17) add_subdirectory(DebugOutput) add_subdirectory(Demo) add_subdirectory(DisableLogging) add_subdirectory(DynamicAppender) add_subdirectory(EventLog) add_subdirectory(Facilities) add_subdirectory(Hello) add_subdirectory(HexDump) add_subdirectory(Library) add_subdirectory(MultiAppender) add_subdirectory(MultiInstance) add_subdirectory(NotShared) add_subdirectory(ObjectiveC) add_subdirectory(Path) add_subdirectory(Performance) add_subdirectory(PrintVar) add_subdirectory(SetFileName) add_subdirectory(Shared) add_subdirectory(SkipNativeEOL) add_subdirectory(UtcTime) add_subdirectory(Utf8Everywhere) plog-1.1.10/samples/CXX11/000077500000000000000000000000001447036265000150125ustar00rootroot00000000000000plog-1.1.10/samples/CXX11/CMakeLists.txt000066400000000000000000000005341447036265000175540ustar00rootroot00000000000000if (NOT CMAKE_VERSION VERSION_LESS 3.3.0) cmake_minimum_required(VERSION 3.3) if(cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) add_executable(CXX11 Main.cpp) target_link_libraries(CXX11 plog::plog) set_target_properties(CXX11 PROPERTIES FOLDER Samples CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON) endif() endif() plog-1.1.10/samples/CXX11/Main.cpp000066400000000000000000000014511447036265000164030ustar00rootroot00000000000000// // CXX11 - demonstrates log stream abilities for C++11 features. // #include #include #include #include #include #include int main() { plog::init(plog::debug, plog::streamStdOut); // Initialize logging std::unordered_map unorderedMap; unorderedMap["red"] = 1; unorderedMap["green"] = 2; unorderedMap["blue"] = 4; PLOG_INFO << unorderedMap; std::unordered_set unorderedSet; unorderedSet.insert("red"); unorderedSet.insert("green"); unorderedSet.insert("blue"); PLOG_INFO << unorderedSet; std::array array = {{1, 2, 3, 4}}; PLOG_INFO << array; return 0; } plog-1.1.10/samples/CXX17/000077500000000000000000000000001447036265000150205ustar00rootroot00000000000000plog-1.1.10/samples/CXX17/CMakeLists.txt000066400000000000000000000014241447036265000175610ustar00rootroot00000000000000if (NOT CMAKE_VERSION VERSION_LESS 3.3.0) cmake_minimum_required(VERSION 3.3) # Unfortunately cxx_std_17 in CMAKE_CXX_COMPILE_FEATURES is not reliable, so check include files include(CheckIncludeFileCXX) CHECK_INCLUDE_FILE_CXX("string_view" HAS_STRING_VIEW) CHECK_INCLUDE_FILE_CXX("filesystem" HAS_FILESYSTEM) if(HAS_STRING_VIEW AND HAS_FILESYSTEM) add_executable(CXX17 Main.cpp) target_link_libraries(CXX17 plog::plog) set_target_properties(CXX17 PROPERTIES FOLDER Samples CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON) if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) target_link_libraries(CXX17 stdc++fs) endif() endif() endif() plog-1.1.10/samples/CXX17/Main.cpp000066400000000000000000000012451447036265000164120ustar00rootroot00000000000000// // CXX17 - demonstrates log stream abilities for C++17 features. // #include #include #include #include #include int main() { plog::init(plog::debug, plog::streamStdOut); // Initialize logging std::string_view strView = "string view"; PLOG_INFO << strView; #if PLOG_ENABLE_WCHAR_INPUT std::wstring_view wstrView = L"wstring view"; PLOG_INFO << wstrView; // on Linux is printed as a container, will be fixed in future #endif PLOG_INFO << "Current path: " << std::filesystem::current_path(); return 0; } plog-1.1.10/samples/Chained/000077500000000000000000000000001447036265000155415ustar00rootroot00000000000000plog-1.1.10/samples/Chained/CMakeLists.txt000066400000000000000000000013701447036265000203020ustar00rootroot00000000000000# some systems have no shared libraries support, so check it if(NOT DEFINED TARGET_SUPPORTS_SHARED_LIBS OR TARGET_SUPPORTS_SHARED_LIBS) add_executable(ChainedApp ChainedApp/Main.cpp) target_link_libraries(ChainedApp ChainedLib plog) set_target_properties(ChainedApp PROPERTIES FOLDER Samples/Chained) # set PLOG to local, so instances will not be shared across modules target_compile_definitions(ChainedApp PRIVATE PLOG_LOCAL) add_library(ChainedLib SHARED ChainedLib/Main.cpp) target_link_libraries(ChainedLib plog) set_target_properties(ChainedLib PROPERTIES FOLDER Samples/Chained) # set PLOG to local, so instances will not be shared across modules target_compile_definitions(ChainedLib PRIVATE PLOG_LOCAL) endif() plog-1.1.10/samples/Chained/ChainedApp/000077500000000000000000000000001447036265000175355ustar00rootroot00000000000000plog-1.1.10/samples/Chained/ChainedApp/Main.cpp000066400000000000000000000013051447036265000211240ustar00rootroot00000000000000// // Chained - shows how to chain a logger (route messages) in a shared library with the main logger. // #include #include // Functions imported form the shared library. extern "C" void initialize(plog::Severity severity, plog::IAppender* appender); extern "C" void foo(); int main() { plog::init(plog::debug, "ChainedApp.txt"); // Initialize the main logger. PLOGD << "Hello from app!"; // Write a log message. initialize(plog::debug, plog::get()); // Initialize the logger in the shared library. Note that it has its own severity. foo(); // Call a function from the shared library that produces a log message. return 0; } plog-1.1.10/samples/Chained/ChainedLib/000077500000000000000000000000001447036265000175235ustar00rootroot00000000000000plog-1.1.10/samples/Chained/ChainedLib/Main.cpp000066400000000000000000000012551447036265000211160ustar00rootroot00000000000000// // Chained - shows how to chain a logger (route messages) in a shared library with the main logger. // #include #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that initializes the logger in the shared library. extern "C" void EXPORT initialize(plog::Severity severity, plog::IAppender* appender) { plog::init(severity, appender); // Initialize the shared library logger. } // Function that produces a log message. extern "C" void EXPORT foo() { PLOGI << "Hello from shared lib!"; } plog-1.1.10/samples/ColorConsole/000077500000000000000000000000001447036265000166075ustar00rootroot00000000000000plog-1.1.10/samples/ColorConsole/CMakeLists.txt000066400000000000000000000002141447036265000213440ustar00rootroot00000000000000add_executable(ColorConsole Main.cpp) target_link_libraries(ColorConsole plog) set_target_properties(ColorConsole PROPERTIES FOLDER Samples)plog-1.1.10/samples/ColorConsole/Main.cpp000066400000000000000000000012721447036265000202010ustar00rootroot00000000000000// // ColorConsole - shows how to use a color console appender. // #include #include #include #include int main() { static plog::ColorConsoleAppender consoleAppender; plog::init(plog::verbose, &consoleAppender); // Log severity levels are printed in different colors. PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.10/samples/CustomAppender/000077500000000000000000000000001447036265000171375ustar00rootroot00000000000000plog-1.1.10/samples/CustomAppender/CMakeLists.txt000066400000000000000000000002221447036265000216730ustar00rootroot00000000000000add_executable(CustomAppender Main.cpp) target_link_libraries(CustomAppender plog) set_target_properties(CustomAppender PROPERTIES FOLDER Samples)plog-1.1.10/samples/CustomAppender/Main.cpp000066400000000000000000000023701447036265000205310ustar00rootroot00000000000000// // CustomAppender - shows how to implement a custom appender that stores log messages in memory. // #include #include #include #include namespace plog { template // Typically a formatter is passed as a template parameter. class MyAppender : public IAppender // All appenders MUST inherit IAppender interface. { public: virtual void write(const Record& record) PLOG_OVERRIDE // This is a method from IAppender that MUST be implemented. { util::nstring str = Formatter::format(record); // Use the formatter to get a string from a record. m_messageList.push_back(str); // Store a log message in a list. } std::list& getMessageList() { return m_messageList; } private: std::list m_messageList; }; } int main() { static plog::MyAppender myAppender; // Create our custom appender. plog::init(plog::debug, &myAppender); // Initialize the logger with our appender. PLOGD << "A debug message!"; myAppender.getMessageList(); // This returns a list of stored log messages. return 0; } plog-1.1.10/samples/CustomConverter/000077500000000000000000000000001447036265000173505ustar00rootroot00000000000000plog-1.1.10/samples/CustomConverter/CMakeLists.txt000066400000000000000000000002251447036265000221070ustar00rootroot00000000000000add_executable(CustomConverter Main.cpp) target_link_libraries(CustomConverter plog) set_target_properties(CustomConverter PROPERTIES FOLDER Samples)plog-1.1.10/samples/CustomConverter/Main.cpp000066400000000000000000000027011447036265000207400ustar00rootroot00000000000000// // CustomConverter - shows how to implement a custom converter that encrypts log messages. // #include #include #include #include namespace plog { class MyConverter { public: static std::string header(const util::nstring& str) { return convert(str); // We have no special header for a file, so just call convert. } static std::string convert(const util::nstring& str) { const std::string& in = UTF8Converter::convert(str); // Convert to UTF8 first as it is more compact. std::string out; out.resize(in.size()); // This is an encryption key. const char kKey[] = "\x56\x5a\x43\x4d\x5f\x81\x4c\x4e\x19\x29\x2e\x13\x7c\x31\x14\x17\x5d\x63\x32\x39"; // Simple XOR encryption. for (size_t i = 0; i < out.size(); ++i) { out[i] = in[i] ^ kKey[i % (sizeof(kKey) - 1)]; } return out; } }; } int main() { static plog::RollingFileAppender appender("CustomConverter.txt"); // Create an appender and pass our converter as a template parameter. plog::init(plog::debug, &appender); // Initialize the logger with the appender. PLOGD << "A debug message!"; PLOGD << "Another one debug message!"; return 0; } plog-1.1.10/samples/CustomFormatter/000077500000000000000000000000001447036265000173445ustar00rootroot00000000000000plog-1.1.10/samples/CustomFormatter/CMakeLists.txt000066400000000000000000000002251447036265000221030ustar00rootroot00000000000000add_executable(CustomFormatter Main.cpp) target_link_libraries(CustomFormatter plog) set_target_properties(CustomFormatter PROPERTIES FOLDER Samples)plog-1.1.10/samples/CustomFormatter/Main.cpp000066400000000000000000000015761447036265000207450ustar00rootroot00000000000000// // CustomFormatter - shows how to implement a custom formatter. // #include #include namespace plog { class MyFormatter { public: static util::nstring header() // This method returns a header for a new file. In our case it is empty. { return util::nstring(); } static util::nstring format(const Record& record) // This method returns a string from a record. { util::nostringstream ss; ss << record.getMessage() << "\n"; // Produce a simple string with a log message. return ss.str(); } }; } int main() { plog::init(plog::debug, "CustomFormatter.txt"); // Initialize the logger and pass our formatter as a template parameter to init function. PLOGD << "A debug message!"; return 0; } plog-1.1.10/samples/CustomType/000077500000000000000000000000001447036265000163225ustar00rootroot00000000000000plog-1.1.10/samples/CustomType/CMakeLists.txt000066400000000000000000000002061447036265000210600ustar00rootroot00000000000000add_executable(CustomType Main.cpp) target_link_libraries(CustomType plog) set_target_properties(CustomType PROPERTIES FOLDER Samples)plog-1.1.10/samples/CustomType/Main.cpp000066400000000000000000000014731447036265000177170ustar00rootroot00000000000000// // CustomType - shows how to print a custom type to the log stream. // #include #include struct Point // This is our custom type that we want to print to the log stream. { int x; int y; }; namespace plog { Record& operator<<(Record& record, const Point& pt) // Implement a stream operator for our type. { return record << "(" << pt.x << ";" << pt.y << ")"; } } int main() { plog::init(plog::debug, "CustomType.txt"); // Initialize the logger. Point pt1 = { 0, 0 }; Point pt2 = { 10, -5 }; PLOGI << "We've got a line with coords: " << pt1 << pt2; // Print our type to the log stream. PLOGI << pt1 << pt2 << " - test for a custom type at begin of the stream"; // Print our type to the log stream. return 0; } plog-1.1.10/samples/DebugOutput/000077500000000000000000000000001447036265000164555ustar00rootroot00000000000000plog-1.1.10/samples/DebugOutput/CMakeLists.txt000066400000000000000000000002471447036265000212200ustar00rootroot00000000000000if(WIN32) add_executable(DebugOutput Main.cpp) target_link_libraries(DebugOutput plog) set_target_properties(DebugOutput PROPERTIES FOLDER Samples) endif()plog-1.1.10/samples/DebugOutput/Main.cpp000066400000000000000000000010441447036265000200440ustar00rootroot00000000000000// // DebugOutput - shows how to use DebugOutputAppender to write to the windows debug output. // #include #include #include #include int main() { static plog::DebugOutputAppender debugOutputAppender; plog::init(plog::verbose, &debugOutputAppender); PLOGD << "Hello log!"; // short macro PLOG_DEBUG << "Hello log!"; // long macro PLOG(plog::debug) << "Hello log!"; // function-style macro return 0; } plog-1.1.10/samples/Demo/000077500000000000000000000000001447036265000150725ustar00rootroot00000000000000plog-1.1.10/samples/Demo/CMakeLists.txt000066400000000000000000000016621447036265000176370ustar00rootroot00000000000000add_executable(Demo Main.cpp MyClass.h MyClass.cpp Customer.h) target_link_libraries(Demo plog) set_target_properties(Demo PROPERTIES FOLDER Samples) if(MSVC AND NOT (MSVC_VERSION LESS 1700)) # Visual Studio 2012 and higher add_executable(DemoManaged Main.cpp MyClass.h MyClass.cpp Customer.h) target_link_libraries(DemoManaged plog) set_property(TARGET DemoManaged PROPERTY COMPILE_FLAGS "/clr /EHa") set_target_properties(DemoManaged PROPERTIES FOLDER Samples CXX_STANDARD 17 CXX_STANDARD_REQUIRED OFF) # the latest standard supported by CLR is C++17 endif() if(NOT WIN32) add_executable(DemoWchar Main.cpp MyClass.h MyClass.cpp Customer.h) target_link_libraries(DemoWchar plog) set_target_properties(DemoWchar PROPERTIES COMPILE_FLAGS "-DPLOG_ENABLE_WCHAR_INPUT=1") set_target_properties(DemoWchar PROPERTIES FOLDER Samples) if(APPLE) target_link_libraries(DemoWchar -liconv) endif() endif() plog-1.1.10/samples/Demo/Customer.h000066400000000000000000000004151447036265000170440ustar00rootroot00000000000000#pragma once #include #include struct Customer { int id; std::string name; }; inline std::ostream& operator<<(std::ostream& os, const Customer& obj) { os << "Customer (id: " << obj.id << ", name: " << obj.name << ")"; return os; } plog-1.1.10/samples/Demo/Main.cpp000066400000000000000000000140051447036265000164620ustar00rootroot00000000000000// // Demo - demonstrates log stream abilities, prints various types of messages. // #include #include #include #include #include #include #include #include #include #include "MyClass.h" #include "Customer.h" #ifdef __cplusplus_cli #pragma managed(push, off) void unmanagedFunc() { PLOGI << "Inside unmanaged function (char)"; PLOGI << L"Inside unmanaged function (wchar_t)"; } #pragma managed(pop) #endif int main() { plog::init(plog::debug, "Demo.csv", 5000, 3); // Initialize logging to the file. plog::ColorConsoleAppender consoleAppender; plog::get()->addAppender(&consoleAppender); // Also add logging to the console. // Log macro types. PLOGD << "Hello log!"; // short macro PLOG_DEBUG << "Hello log!"; // long macro PLOG(plog::debug) << "Hello log!"; // function-style macro // Log severity levels. PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; PLOG_NONE << "This is a NONE message"; // Integers demo. PLOG_INFO << "This is a bool: " << std::boolalpha << true; PLOG_INFO << "This is a char: " << 'x'; PLOG_INFO << "This is an unsigned char: " << (unsigned char)40; PLOG_INFO << "This is a short: " << (short)-1000; PLOG_INFO << "This is an unsigned short: " << (unsigned short)1000; PLOG_INFO << "This is an int: " << (int)-1000000; PLOG_INFO << "This is an unsigned int: " << (unsigned int)1000000; PLOG_INFO << "This is a long(hex): " << std::hex << (long)100000000; PLOG_INFO << "This is an unsigned long: " << (unsigned long)100000000; PLOG_INFO << "This is a float: " << 1.2345f; PLOG_INFO << "This is a double: " << std::setprecision(15) << 1.234512345; #ifndef __cplusplus_cli PLOG_INFO.printf("This is a format %s %d", "message", 42); #ifdef _WIN32 PLOG_INFO.printf(L"This is a wide format %s %d", L"message", 42); #endif #endif //__cplusplus_cli // Managed string. #ifdef __cplusplus_cli System::String^ managedStr = "This is a managed string"; PLOG_INFO << managedStr; unmanagedFunc(); #endif // Null strings are safe. PLOG_DEBUG << static_cast(NULL); PLOG_DEBUG << static_cast(NULL); #if PLOG_ENABLE_WCHAR_INPUT PLOG_DEBUG << static_cast(NULL); PLOG_DEBUG << static_cast(NULL); #endif // Plog handles unicode and std::string/wstring. #ifndef _WIN32 // On Windows the following code produces a warning C4566 if the codepage is not Cyrillic. PLOG_DEBUG << "test - теÑÑ‚"; PLOG_DEBUG << std::string("test - теÑÑ‚"); #endif #if PLOG_ENABLE_WCHAR_INPUT PLOG_DEBUG << L"test - теÑÑ‚"; PLOG_DEBUG << std::wstring(L"test - теÑÑ‚"); PLOG_DEBUG << L'ж'; #endif #ifdef __cpp_char8_t PLOG_DEBUG << u8"Chinese: 中文"; PLOG_DEBUG << const_cast(u8"Cyrillic: теÑÑ‚"); #endif // Multiline. PLOG_INFO << "This\nis\na" << std::endl << "multiline\nmessage!"; // Quotes. PLOG_INFO << "This is a message with \"quotes\"!"; // Conditional logging. int var = 0; PLOG_DEBUG_IF(var != 0) << "You shouldn't see this message"; PLOG_DEBUG_IF(var == 0) << "This is a conditional log message"; // Executed only on log level >= debug. IF_PLOG(plog::debug) var = 5; // one line IF_PLOG(plog::debug) // block { var++; } // Log macros don't break then-else clause without braces. if (var == 0) PLOGI << "then clause (condition is false, so it is skipped)"; else PLOGI << "else clase (should be visible)"; // Log in a class (capture this pointer, c++ function names). MyClass obj; obj.method(); obj.inlineMethod(); MyClass::staticMethod(); // Log in a template class. MyTemplateClass(1, 2).inlineMethod(); // Implicit cast to string. PLOG_INFO << obj; // ostream operator<< (on Windows wostream operator<< has priority but not required) Customer customer = { 10, "John" }; PLOG_INFO << customer; // Std containers can be printed std::vector vectorOfInts; vectorOfInts.push_back(1); vectorOfInts.push_back(2); vectorOfInts.push_back(3); PLOG_INFO << "std::vector: " << vectorOfInts; std::deque dequeOfStrings; dequeOfStrings.push_back("one"); dequeOfStrings.push_back("two"); dequeOfStrings.push_back("three"); PLOG_INFO << "std::deque: " << dequeOfStrings; std::list listOfCharPointers; listOfCharPointers.push_back("one"); listOfCharPointers.push_back("two"); listOfCharPointers.push_back(NULL); PLOG_INFO << "std::list: " << listOfCharPointers; std::set setOfInts; setOfInts.insert(10); setOfInts.insert(20); setOfInts.insert(30); PLOG_INFO << "std::set: " << setOfInts; std::map mapStringToInt; mapStringToInt["red"] = 1; mapStringToInt["green"] = 2; mapStringToInt["blue"] = 4; PLOG_INFO << "std::map: " << mapStringToInt; std::multimap multimapIntToString; multimapIntToString.insert(std::make_pair(1, "one")); multimapIntToString.insert(std::make_pair(1, "uno")); multimapIntToString.insert(std::make_pair(2, "two")); multimapIntToString.insert(std::make_pair(2, "due")); PLOG_INFO << "std::multimap: " << multimapIntToString; std::vector > vectorOfVectorsOfInts(3); vectorOfVectorsOfInts[0].push_back(1); vectorOfVectorsOfInts[0].push_back(2); vectorOfVectorsOfInts[1].push_back(-1); vectorOfVectorsOfInts[1].push_back(-2); PLOG_INFO << "std::vector >: " << vectorOfVectorsOfInts; return 0; } plog-1.1.10/samples/Demo/MyClass.cpp000066400000000000000000000004321447036265000171500ustar00rootroot00000000000000#include "MyClass.h" MyClass::MyClass() { PLOGD; } MyClass::~MyClass() { PLOGD; } void MyClass::method() { PLOGD; } void MyClass::staticMethod() { PLOGD; } MyClass::operator std::string() const { return std::string("This is an implicit cast to string."); } plog-1.1.10/samples/Demo/MyClass.h000066400000000000000000000007371447036265000166250ustar00rootroot00000000000000#pragma once #include class MyClass { public: MyClass(); ~MyClass(); void method(); void inlineMethod() { PLOGD; } static void staticMethod(); operator std::string() const; }; template class MyTemplateClass { public: MyTemplateClass(T t1, U t2) : m_t1(t1) , m_t2(t2) { } inline void inlineMethod() { PLOGD; } private: T m_t1; U m_t2; }; plog-1.1.10/samples/DisableLogging/000077500000000000000000000000001447036265000170605ustar00rootroot00000000000000plog-1.1.10/samples/DisableLogging/CMakeLists.txt000066400000000000000000000002231447036265000216150ustar00rootroot00000000000000add_executable(DisableLogging Main.cpp) target_link_libraries(DisableLogging plog) set_target_properties(DisableLogging PROPERTIES FOLDER Samples) plog-1.1.10/samples/DisableLogging/Main.cpp000066400000000000000000000014471447036265000204560ustar00rootroot00000000000000// // DisableLogging - shows how to disable logging (so it will be stripped from the binary). // #define PLOG_DISABLE_LOGGING #include #include #include #include int main() { #ifndef PLOG_DISABLE_LOGGING static plog::ColorConsoleAppender consoleAppender; plog::init(plog::verbose, &consoleAppender); #endif // The following code will be stripped from the binary by optimizer. PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.10/samples/DynamicAppender/000077500000000000000000000000001447036265000172515ustar00rootroot00000000000000plog-1.1.10/samples/DynamicAppender/CMakeLists.txt000066400000000000000000000002261447036265000220110ustar00rootroot00000000000000add_executable(DynamicAppender Main.cpp) target_link_libraries(DynamicAppender plog) set_target_properties(DynamicAppender PROPERTIES FOLDER Samples) plog-1.1.10/samples/DynamicAppender/Main.cpp000066400000000000000000000014231447036265000206410ustar00rootroot00000000000000// // DynamicAppender - shows how to add/remove appenders dynamically. // #include #include #include #include #include int main() { static plog::DynamicAppender dynamicAppender; plog::init(plog::verbose, &dynamicAppender); PLOGW << "This message goes nowhere as no real appenders exist"; { plog::ColorConsoleAppender consoleAppender; dynamicAppender.addAppender(&consoleAppender); PLOGI << "Message from a dynamically added appender"; dynamicAppender.removeAppender(&consoleAppender); } PLOGW << "This message goes nowhere as no real appenders exist"; return 0; } plog-1.1.10/samples/EventLog/000077500000000000000000000000001447036265000157315ustar00rootroot00000000000000plog-1.1.10/samples/EventLog/CMakeLists.txt000066400000000000000000000002361447036265000204720ustar00rootroot00000000000000if(WIN32) add_executable(EventLog Main.cpp) target_link_libraries(EventLog plog) set_target_properties(EventLog PROPERTIES FOLDER Samples) endif()plog-1.1.10/samples/EventLog/Main.cpp000066400000000000000000000034021447036265000173200ustar00rootroot00000000000000// // EventLog - shows how to use EventLogAppender to write to the windows event log. // #include #include #include #include #include using namespace std; int main(int argc, char* argv[]) { const wchar_t kEventSourceName[] = L"EventLogSample"; if (argc == 2) { // // Note: register/unregister operations require admin rights. // if (0 == strcmp(argv[1], "--register")) { if (!plog::EventLogAppenderRegistry::add(kEventSourceName)) { cerr << "Failed to register eventlog source." << endl; return -1; } cout << "Successfully registered eventlog source." << endl; } else if (0 == strcmp(argv[1], "--unregister")) { plog::EventLogAppenderRegistry::remove(kEventSourceName); cout << "Successfully unregistered eventlog source." << endl; } else if (0 == strcmp(argv[1], "--query")) { cout << "Eventlog source exists: " << plog::EventLogAppenderRegistry::exists(kEventSourceName) << endl; } return 0; } // // Note: eventlog source must be registered prior to creating its appender. // static plog::EventLogAppender eventLogAppender(kEventSourceName); plog::init(plog::verbose, &eventLogAppender); PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.10/samples/Facilities/000077500000000000000000000000001447036265000162625ustar00rootroot00000000000000plog-1.1.10/samples/Facilities/CMakeLists.txt000066400000000000000000000002061447036265000210200ustar00rootroot00000000000000add_executable(Facilities Main.cpp) target_link_libraries(Facilities plog) set_target_properties(Facilities PROPERTIES FOLDER Samples)plog-1.1.10/samples/Facilities/Main.cpp000066400000000000000000000020651447036265000176550ustar00rootroot00000000000000// // Facilities - shows how to use logging per facilities via multiple logger instances (useful for big projects). // #include #include enum Facility // Define log facilities. { Default, // The default is 0. Auth, FileIO, Sink = -1 // This is a log sink. Messages from other facilities go there. }; int main() { plog::init(plog::debug, "Facility.csv"); // Initialize the sink logger. // Initialize all other loggers and set the sink logger as an appender. Each of the loggers can have their own severity level. plog::init(plog::debug, plog::get()); plog::init(plog::warning, plog::get()); plog::init(plog::info, plog::get()); PLOGD_(Default) << "This is a message from the Default facility"; PLOGD << "This is a message from the Default facility too because Default = 0"; PLOGW_(Auth) << "This is a message from the Auth facility"; PLOGI_(FileIO) << "This is a message from the FileIO facility"; return 0; } plog-1.1.10/samples/Hello/000077500000000000000000000000001447036265000152515ustar00rootroot00000000000000plog-1.1.10/samples/Hello/CMakeLists.txt000066400000000000000000000001671447036265000200150ustar00rootroot00000000000000add_executable(Hello Main.cpp) target_link_libraries(Hello plog) set_target_properties(Hello PROPERTIES FOLDER Samples)plog-1.1.10/samples/Hello/Main.cpp000066400000000000000000000011031447036265000166340ustar00rootroot00000000000000// // Hello - a minimal introduction sample, shows the basic 3 steps to start using plog. // #include // Step1: include the header. #include int main() { plog::init(plog::debug, "Hello.txt"); // Step2: initialize the logger. // Step3: write log messages using a special macro. There are several log macros, use the macro you liked the most. PLOGD << "Hello log!"; // short macro PLOG_DEBUG << "Hello log!"; // long macro PLOG(plog::debug) << "Hello log!"; // function-style macro return 0; } plog-1.1.10/samples/HexDump/000077500000000000000000000000001447036265000155605ustar00rootroot00000000000000plog-1.1.10/samples/HexDump/CMakeLists.txt000066400000000000000000000001761447036265000203240ustar00rootroot00000000000000add_executable(HexDump Main.cpp) target_link_libraries(HexDump plog) set_target_properties(HexDump PROPERTIES FOLDER Samples) plog-1.1.10/samples/HexDump/Main.cpp000066400000000000000000000021541447036265000171520ustar00rootroot00000000000000// // HexDump - shows how to use plog::hexdump to dump binary buffers into hex. // #include #include #include #include #include #include #include int main() { static plog::ColorConsoleAppender consoleAppender; plog::init(plog::verbose, &consoleAppender); std::vector v; v.push_back(10); v.push_back(20); v.push_back(30); v.push_back(40); v.push_back(1000000); v.push_back(2000000); v.push_back(3000000); PLOGI << "v: " << plog::hexdump(v); PLOGI << "v: " << plog::hexdump(v).group(0); PLOGI << "v: " << plog::hexdump(v).separator("", ""); PLOGI << "v: " << plog::hexdump(v).group(4).separator(" ", "|"); PLOGI << "v: " << plog::hexdump(v).separator("", " "); std::string s("Hello!"); PLOGI << "s: " << plog::hexdump(s); int arr[] = {255, 511, 65535}; PLOGI << "arr: " << plog::hexdump(arr); void* p = malloc(100); PLOGI << "p: " << plog::hexdump(p, 100); return 0; } plog-1.1.10/samples/Library/000077500000000000000000000000001447036265000156125ustar00rootroot00000000000000plog-1.1.10/samples/Library/CMakeLists.txt000066400000000000000000000005021447036265000203470ustar00rootroot00000000000000add_executable(LibraryApp LibraryApp/Main.cpp) target_link_libraries(LibraryApp LibraryLib plog) set_target_properties(LibraryApp PROPERTIES FOLDER Samples/Library) add_library(LibraryLib STATIC LibraryLib/Lib.cpp) target_link_libraries(LibraryLib plog) set_target_properties(LibraryLib PROPERTIES FOLDER Samples/Library)plog-1.1.10/samples/Library/LibraryApp/000077500000000000000000000000001447036265000176575ustar00rootroot00000000000000plog-1.1.10/samples/Library/LibraryApp/Main.cpp000066400000000000000000000007611447036265000212530ustar00rootroot00000000000000// // Library - shows plog usage in static libraries. // #include #include void foo(); // Function from the static library. int main() { plog::init(plog::debug, "LibraryApp.txt"); // Initialize the logger. The static library will use it. // Note that the main app is not required to use plog, the static library will be linked fine in any case. foo(); PLOGD << "A message from the main application!"; return 0; } plog-1.1.10/samples/Library/LibraryLib/000077500000000000000000000000001447036265000176455ustar00rootroot00000000000000plog-1.1.10/samples/Library/LibraryLib/Lib.cpp000066400000000000000000000004411447036265000210560ustar00rootroot00000000000000// // Library - shows plog usage in static libraries. // #include void foo() { // The logger is initialized in the main app. It is safe not to do that and even not to use plog at all. The library will be linked fine. PLOGD << "A message from the static library!"; } plog-1.1.10/samples/MultiAppender/000077500000000000000000000000001447036265000167575ustar00rootroot00000000000000plog-1.1.10/samples/MultiAppender/CMakeLists.txt000066400000000000000000000002171447036265000215170ustar00rootroot00000000000000add_executable(MultiAppender Main.cpp) target_link_libraries(MultiAppender plog) set_target_properties(MultiAppender PROPERTIES FOLDER Samples)plog-1.1.10/samples/MultiAppender/Main.cpp000066400000000000000000000015341447036265000203520ustar00rootroot00000000000000// // MultiAppender - shows how to use multiple appenders with the same logger. // #include #include #include #include #include #include int main() { static plog::RollingFileAppender fileAppender("MultiAppender.csv", 8000, 3); // Create the 1st appender. static plog::ConsoleAppender consoleAppender; // Create the 2nd appender. plog::init(plog::debug, &fileAppender).addAppender(&consoleAppender); // Initialize the logger with the both appenders. // A bunch of log lines that goes to the both appenders: to the file and to the console. for (int i = 0; i < 100; ++i) { PLOG_INFO << "i: " << i; } return 0; } plog-1.1.10/samples/MultiInstance/000077500000000000000000000000001447036265000167655ustar00rootroot00000000000000plog-1.1.10/samples/MultiInstance/CMakeLists.txt000066400000000000000000000002171447036265000215250ustar00rootroot00000000000000add_executable(MultiInstance Main.cpp) target_link_libraries(MultiInstance plog) set_target_properties(MultiInstance PROPERTIES FOLDER Samples)plog-1.1.10/samples/MultiInstance/Main.cpp000066400000000000000000000016111447036265000203540ustar00rootroot00000000000000// // MultiInstance - shows how to use multiple logger instances, each instance has its own independent configuration. // #include #include enum // Define log instances. Default is 0 and is omitted from this enum. { SecondLog = 1 }; int main() { plog::init(plog::debug, "MultiInstance-default.txt"); // Initialize the default logger instance. plog::init(plog::debug, "MultiInstance-second.txt"); // Initialize the 2nd logger instance. // Write some messages to the default log. PLOGD << "Hello default log!"; PLOG_DEBUG << "Hello default log!"; PLOG(plog::debug) << "Hello default log!"; // Write some messages to the 2nd log. PLOGD_(SecondLog) << "Hello second log!"; PLOG_DEBUG_(SecondLog) << "Hello second log!"; PLOG_(SecondLog, plog::debug) << "Hello second log!"; return 0; } plog-1.1.10/samples/NotShared/000077500000000000000000000000001447036265000160755ustar00rootroot00000000000000plog-1.1.10/samples/NotShared/CMakeLists.txt000066400000000000000000000026331447036265000206410ustar00rootroot00000000000000# some systems have no shared libraries support, so check it if(NOT DEFINED TARGET_SUPPORTS_SHARED_LIBS OR TARGET_SUPPORTS_SHARED_LIBS) # reset visibility to default to test more harsh conditions # in real code it's recommended to use hidden visibility set(CMAKE_CXX_VISIBILITY_PRESET default) set(CMAKE_VISIBILITY_INLINES_HIDDEN 0) add_executable(NotSharedApp NotSharedApp/Main.cpp) target_link_libraries(NotSharedApp PRIVATE plog::plog NotSharedLib1 NotSharedLib2) set_target_properties(NotSharedApp PROPERTIES FOLDER Samples/NotShared) # define PLOG_LOCAL to make plog instances local (not shared between shared objects) target_compile_definitions(NotSharedApp PRIVATE PLOG_LOCAL) add_library(NotSharedLib1 SHARED NotSharedLib1/Main.cpp) target_link_libraries(NotSharedLib1 PRIVATE plog::plog) set_target_properties(NotSharedLib1 PROPERTIES FOLDER Samples/NotShared) # define PLOG_LOCAL to make plog instances local (not shared between shared objects) target_compile_definitions(NotSharedLib1 PRIVATE PLOG_LOCAL) add_library(NotSharedLib2 SHARED NotSharedLib2/Main.cpp) target_link_libraries(NotSharedLib2 PRIVATE plog::plog) set_target_properties(NotSharedLib2 PROPERTIES FOLDER Samples/NotShared) # define PLOG_LOCAL to make plog instances local (not shared between shared objects) target_compile_definitions(NotSharedLib2 PRIVATE PLOG_LOCAL) endif() plog-1.1.10/samples/NotShared/NotSharedApp/000077500000000000000000000000001447036265000204255ustar00rootroot00000000000000plog-1.1.10/samples/NotShared/NotSharedApp/Main.cpp000066400000000000000000000014401447036265000220140ustar00rootroot00000000000000// // NotShared - shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful). // #include #include // Functions imported form the shared libraries. extern "C" void foo1(); extern "C" void foo2(); int main() { plog::init(plog::debug, "NotSharedApp.txt"); // Initialize the logger. It will be visible only in this module and not in other modules because PLOG_LOCAL is defined. PLOGD << "Hello from app!"; // The message will go to the logger in this module. foo1(); // Call a function from the shared library that produces a log message. foo2(); // Call a function from the shared library that produces a log message. return 0; } plog-1.1.10/samples/NotShared/NotSharedLib1/000077500000000000000000000000001447036265000204745ustar00rootroot00000000000000plog-1.1.10/samples/NotShared/NotSharedLib1/Main.cpp000066400000000000000000000014101447036265000220600ustar00rootroot00000000000000// // NotShared - shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful). // #include #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that produces a log message. extern "C" void EXPORT foo1() { plog::init(plog::debug, "NotSharedLib1.txt"); // Initialize the logger. It will be visible only in this module and not in other modules because PLOG_LOCAL is defined. PLOGI << "Hello from shared lib #1!"; // The message will go to the logger in this module. } plog-1.1.10/samples/NotShared/NotSharedLib2/000077500000000000000000000000001447036265000204755ustar00rootroot00000000000000plog-1.1.10/samples/NotShared/NotSharedLib2/Main.cpp000066400000000000000000000014101447036265000220610ustar00rootroot00000000000000// // NotShared - shows how to make logger instances local across binary modules (this is the default behavior on Windows but not on other platforms, so be careful). // #include #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that produces a log message. extern "C" void EXPORT foo2() { plog::init(plog::debug, "NotSharedLib2.txt"); // Initialize the logger. It will be visible only in this module and not in other modules because PLOG_LOCAL is defined. PLOGI << "Hello from shared lib #2!"; // The message will go to the logger in this module. } plog-1.1.10/samples/ObjectiveC/000077500000000000000000000000001447036265000162235ustar00rootroot00000000000000plog-1.1.10/samples/ObjectiveC/CMakeLists.txt000066400000000000000000000004201447036265000207570ustar00rootroot00000000000000if(CMAKE_OBJCXX_AVAILABLE AND NOT CMAKE_COMPILER_IS_CLANGXX) add_executable(ObjectiveC Main.mm) target_link_libraries(ObjectiveC objc plog) else() add_custom_target(ObjectiveC SOURCES Main.mm) endif() set_target_properties(ObjectiveC PROPERTIES FOLDER Samples)plog-1.1.10/samples/ObjectiveC/Main.mm000066400000000000000000000007271447036265000174500ustar00rootroot00000000000000// // ObjectiveC - shows that plog can be used in ObjectiveC++. // #include #include #include @interface Greeter : Object { int dummy; } +(void) greet; @end @implementation Greeter +(void) greet { PLOGD << "Hello ObjC++!"; } @end int main() { plog::init(plog::debug, "ObjectiveC.csv"); // Initialize the logger. PLOGD << "Hello ObjC++!"; [Greeter greet]; return 0; } plog-1.1.10/samples/Path/000077500000000000000000000000001447036265000151025ustar00rootroot00000000000000plog-1.1.10/samples/Path/CMakeLists.txt000066400000000000000000000013651447036265000176470ustar00rootroot00000000000000if (CMAKE_VERSION VERSION_GREATER 3.1.0) if ((CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) OR (MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.10)) add_executable(Path Main.cpp) target_link_libraries(Path plog) set_target_properties(Path PROPERTIES FOLDER Samples) set_target_properties(Path PROPERTIES CXX_STANDARD 17) if (MSVC) target_compile_options(Path PRIVATE "/permissive-") # enable conformance mode to be more strict endif() if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) target_link_libraries(Path stdc++fs) endif() endif() endif() plog-1.1.10/samples/Path/Main.cpp000066400000000000000000000007411447036265000164740ustar00rootroot00000000000000// // Path - a test sample to check that std::filesystem::path can be logged. // #include #include #if __has_include() #include namespace fs = std::filesystem; #else #include namespace fs = std::experimental::filesystem; #endif int main() { plog::init(plog::debug, "Path.txt"); PLOGI << "Current path: " << fs::current_path(); return 0; } plog-1.1.10/samples/Performance/000077500000000000000000000000001447036265000164475ustar00rootroot00000000000000plog-1.1.10/samples/Performance/CMakeLists.txt000066400000000000000000000002111447036265000212010ustar00rootroot00000000000000add_executable(Performance Main.cpp) target_link_libraries(Performance plog) set_target_properties(Performance PROPERTIES FOLDER Samples)plog-1.1.10/samples/Performance/Main.cpp000066400000000000000000000021511447036265000200360ustar00rootroot00000000000000// // Performance - measures time per a log call. // #include #include #include #include #include enum { Console = 1 }; int main() { // Initialize the logger that will be measured. plog::init(plog::debug, "Performance.txt"); // Initialize the logger for printing info messages. static plog::ConsoleAppender consoleAppender; plog::init(plog::debug, &consoleAppender); PLOGI_(Console) << "Test started"; plog::util::Time startTime; plog::util::ftime(&startTime); const int kCount = 50000; // Performance measure loop. for (int i = 0; i < kCount; ++i) { PLOGD << "Hello log!"; } plog::util::Time finishTime; plog::util::ftime(&finishTime); time_t timeDiff = (finishTime.millitm - startTime.millitm) + (finishTime.time - startTime.time) * 1000; PLOGI_(Console) << "Test finished: " << static_cast(timeDiff) * 1000 / kCount << " microsec per call"; return 0; } plog-1.1.10/samples/PrintVar/000077500000000000000000000000001447036265000157535ustar00rootroot00000000000000plog-1.1.10/samples/PrintVar/CMakeLists.txt000066400000000000000000000002011447036265000205040ustar00rootroot00000000000000add_executable(PrintVar Main.cpp) target_link_libraries(PrintVar plog) set_target_properties(PrintVar PROPERTIES FOLDER Samples) plog-1.1.10/samples/PrintVar/Main.cpp000066400000000000000000000024461447036265000173510ustar00rootroot00000000000000// // PrintVar - shows how to use PLOG_PRINT_VAR to print variables. // #include #include #include #include class C { public: C(int value) : m_value(value) { PLOGD << PLOG_PRINT_VAR(this, m_value) << " - can print 'this' (useful for distinguishing class instances)"; } ~C() { PLOGD << PLOG_PRINT_VAR(this); } private: int m_value; }; int main() { plog::init(plog::verbose, plog::streamStdOut); C c1(42); C c2(42); int x = 10; int y = 20; int z = 30; PLOGI << PLOG_PRINT_VAR(x); PLOGI << PLOG_PRINT_VAR(x, y); PLOGI << PLOG_PRINT_VAR(x, y, z); PLOGI << PLOG_PRINT_VAR(x, y, z, x); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z, x); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z, x, y); PLOGI << PLOG_PRINT_VAR(x, y, z, x, y, z, x, y, z) << " - can print up to 9 variables"; PLOGI << PLOG_PRINT_VAR(x + y, z * 2, &c1) << " - can execute expressions"; PLOGI << "can print some data before, " << PLOG_PRINT_VAR(x, y) << " - in the middle, " PLOG_PRINT_VAR(z) << " - and after"; return 0; } plog-1.1.10/samples/SetFileName/000077500000000000000000000000001447036265000163425ustar00rootroot00000000000000plog-1.1.10/samples/SetFileName/CMakeLists.txt000066400000000000000000000002121447036265000210750ustar00rootroot00000000000000add_executable(SetFileName Main.cpp) target_link_libraries(SetFileName plog) set_target_properties(SetFileName PROPERTIES FOLDER Samples) plog-1.1.10/samples/SetFileName/Main.cpp000066400000000000000000000011251447036265000177310ustar00rootroot00000000000000// // SetFileName - shows how to change a log file name at arbitrary moment. // #include #include #include #include int main() { static plog::RollingFileAppender fileAppender("SetFileNameAAA.log"); plog::init(plog::debug, &fileAppender); for (int i = 0; i < 100; ++i) { PLOG_INFO << "i: " << i; } fileAppender.setFileName("SetFileNameBBB.log"); for (int i = 0; i < 100; ++i) { PLOG_INFO << "i: " << i; } return 0; } plog-1.1.10/samples/Shared/000077500000000000000000000000001447036265000154145ustar00rootroot00000000000000plog-1.1.10/samples/Shared/CMakeLists.txt000066400000000000000000000033331447036265000201560ustar00rootroot00000000000000# some systems have no shared libraries support, so check it if(NOT DEFINED TARGET_SUPPORTS_SHARED_LIBS OR TARGET_SUPPORTS_SHARED_LIBS) add_executable(SharedApp SharedApp/Main.cpp) target_link_libraries(SharedApp PRIVATE plog::plog) set_target_properties(SharedApp PROPERTIES FOLDER Samples/Shared) # define PLOG_GLOBAL to share plog instances across modules (PLOG_EXPORT to export on Windows) if(WIN32) target_compile_definitions(SharedApp PRIVATE PLOG_EXPORT) set_target_properties(SharedApp PROPERTIES ENABLE_EXPORTS 1) else() target_compile_definitions(SharedApp PRIVATE PLOG_GLOBAL) target_link_libraries(SharedApp PRIVATE SharedLib1 SharedLib2) endif() add_library(SharedLib1 SHARED SharedLib1/Main.cpp) target_link_libraries(SharedLib1 PRIVATE plog::plog) set_target_properties(SharedLib1 PROPERTIES FOLDER Samples/Shared) # define PLOG_GLOBAL to share plog instances across modules (PLOG_IMPORT to import on Windows) if(WIN32) target_compile_definitions(SharedLib1 PRIVATE PLOG_IMPORT) target_link_libraries(SharedLib1 PRIVATE SharedApp) else() target_compile_definitions(SharedLib1 PRIVATE PLOG_GLOBAL) endif() add_library(SharedLib2 SHARED SharedLib2/Main.cpp) target_link_libraries(SharedLib2 PRIVATE plog::plog) set_target_properties(SharedLib2 PROPERTIES FOLDER Samples/Shared) # define PLOG_GLOBAL to share plog instances across modules (PLOG_IMPORT to import on Windows) if(WIN32) target_compile_definitions(SharedLib2 PRIVATE PLOG_IMPORT) target_link_libraries(SharedLib2 PRIVATE SharedApp) else() target_compile_definitions(SharedLib2 PRIVATE PLOG_GLOBAL) endif() endif() plog-1.1.10/samples/Shared/SharedApp/000077500000000000000000000000001447036265000172635ustar00rootroot00000000000000plog-1.1.10/samples/Shared/SharedApp/Main.cpp000066400000000000000000000034131447036265000206540ustar00rootroot00000000000000// // Shared - shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful). // #include #include // Functions imported form the shared libraries. // // NOTE: // We use dynamic linking on Windows as DLL imports logger instances from EXE and static linking will make a dependency loop: // DLL --(import logger)--> EXE, EXE --(import foo)--> DLL. // // On everything except Winows logger instances are shared across all modules, so there is no dependency loop. #ifdef _WIN32 # include typedef void (*Foo1Fn)(); typedef void (*Foo2Fn)(); #else extern "C" void foo1(); extern "C" void foo2(); #endif int main() { plog::init(plog::debug, "Shared.txt"); // Initialize the logger. // It will be shared across modules because PLOG_GLOBAL is defined, so no need to call `plog::init` in them. // On Windows the logger will be exported because PLOG_EXPORT is defined. PLOGD << "Hello from app!"; // The message will go to the logger in this module. #ifdef _WIN32 HMODULE lib1 = LoadLibraryW(L"SharedLib1.dll"); PLOGE_IF(!lib1) << "Couldn't load SharedLib1.dll"; Foo1Fn foo1 = reinterpret_cast(GetProcAddress(lib1, "foo1")); PLOGE_IF(!foo1) << "Couldn't get foo1 from SharedLib1.dll"; HMODULE lib2 = LoadLibraryW(L"SharedLib2.dll"); PLOGE_IF(!lib2) << "Couldn't load SharedLib2.dll"; Foo1Fn foo2 = reinterpret_cast(GetProcAddress(lib2, "foo2")); PLOGE_IF(!foo2) << "Couldn't get foo2 from SharedLib2.dll"; #endif foo1(); // Call a function from the shared library that produces a log message. foo2(); // Call a function from the shared library that produces a log message. return 0; } plog-1.1.10/samples/Shared/SharedLib1/000077500000000000000000000000001447036265000173325ustar00rootroot00000000000000plog-1.1.10/samples/Shared/SharedLib1/Main.cpp000066400000000000000000000012541447036265000207240ustar00rootroot00000000000000// // Shared - shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful). // #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that produces a log message. extern "C" void EXPORT foo1() { PLOGI << "Hello from shared lib #1!"; // The message will go to the logger initialized in the main module because PLOG_GLOBAL is defined. // On Windows the logger instance is imported from the main module because PLOG_IMPORT is defined. } plog-1.1.10/samples/Shared/SharedLib2/000077500000000000000000000000001447036265000173335ustar00rootroot00000000000000plog-1.1.10/samples/Shared/SharedLib2/Main.cpp000066400000000000000000000012541447036265000207250ustar00rootroot00000000000000// // Shared - shows how to share logger instances across binary modules (this is the default behavior on everything except Windows, so be careful). // #include // Helper macro to mark functions exported from the library. #ifdef _WIN32 # define EXPORT __declspec(dllexport) #else # define EXPORT __attribute__ ((visibility ("default"))) #endif // Function that produces a log message. extern "C" void EXPORT foo2() { PLOGI << "Hello from shared lib #2!"; // The message will go to the logger initialized in the main module because PLOG_GLOBAL is defined. // On Windows the logger instance is imported from the main module because PLOG_IMPORT is defined. } plog-1.1.10/samples/SkipNativeEOL/000077500000000000000000000000001447036265000166235ustar00rootroot00000000000000plog-1.1.10/samples/SkipNativeEOL/CMakeLists.txt000066400000000000000000000002201447036265000213550ustar00rootroot00000000000000add_executable(SkipNativeEOL Main.cpp) target_link_libraries(SkipNativeEOL plog) set_target_properties(SkipNativeEOL PROPERTIES FOLDER Samples) plog-1.1.10/samples/SkipNativeEOL/Main.cpp000066400000000000000000000015201447036265000202110ustar00rootroot00000000000000// // SkipNativeEOL - shows how to skip NativeEOLConverter. // #include #include #include #include #include int main() { // NativeEOLConverter will use on Windows and on everything else as line endings. // It's used by default. // If you want to always use you can skip NativeEOLConverter and specify UTF8Converter directly. static plog::RollingFileAppender fileAppender("SkipNativeEOL.log", 8000, 3); // Create an appender without NativeEOLConverter. plog::init(plog::debug, &fileAppender); // Initialize the logger. // Write some data. for (int i = 0; i < 100; ++i) { PLOGI << "i: " << i; } return 0; } plog-1.1.10/samples/UtcTime/000077500000000000000000000000001447036265000155605ustar00rootroot00000000000000plog-1.1.10/samples/UtcTime/CMakeLists.txt000066400000000000000000000001751447036265000203230ustar00rootroot00000000000000add_executable(UtcTime Main.cpp) target_link_libraries(UtcTime plog) set_target_properties(UtcTime PROPERTIES FOLDER Samples)plog-1.1.10/samples/UtcTime/Main.cpp000066400000000000000000000016021447036265000171470ustar00rootroot00000000000000// // UtcTime - shows how to use UTC time in logs. // #include #include #include #include #include #include int main() { static plog::ColorConsoleAppender consoleAppender; // TxtFormatter in UTC static plog::RollingFileAppender fileAppender("UtcTime.csv", 10000, 2); // CsvFormatter in UTC plog::init(plog::verbose, &consoleAppender).addAppender(&fileAppender); PLOG_VERBOSE << "This is a VERBOSE message"; PLOG_DEBUG << "This is a DEBUG message"; PLOG_INFO << "This is an INFO message"; PLOG_WARNING << "This is a WARNING message"; PLOG_ERROR << "This is an ERROR message"; PLOG_FATAL << "This is a FATAL message"; return 0; } plog-1.1.10/samples/Utf8Everywhere/000077500000000000000000000000001447036265000171025ustar00rootroot00000000000000plog-1.1.10/samples/Utf8Everywhere/CMakeLists.txt000066400000000000000000000004101447036265000216350ustar00rootroot00000000000000if(MSVC AND NOT (MSVC_VERSION LESS 1900)) # Visual Studio 2015 and higher add_executable(Utf8Everywhere Main.cpp) target_link_libraries(Utf8Everywhere plog) set_target_properties(Utf8Everywhere PROPERTIES FOLDER Samples COMPILE_FLAGS "/utf-8") endif() plog-1.1.10/samples/Utf8Everywhere/Main.cpp000066400000000000000000000013331447036265000204720ustar00rootroot00000000000000// // Utf8Everywhere - demonstrates using http://utf8everywhere.org on Windows. // #include #include #include #include void 中文() { PLOGD << "Chinese: 中文"; PLOGD << L"Chinese: 中文"; } void теÑÑ‚() { PLOGD << "Cyrillic: теÑÑ‚"; PLOGD << L"Cyrillic: теÑÑ‚"; } int main() { plog::init(plog::debug, "Utf8Everywhere.log"); // Initialize logging to the file. plog::ColorConsoleAppender consoleAppender; plog::get()->addAppender(&consoleAppender); // Also add logging to the console. 中文(); теÑÑ‚(); return 0; } plog-1.1.10/test/000077500000000000000000000000001447036265000135215ustar00rootroot00000000000000plog-1.1.10/test/CMakeLists.txt000066400000000000000000000010031447036265000162530ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0) project(PlogTest CXX) add_executable(${PROJECT_NAME} doctest.h Conditional.cpp TestAppender.h Main.cpp Printf.cpp SimpleTypes.cpp StringTypes.cpp StdManipulators.cpp StdContainers.cpp ) target_link_libraries(${PROJECT_NAME} plog::plog) set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "-DDOCTEST_CONFIG_NO_POSIX_SIGNALS") set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER Test) add_test(${PROJECT_NAME} ${PROJECT_NAME}) plog-1.1.10/test/Conditional.cpp000066400000000000000000000044341447036265000164750ustar00rootroot00000000000000#include "doctest.h" #include #include "TestAppender.h" SCENARIO("conditional logging") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::info); logger.addAppender(&testAppender); WHEN("condition is true") { int var = 0; PLOG_INFO_IF(var == 0) << "message"; THEN("the message is printed") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("message")); } } WHEN("condition is false") { int var = 0; PLOG_INFO_IF(var != 0) << "message"; THEN("the message is not printed") { CHECK(testAppender.getMessage().empty()); } } WHEN("log level check is true") { int var = 0; IF_PLOG(plog::info) var = 5; // one line IF_PLOG(plog::info) // block { var++; } THEN("statements were executed") { CHECK_EQ(var, 5 + 1); } } WHEN("log level check is false") { int var = 0; IF_PLOG(plog::debug) var = 5; // one line IF_PLOG(plog::debug) // block { var++; } THEN("statements were not executed") { CHECK_EQ(var, 0); } } WHEN("log macros are used in 'then-else' clauses without braces and condition is true") { int var = 0; if (var == 0) PLOGI << "then clause"; else PLOGI << "else clause"; THEN("nothing is broken, 'then' clause is executed") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("then clause")); } } WHEN("log macros are used in 'then-else' clauses without braces and condition is false") { int var = 0; if (var != 0) PLOGI << "then clause"; else PLOGI << "else clause"; THEN("nothing is broken, 'else' clause is executed") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("else clause")); } } } } plog-1.1.10/test/Main.cpp000066400000000000000000000001001447036265000151000ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest.h" plog-1.1.10/test/Printf.cpp000066400000000000000000000044301447036265000154700ustar00rootroot00000000000000#include "doctest.h" #include #include "TestAppender.h" #ifndef __cplusplus_cli SCENARIO("printf-style messages") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("use empty format") { PLOGI.printf(""); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("")); } } WHEN("use text only format") { PLOGI.printf("test"); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("use format %d") { PLOGI.printf("test %d", 42); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test 42")); } } WHEN("use format %s") { PLOGI.printf("hello %s", "world"); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("hello world")); } } #ifdef _WIN32 WHEN("use empty format (wide)") { PLOGI.printf(L""); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("")); } } WHEN("use text only format (wide)") { PLOGI.printf(L"test"); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("use format %d (wide)") { PLOGI.printf(L"test %d", 42); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test 42")); } } WHEN("use format %s (wide)") { PLOGI.printf(L"hello %s", L"world"); THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("hello world")); } } #endif } } #endif plog-1.1.10/test/SimpleTypes.cpp000066400000000000000000000115301447036265000165030ustar00rootroot00000000000000#include "doctest.h" #include #include "TestAppender.h" SCENARIO("simple types") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("type is bool") { bool var = true; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1")); } } WHEN("type is char") { char var = 'a'; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("a")); } } WHEN("type is signed char") { signed char var = 'a'; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("a")); } } WHEN("type is unsigned char") { unsigned char var = 'a'; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("a")); } } WHEN("type is short") { short var = -1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); } } WHEN("type is unsigned short") { unsigned short var = 1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); } } WHEN("type is int") { int var = -1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); } } WHEN("type is unsigned int") { unsigned int var = 1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); } } WHEN("type is long") { long var = -1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); } } WHEN("type is unsigned long") { unsigned long var = 1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); } } WHEN("type is long long") { long long var = -1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("-1000")); } } WHEN("type is unsigned long long") { unsigned long long var = 1000; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1000")); } } WHEN("type is float") { float var = 1.2345f; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1.2345")); } } WHEN("type is double") { double var = 1.2345; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1.2345")); } } WHEN("type is void pointer") { void* var = &var; PLOGI << var; THEN("the result is as expected") { CHECK(testAppender.getMessage().size() >= 4); // could be 2 hex formats for pointers: with 0x prefix and without it if (testAppender.getMessage().at(1) == PLOG_NSTR('x')) { CHECK_EQ(testAppender.getMessage().at(0), PLOG_NSTR('0')); for (size_t i = 2; i < testAppender.getMessage().size(); ++i) { CHECK(std::isxdigit(testAppender.getMessage().at(i))); } } else { for (size_t i = 0; i < testAppender.getMessage().size(); ++i) { CHECK(std::isxdigit(testAppender.getMessage().at(i))); } } } } } } plog-1.1.10/test/StdContainers.cpp000066400000000000000000000101411447036265000170020ustar00rootroot00000000000000#include "doctest.h" #include #include "TestAppender.h" #include #include #include #include #include #include SCENARIO("std containers") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("empty collection") { std::vector vectorOfInts; PLOGI << vectorOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[]")); } } WHEN("std::vector") { std::vector vectorOfInts; vectorOfInts.push_back(1); vectorOfInts.push_back(2); vectorOfInts.push_back(3); PLOGI << vectorOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[1, 2, 3]")); } } WHEN("std::deque") { std::deque dequeOfStrings; dequeOfStrings.push_back("one"); dequeOfStrings.push_back("two"); dequeOfStrings.push_back("three"); PLOGI << dequeOfStrings; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[one, two, three]")); } } WHEN("std::list") { std::list listOfCharPointers; listOfCharPointers.push_back("one"); listOfCharPointers.push_back("two"); listOfCharPointers.push_back(NULL); PLOGI << listOfCharPointers; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[one, two, (null)]")); } } WHEN("std::set") { std::set setOfInts; setOfInts.insert(10); setOfInts.insert(20); setOfInts.insert(30); PLOGI << setOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[10, 20, 30]")); } } WHEN("std::map") { std::map mapStringToInt; mapStringToInt["red"] = 1; mapStringToInt["green"] = 2; mapStringToInt["blue"] = 4; PLOGI << mapStringToInt; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[blue:4, green:2, red:1]")); } } WHEN("std::multimap") { std::multimap multimapIntToString; multimapIntToString.insert(std::make_pair(1, "one")); multimapIntToString.insert(std::make_pair(1, "uno")); multimapIntToString.insert(std::make_pair(2, "two")); multimapIntToString.insert(std::make_pair(2, "due")); PLOGI << multimapIntToString; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[1:one, 1:uno, 2:two, 2:due]")); } } WHEN("std::vector of std::vector") { std::vector > vectorOfVectorsOfInts(3); vectorOfVectorsOfInts[0].push_back(1); vectorOfVectorsOfInts[0].push_back(2); vectorOfVectorsOfInts[1].push_back(-1); vectorOfVectorsOfInts[1].push_back(-2); PLOGI << vectorOfVectorsOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("[[1, 2], [-1, -2], []]")); } } WHEN("std::pair") { std::pair pairOfInts(5, 10); PLOGI << pairOfInts; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("5:10")); } } } }plog-1.1.10/test/StdManipulators.cpp000066400000000000000000000023141447036265000173560ustar00rootroot00000000000000#include "doctest.h" #include #include "TestAppender.h" SCENARIO("std manipulators") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("std::boolalpha") { PLOGI << std::boolalpha << true; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("true")); } } WHEN("std::hex") { PLOGI << std::hex << 65534; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("fffe")); } } WHEN("use manipulator for the 1st message") { bool var = true; PLOGI << std::boolalpha << var; AND_WHEN("log the 2nd message") { PLOGI << var; THEN("manipulators are cleared for the 2nd message") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("1")); } } } } }plog-1.1.10/test/StringTypes.cpp000066400000000000000000000075621447036265000165320ustar00rootroot00000000000000#include "doctest.h" #include #include "TestAppender.h" SCENARIO("string types") { GIVEN("logger is initialised") { plog::TestAppender testAppender; plog::Logger logger(plog::verbose); logger.addAppender(&testAppender); WHEN("type is char*") { char* var = const_cast("test"); PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const char*") { const char* var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is char[]") { char var[] = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const char[]") { const char var[] = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is std::string") { std::string var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const std::string") { const std::string var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } #if PLOG_ENABLE_WCHAR_INPUT WHEN("type is wchar_t*") { wchar_t* var = const_cast(L"test"); PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const wchar_t*") { const wchar_t* var = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is wchar_t[]") { wchar_t var[] = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const wchar_t[]") { const wchar_t var[] = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is std::wstring") { std::wstring var = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } WHEN("type is const std::wstring") { const std::wstring var = L"test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } #endif #ifdef __cplusplus_cli WHEN("type is const System::String^") { System::String^ var = "test"; PLOGI << var; THEN("the result is as expected") { CHECK_EQ(testAppender.getMessage(), PLOG_NSTR("test")); } } #endif //TODO: add more tests } } plog-1.1.10/test/TestAppender.h000066400000000000000000000006331447036265000162720ustar00rootroot00000000000000#pragma once #include namespace plog { class TestAppender : public IAppender { public: virtual void write(const Record& record) PLOG_OVERRIDE { m_message = record.getMessage(); } const util::nstring& getMessage() const { return m_message; } private: util::nstring m_message; }; }plog-1.1.10/test/doctest.h000066400000000000000000007512261447036265000153540ustar00rootroot00000000000000// ====================================================================== // == DO NOT MODIFY THIS FILE BY HAND - IT IS AUTO GENERATED BY CMAKE! == // ====================================================================== // // doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD // // Copyright (c) 2016-2018 Viktor Kirilov // // Distributed under the MIT Software License // See accompanying file LICENSE.txt or copy at // https://opensource.org/licenses/MIT // // The documentation can be found at the library's page: // https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md // // ================================================================================================= // ================================================================================================= // ================================================================================================= // // The library is heavily influenced by Catch - https://github.com/philsquared/Catch // which uses the Boost Software License - Version 1.0 // see here - https://github.com/philsquared/Catch/blob/master/LICENSE.txt // // The concept of subcases (sections in Catch) and expression decomposition are from there. // Some parts of the code are taken directly: // - stringification - the detection of "ostream& operator<<(ostream&, const T&)" and StringMaker<> // - the Approx() helper class for floating point comparison // - colors in the console // - breaking into a debugger // - signal / SEH handling // - timer // // The expression decomposing templates are taken from lest - https://github.com/martinmoene/lest // which uses the Boost Software License - Version 1.0 // see here - https://github.com/martinmoene/lest/blob/master/LICENSE.txt // // The type list and the foreach algorithm on it for C++98 are taken from Loki // - http://loki-lib.sourceforge.net/ // - https://en.wikipedia.org/wiki/Loki_%28C%2B%2B%29 // - https://github.com/snaewe/loki-lib // which uses the MIT Software License // // ================================================================================================= // ================================================================================================= // ================================================================================================= #ifndef DOCTEST_LIBRARY_INCLUDED #define DOCTEST_LIBRARY_INCLUDED // ================================================================================================= // == VERSION ====================================================================================== // ================================================================================================= #define DOCTEST_VERSION_MAJOR 1 #define DOCTEST_VERSION_MINOR 2 #define DOCTEST_VERSION_PATCH 9 #define DOCTEST_VERSION_STR "1.2.9" #define DOCTEST_VERSION \ (DOCTEST_VERSION_MAJOR * 10000 + DOCTEST_VERSION_MINOR * 100 + DOCTEST_VERSION_PATCH) // ================================================================================================= // == COMPILER VERSION ============================================================================= // ================================================================================================= // ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect #define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH)) #if defined(_MSC_VER) && defined(_MSC_FULL_VER) #if _MSC_VER == _MSC_FULL_VER / 10000 #define DOCTEST_MSVC DOCTEST_COMPILER(_MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 10000) #else #define DOCTEST_MSVC \ DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000) #endif #elif defined(__clang__) && defined(__clang_minor__) #define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__) #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \ !defined(__INTEL_COMPILER) #define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #endif #ifndef DOCTEST_MSVC #define DOCTEST_MSVC 0 #endif // DOCTEST_MSVC #ifndef DOCTEST_CLANG #define DOCTEST_CLANG 0 #endif // DOCTEST_CLANG #ifndef DOCTEST_GCC #define DOCTEST_GCC 0 #endif // DOCTEST_GCC // ================================================================================================= // == COMPILER WARNINGS HELPERS ==================================================================== // ================================================================================================= #if DOCTEST_CLANG #ifdef __has_warning #define DOCTEST_CLANG_HAS_WARNING(x) __has_warning(x) #endif // __has_warning #ifdef __has_feature #define DOCTEST_CLANG_HAS_FEATURE(x) __has_feature(x) #endif // __has_feature #define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push") #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH #define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w) #define DOCTEST_MSVC_SUPPRESS_WARNING(w) #define DOCTEST_GCC_SUPPRESS_WARNING(w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop") #define DOCTEST_MSVC_SUPPRESS_WARNING_POP #define DOCTEST_GCC_SUPPRESS_WARNING_POP #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING(w) #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) #elif DOCTEST_GCC #define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x) #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 7, 0) #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH _Pragma("GCC diagnostic push") #else // GCC 4.7+ #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH #endif // GCC 4.7+ #define DOCTEST_CLANG_SUPPRESS_WARNING(w) #define DOCTEST_MSVC_SUPPRESS_WARNING(w) #define DOCTEST_GCC_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(GCC diagnostic ignored w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_MSVC_SUPPRESS_WARNING_POP #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 7, 0) #define DOCTEST_GCC_SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop") #else // GCC 4.7+ #define DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // GCC 4.7+ #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING(w) #elif DOCTEST_MSVC #define DOCTEST_PRAGMA_TO_STR(x) #define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH #define DOCTEST_MSVC_SUPPRESS_WARNING_PUSH __pragma(warning(push)) #define DOCTEST_GCC_SUPPRESS_WARNING_PUSH #define DOCTEST_CLANG_SUPPRESS_WARNING(w) #define DOCTEST_MSVC_SUPPRESS_WARNING(w) __pragma(warning(disable : w)) #define DOCTEST_GCC_SUPPRESS_WARNING(w) #define DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_MSVC_SUPPRESS_WARNING_POP __pragma(warning(pop)) #define DOCTEST_GCC_SUPPRESS_WARNING_POP #define DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH(w) #define DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(w) \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(w) #define DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH(w) #endif // different compilers - warning suppression macros #ifndef DOCTEST_CLANG_HAS_WARNING #define DOCTEST_CLANG_HAS_WARNING(x) 1 #endif // DOCTEST_CLANG_HAS_WARNING #ifndef DOCTEST_CLANG_HAS_FEATURE #define DOCTEST_CLANG_HAS_FEATURE(x) 0 #endif // DOCTEST_CLANG_HAS_FEATURE // ================================================================================================= // == COMPILER WARNINGS ============================================================================ // ================================================================================================= DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") DOCTEST_CLANG_SUPPRESS_WARNING("-Wnon-virtual-dtor") DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdeprecated") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++11-long-long") #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_WARNING("-Wzero-as-null-pointer-constant") DOCTEST_CLANG_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant") #endif // clang - 0 as null DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") DOCTEST_GCC_SUPPRESS_WARNING("-Wctor-dtor-privacy") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") DOCTEST_GCC_SUPPRESS_WARNING("-Wnon-virtual-dtor") DOCTEST_GCC_SUPPRESS_WARNING("-Winline") DOCTEST_GCC_SUPPRESS_WARNING("-Wlong-long") DOCTEST_GCC_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant") DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding DOCTEST_MSVC_SUPPRESS_WARNING(4625) // copy constructor was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(4626) // assignment operator was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(5027) // move assignment operator was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(5026) // move constructor was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly defined as deleted DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe // C4548 - expression before comma has no effect; expected expression with side - effect // C4986 - exception specification does not match previous declaration // C4350 - behavior change: 'member1' called instead of 'member2' // C4668 - 'x' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' // C4365 - conversion from 'int' to 'unsigned long', signed/unsigned mismatch // C4774 - format string expected in argument 'x' is not a string literal // C4820 - padding in structs // only 4 should be disabled globally: // - C4514 # unreferenced inline function has been removed // - C4571 # SEH related // - C4710 # function not inlined // - C4711 # function 'x' selected for automatic inline expansion #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN \ DOCTEST_MSVC_SUPPRESS_WARNING_PUSH \ DOCTEST_MSVC_SUPPRESS_WARNING(4548) \ DOCTEST_MSVC_SUPPRESS_WARNING(4986) \ DOCTEST_MSVC_SUPPRESS_WARNING(4350) \ DOCTEST_MSVC_SUPPRESS_WARNING(4668) \ DOCTEST_MSVC_SUPPRESS_WARNING(4365) \ DOCTEST_MSVC_SUPPRESS_WARNING(4774) \ DOCTEST_MSVC_SUPPRESS_WARNING(4820) \ DOCTEST_MSVC_SUPPRESS_WARNING(4625) \ DOCTEST_MSVC_SUPPRESS_WARNING(4626) \ DOCTEST_MSVC_SUPPRESS_WARNING(5027) \ DOCTEST_MSVC_SUPPRESS_WARNING(5026) \ DOCTEST_MSVC_SUPPRESS_WARNING(4623) \ DOCTEST_MSVC_SUPPRESS_WARNING(5039) #define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP // ================================================================================================= // == FEATURE DETECTION ============================================================================ // ================================================================================================= #if __cplusplus >= 201103L #ifndef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #ifndef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #ifndef DOCTEST_CONFIG_WITH_NULLPTR #define DOCTEST_CONFIG_WITH_NULLPTR #endif // DOCTEST_CONFIG_WITH_NULLPTR #ifndef DOCTEST_CONFIG_WITH_LONG_LONG #define DOCTEST_CONFIG_WITH_LONG_LONG #endif // DOCTEST_CONFIG_WITH_LONG_LONG #ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT #define DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT #ifndef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // __cplusplus >= 201103L // MSVC C++11 feature support table: https://msdn.microsoft.com/en-us/library/hh567368.aspx // GCC C++11 feature support table: https://gcc.gnu.org/projects/cxx-status.html // MSVC version table: // MSVC++ 15.0 _MSC_VER == 1910 (Visual Studio 2017) // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) // MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) // MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) // deleted functions #ifndef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #if DOCTEST_MSVC >= DOCTEST_COMPILER(18, 0, 0) #define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // MSVC #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_FEATURE(cxx_deleted_functions) #define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // clang #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 5, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // GCC #endif // DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #if defined(DOCTEST_CONFIG_NO_DELETED_FUNCTIONS) && defined(DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS) #undef DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS #endif // DOCTEST_CONFIG_NO_DELETED_FUNCTIONS // rvalue references #ifndef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #if DOCTEST_MSVC >= DOCTEST_COMPILER(16, 0, 0) #define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // MSVC #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_FEATURE(cxx_rvalue_references) #define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // clang #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 3, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // GCC #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #if defined(DOCTEST_CONFIG_NO_RVALUE_REFERENCES) && defined(DOCTEST_CONFIG_WITH_RVALUE_REFERENCES) #undef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES #endif // DOCTEST_CONFIG_NO_RVALUE_REFERENCES // nullptr #ifndef DOCTEST_CONFIG_WITH_NULLPTR #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_FEATURE(cxx_nullptr) #define DOCTEST_CONFIG_WITH_NULLPTR #endif // clang #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 6, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_NULLPTR #endif // GCC #if DOCTEST_MSVC >= DOCTEST_COMPILER(16, 0, 0) #define DOCTEST_CONFIG_WITH_NULLPTR #endif // MSVC #endif // DOCTEST_CONFIG_WITH_NULLPTR #if defined(DOCTEST_CONFIG_NO_NULLPTR) && defined(DOCTEST_CONFIG_WITH_NULLPTR) #undef DOCTEST_CONFIG_WITH_NULLPTR #endif // DOCTEST_CONFIG_NO_NULLPTR // variadic macros #ifndef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #if DOCTEST_MSVC >= DOCTEST_COMPILER(14, 0, 0) && !defined(__EDGE__) #define DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // MSVC #if(DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(4, 1, 0)) && \ defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // GCC and clang #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #if defined(DOCTEST_CONFIG_NO_VARIADIC_MACROS) && defined(DOCTEST_CONFIG_WITH_VARIADIC_MACROS) #undef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // DOCTEST_CONFIG_NO_VARIADIC_MACROS // long long #ifndef DOCTEST_CONFIG_WITH_LONG_LONG #if DOCTEST_MSVC >= DOCTEST_COMPILER(14, 0, 0) #define DOCTEST_CONFIG_WITH_LONG_LONG #endif // MSVC #if(DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(4, 5, 0)) && \ defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_LONG_LONG #endif // GCC and clang #endif // DOCTEST_CONFIG_WITH_LONG_LONG #if defined(DOCTEST_CONFIG_NO_LONG_LONG) && defined(DOCTEST_CONFIG_WITH_LONG_LONG) #undef DOCTEST_CONFIG_WITH_LONG_LONG #endif // DOCTEST_CONFIG_NO_LONG_LONG // static_assert #ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_FEATURE(cxx_static_assert) #define DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // clang #if DOCTEST_GCC >= DOCTEST_COMPILER(4, 3, 0) && defined(__GXX_EXPERIMENTAL_CXX0X__) #define DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // GCC #if DOCTEST_MSVC >= DOCTEST_COMPILER(16, 0, 0) #define DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // MSVC #endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT #if defined(DOCTEST_CONFIG_NO_STATIC_ASSERT) && defined(DOCTEST_CONFIG_WITH_STATIC_ASSERT) #undef DOCTEST_CONFIG_WITH_STATIC_ASSERT #endif // DOCTEST_CONFIG_NO_STATIC_ASSERT // other stuff... #if defined(DOCTEST_CONFIG_WITH_RVALUE_REFERENCES) || defined(DOCTEST_CONFIG_WITH_LONG_LONG) || \ defined(DOCTEST_CONFIG_WITH_DELETED_FUNCTIONS) || defined(DOCTEST_CONFIG_WITH_NULLPTR) || \ defined(DOCTEST_CONFIG_WITH_VARIADIC_MACROS) || defined(DOCTEST_CONFIG_WITH_STATIC_ASSERT) #define DOCTEST_NO_CPP11_COMPAT #endif // c++11 stuff #if defined(DOCTEST_NO_CPP11_COMPAT) DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") #endif // DOCTEST_NO_CPP11_COMPAT #if DOCTEST_MSVC && !defined(DOCTEST_CONFIG_WINDOWS_SEH) #define DOCTEST_CONFIG_WINDOWS_SEH #endif // MSVC #if defined(DOCTEST_CONFIG_NO_WINDOWS_SEH) && defined(DOCTEST_CONFIG_WINDOWS_SEH) #undef DOCTEST_CONFIG_WINDOWS_SEH #endif // DOCTEST_CONFIG_NO_WINDOWS_SEH #if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) #define DOCTEST_CONFIG_POSIX_SIGNALS #endif // _WIN32 #if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS) #undef DOCTEST_CONFIG_POSIX_SIGNALS #endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #if(DOCTEST_GCC || DOCTEST_CLANG) && !defined(__EXCEPTIONS) #define DOCTEST_CONFIG_NO_EXCEPTIONS #endif // clang and gcc // in MSVC _HAS_EXCEPTIONS is defined in a header instead of as a project define // so we can't do the automatic detection for MSVC without including some header #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS #define DOCTEST_CONFIG_NO_EXCEPTIONS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #if defined(DOCTEST_CONFIG_NO_EXCEPTIONS) && !defined(DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS) #define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT) #define DOCTEST_CONFIG_IMPLEMENT #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #if defined _WIN32 || defined __CYGWIN__ #if DOCTEST_MSVC #define DOCTEST_SYMBOL_EXPORT __declspec(dllexport) #define DOCTEST_SYMBOL_IMPORT __declspec(dllimport) #else // MSVC #define DOCTEST_SYMBOL_EXPORT __attribute__((dllexport)) #define DOCTEST_SYMBOL_IMPORT __attribute__((dllimport)) #endif // MSVC #else // _WIN32 #define DOCTEST_SYMBOL_EXPORT __attribute__((visibility("default"))) #define DOCTEST_SYMBOL_IMPORT #endif // _WIN32 #ifdef DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #ifdef DOCTEST_CONFIG_IMPLEMENT #define DOCTEST_INTERFACE DOCTEST_SYMBOL_EXPORT #else // DOCTEST_CONFIG_IMPLEMENT #define DOCTEST_INTERFACE DOCTEST_SYMBOL_IMPORT #endif // DOCTEST_CONFIG_IMPLEMENT #else // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #define DOCTEST_INTERFACE #endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #if DOCTEST_MSVC #define DOCTEST_NOINLINE __declspec(noinline) #define DOCTEST_UNUSED #define DOCTEST_ALIGNMENT(x) #else // MSVC #define DOCTEST_NOINLINE __attribute__((noinline)) #define DOCTEST_UNUSED __attribute__((unused)) #define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x))) #endif // MSVC #ifndef DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK #define DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK 5 #endif // DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK // ================================================================================================= // == FEATURE DETECTION END ======================================================================== // ================================================================================================= // internal macros for string concatenation and anonymous variable name generation #define DOCTEST_CAT_IMPL(s1, s2) s1##s2 #define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2) #ifdef __COUNTER__ // not standard and may be missing for some compilers #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __COUNTER__) #else // __COUNTER__ #define DOCTEST_ANONYMOUS(x) DOCTEST_CAT(x, __LINE__) #endif // __COUNTER__ // macro for making a string out of an identifier #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TOSTR_IMPL(...) #__VA_ARGS__ #define DOCTEST_TOSTR(...) DOCTEST_TOSTR_IMPL(__VA_ARGS__) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TOSTR_IMPL(x) #x #define DOCTEST_TOSTR(x) DOCTEST_TOSTR_IMPL(x) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // counts the number of elements in a C string #define DOCTEST_COUNTOF(x) (sizeof(x) / sizeof(x[0])) #ifndef DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE #define DOCTEST_REF_WRAP(x) x& #else // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE #define DOCTEST_REF_WRAP(x) x #endif // DOCTEST_CONFIG_ASSERTION_PARAMETERS_BY_VALUE // not using __APPLE__ because... this is how Catch does it #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) #define DOCTEST_PLATFORM_MAC #elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) #define DOCTEST_PLATFORM_IPHONE #elif defined(_WIN32) #define DOCTEST_PLATFORM_WINDOWS #else #define DOCTEST_PLATFORM_LINUX #endif #define DOCTEST_GLOBAL_NO_WARNINGS(var) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") static int var DOCTEST_UNUSED #define DOCTEST_GLOBAL_NO_WARNINGS_END() DOCTEST_CLANG_SUPPRESS_WARNING_POP // should probably take a look at https://github.com/scottt/debugbreak #ifdef DOCTEST_PLATFORM_MAC #define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) #elif DOCTEST_MSVC #define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak() #elif defined(__MINGW32__) extern "C" __declspec(dllimport) void __stdcall DebugBreak(); #define DOCTEST_BREAK_INTO_DEBUGGER() ::DebugBreak() #else // linux #define DOCTEST_BREAK_INTO_DEBUGGER() ((void)0) #endif // linux #if DOCTEST_CLANG // to detect if libc++ is being used with clang (the _LIBCPP_VERSION identifier) #include #endif // clang #ifdef _LIBCPP_VERSION // not forward declaring ostream for libc++ because I had some problems (inline namespaces vs c++98) // so the header is used - also it is very light and doesn't drag a ton of stuff #include #else // _LIBCPP_VERSION #ifndef DOCTEST_CONFIG_USE_IOSFWD namespace std { template struct char_traits; template <> struct char_traits; template class basic_ostream; typedef basic_ostream > ostream; } // namespace std #else // DOCTEST_CONFIG_USE_IOSFWD #include #endif // DOCTEST_CONFIG_USE_IOSFWD #endif // _LIBCPP_VERSION // static assert macro - because of the c++98 support requires that the message is an // identifier (no spaces and not a C string) - example without quotes: I_am_a_message // taken from here: http://stackoverflow.com/a/1980156/3162383 #ifdef DOCTEST_CONFIG_WITH_STATIC_ASSERT #define DOCTEST_STATIC_ASSERT(expression, message) static_assert(expression, #message) #else // DOCTEST_CONFIG_WITH_STATIC_ASSERT #define DOCTEST_STATIC_ASSERT(expression, message) \ struct DOCTEST_CAT(__static_assertion_at_line_, __LINE__) \ { \ doctest::detail::static_assert_impl::StaticAssertion((expression))> \ DOCTEST_CAT(DOCTEST_CAT(DOCTEST_CAT(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), \ _), \ message); \ }; \ typedef doctest::detail::static_assert_impl::StaticAssertionTest( \ sizeof(DOCTEST_CAT(__static_assertion_at_line_, __LINE__)))> \ DOCTEST_CAT(__static_assertion_test_at_line_, __LINE__) #endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT #ifdef DOCTEST_CONFIG_WITH_NULLPTR #ifdef _LIBCPP_VERSION #include #else // _LIBCPP_VERSION namespace std { typedef decltype(nullptr) nullptr_t; } #endif // _LIBCPP_VERSION #endif // DOCTEST_CONFIG_WITH_NULLPTR #ifndef DOCTEST_CONFIG_DISABLE #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #include #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS namespace doctest { namespace detail { struct TestSuite { const char* m_test_suite; const char* m_description; bool m_skip; bool m_may_fail; bool m_should_fail; int m_expected_failures; double m_timeout; TestSuite& operator*(const char* in) { m_test_suite = in; // clear state m_description = 0; m_skip = false; m_may_fail = false; m_should_fail = false; m_expected_failures = 0; m_timeout = 0; return *this; } template TestSuite& operator*(const T& in) { in.fill(*this); return *this; } }; } // namespace detail } // namespace doctest // in a separate namespace outside of doctest because the DOCTEST_TEST_SUITE macro // introduces an anonymous namespace in which getCurrentTestSuite gets overridden namespace doctest_detail_test_suite_ns { DOCTEST_INTERFACE doctest::detail::TestSuite& getCurrentTestSuite(); } // namespace doctest_detail_test_suite_ns #endif // DOCTEST_CONFIG_DISABLE namespace doctest { // A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length // of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for: // - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128) // - if small - capacity left before going on the heap - using the lowest 5 bits // - if small - 2 bits are left unused - the second and third highest ones // - if small - acts as a null terminator if strlen() is 23 (24 including the null terminator) // and the "is small" bit remains "0" ("as well as the capacity left") so its OK // Idea taken from this lecture about the string implementation of facebook/folly - fbstring // https://www.youtube.com/watch?v=kPR8h4-qZdk // TODO: // - optimizations - like not deleting memory unnecessarily in operator= and etc. // - resize/reserve/clear // - substr // - replace // - back/front // - iterator stuff // - find & friends // - push_back/pop_back // - assign/insert/erase // - relational operators as free functions - taking const char* as one of the params class DOCTEST_INTERFACE String { static const unsigned len = 24; //!OCLINT avoid private static members static const unsigned last = len - 1; //!OCLINT avoid private static members struct view // len should be more than sizeof(view) - because of the final byte for flags { char* ptr; unsigned size; unsigned capacity; }; union { char buf[len]; view data; }; void copy(const String& other); void setOnHeap() { *reinterpret_cast(&buf[last]) = 128; } void setLast(unsigned in = last) { buf[last] = char(in); } public: String() { buf[0] = '\0'; setLast(); } String(const char* in); String(const String& other) { copy(other); } ~String() { if(!isOnStack()) delete[] data.ptr; } // GCC 4.9/5/6 report Wstrict-overflow when optimizations are ON and it got inlined in the vector class somewhere... // see commit 574ef95f0cd379118be5011704664e4b5351f1e0 and build https://travis-ci.org/onqtam/doctest/builds/230671611 DOCTEST_NOINLINE String& operator=(const String& other) { if(this != &other) { if(!isOnStack()) delete[] data.ptr; copy(other); } return *this; } String& operator+=(const String& other); String operator+(const String& other) const { return String(*this) += other; } #ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES String(String&& other); String& operator=(String&& other); #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES bool isOnStack() const { return (buf[last] & 128) == 0; } char operator[](unsigned i) const { return const_cast(this)->operator[](i); } // NOLINT char& operator[](unsigned i) { if(isOnStack()) return reinterpret_cast(buf)[i]; return data.ptr[i]; } const char* c_str() const { return const_cast(this)->c_str(); } // NOLINT char* c_str() { if(isOnStack()) return reinterpret_cast(buf); return data.ptr; } DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wmaybe-uninitialized") unsigned size() const { if(isOnStack()) return last - (unsigned(buf[last]) & 31); // using "last" would work only if "len" is 32 return data.size; } DOCTEST_GCC_SUPPRESS_WARNING_POP unsigned capacity() const { if(isOnStack()) return len; return data.capacity; } int compare(const char* other, bool no_case = false) const; int compare(const String& other, bool no_case = false) const; }; // clang-format off inline bool operator==(const String& lhs, const String& rhs) { return lhs.compare(rhs) == 0; } inline bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; } inline bool operator< (const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; } inline bool operator> (const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; } inline bool operator<=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) < 0 : true; } inline bool operator>=(const String& lhs, const String& rhs) { return (lhs != rhs) ? lhs.compare(rhs) > 0 : true; } // clang-format on DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in); namespace detail { #ifndef DOCTEST_CONFIG_WITH_STATIC_ASSERT namespace static_assert_impl { template struct StaticAssertion; template <> struct StaticAssertion {}; template struct StaticAssertionTest {}; } // namespace static_assert_impl #endif // DOCTEST_CONFIG_WITH_STATIC_ASSERT template struct enable_if {}; template struct enable_if { typedef TYPE type; }; template struct deferred_false // cppcheck-suppress unusedStructMember { static const bool value = false; }; // to silence the warning "-Wzero-as-null-pointer-constant" only for gcc 5 for the Approx template ctor - pragmas don't work for it... inline void* getNull() { return 0; } namespace has_insertion_operator_impl { typedef char no; typedef char yes[2]; struct any_t { template // cppcheck-suppress noExplicitConstructor any_t(const DOCTEST_REF_WRAP(T)); }; yes& testStreamable(std::ostream&); no testStreamable(no); no operator<<(const std::ostream&, const any_t&); template struct has_insertion_operator { static std::ostream& s; static const DOCTEST_REF_WRAP(T) t; static const bool value = sizeof(testStreamable(s << t)) == sizeof(yes); }; } // namespace has_insertion_operator_impl template struct has_insertion_operator : has_insertion_operator_impl::has_insertion_operator {}; DOCTEST_INTERFACE void my_memcpy(void* dest, const void* src, unsigned num); DOCTEST_INTERFACE std::ostream* createStream(); DOCTEST_INTERFACE String getStreamResult(std::ostream*); DOCTEST_INTERFACE void freeStream(std::ostream*); template struct StringMakerBase { template static String convert(const DOCTEST_REF_WRAP(T)) { return "{?}"; } }; template <> struct StringMakerBase { template static String convert(const DOCTEST_REF_WRAP(T) in) { std::ostream* s = createStream(); *s << in; String result = getStreamResult(s); freeStream(s); return result; } }; DOCTEST_INTERFACE String rawMemoryToString(const void* object, unsigned size); template String rawMemoryToString(const DOCTEST_REF_WRAP(T) object) { return rawMemoryToString(&object, sizeof(object)); } class NullType { }; template struct Typelist { typedef T Head; typedef U Tail; }; // type of recursive function template struct ForEachType; // Recursion rule template struct ForEachType, Callable> : public ForEachType { enum { value = 1 + ForEachType::value }; explicit ForEachType(Callable& callable) : ForEachType(callable) { #if DOCTEST_MSVC && DOCTEST_MSVC < DOCTEST_COMPILER(19, 10, 0) callable.operator()(); #else // MSVC callable.template operator()(); #endif // MSVC } }; // Recursion end template struct ForEachType, Callable> { public: enum { value = 0 }; explicit ForEachType(Callable& callable) { #if DOCTEST_MSVC && DOCTEST_MSVC < DOCTEST_COMPILER(19, 10, 0) callable.operator()(); #else // MSVC callable.template operator()(); #endif // MSVC } }; template const char* type_to_string() { return "<>"; } } // namespace detail template struct Types { private: typedef typename Types::Result TailResult; public: typedef detail::Typelist Result; }; template <> struct Types<> { typedef detail::NullType Result; }; template struct StringMaker : detail::StringMakerBase::value> {}; template struct StringMaker { template static String convert(U* p) { if(p) return detail::rawMemoryToString(p); return "NULL"; } }; template struct StringMaker { static String convert(R C::*p) { if(p) return detail::rawMemoryToString(p); return "NULL"; } }; template String toString(const DOCTEST_REF_WRAP(T) value) { return StringMaker::convert(value); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE String toString(char* in); DOCTEST_INTERFACE String toString(const char* in); #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE String toString(bool in); DOCTEST_INTERFACE String toString(float in); DOCTEST_INTERFACE String toString(double in); DOCTEST_INTERFACE String toString(double long in); DOCTEST_INTERFACE String toString(char in); DOCTEST_INTERFACE String toString(char signed in); DOCTEST_INTERFACE String toString(char unsigned in); DOCTEST_INTERFACE String toString(int short in); DOCTEST_INTERFACE String toString(int short unsigned in); DOCTEST_INTERFACE String toString(int in); DOCTEST_INTERFACE String toString(int unsigned in); DOCTEST_INTERFACE String toString(int long in); DOCTEST_INTERFACE String toString(int long unsigned in); #ifdef DOCTEST_CONFIG_WITH_LONG_LONG DOCTEST_INTERFACE String toString(int long long in); DOCTEST_INTERFACE String toString(int long long unsigned in); #endif // DOCTEST_CONFIG_WITH_LONG_LONG #ifdef DOCTEST_CONFIG_WITH_NULLPTR DOCTEST_INTERFACE String toString(std::nullptr_t in); #endif // DOCTEST_CONFIG_WITH_NULLPTR class DOCTEST_INTERFACE Approx { public: explicit Approx(double value); Approx operator()(double value) const { Approx approx(value); approx.epsilon(m_epsilon); approx.scale(m_scale); return approx; } #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template explicit Approx(const T& value, typename detail::enable_if::value>::type* = static_cast(detail::getNull())) { *this = Approx(static_cast(value)); } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS // clang-format off // overloads for double - the first one is necessary as it is in the implementation part of doctest // as for the others - keeping them for potentially faster compile times DOCTEST_INTERFACE friend bool operator==(double lhs, Approx const& rhs); friend bool operator==(Approx const& lhs, double rhs) { return operator==(rhs, lhs); } friend bool operator!=(double lhs, Approx const& rhs) { return !operator==(lhs, rhs); } friend bool operator!=(Approx const& lhs, double rhs) { return !operator==(rhs, lhs); } friend bool operator<=(double lhs, Approx const& rhs) { return lhs < rhs.m_value || lhs == rhs; } friend bool operator<=(Approx const& lhs, double rhs) { return lhs.m_value < rhs || lhs == rhs; } friend bool operator>=(double lhs, Approx const& rhs) { return lhs > rhs.m_value || lhs == rhs; } friend bool operator>=(Approx const& lhs, double rhs) { return lhs.m_value > rhs || lhs == rhs; } friend bool operator< (double lhs, Approx const& rhs) { return lhs < rhs.m_value && lhs != rhs; } friend bool operator< (Approx const& lhs, double rhs) { return lhs.m_value < rhs && lhs != rhs; } friend bool operator> (double lhs, Approx const& rhs) { return lhs > rhs.m_value && lhs != rhs; } friend bool operator> (Approx const& lhs, double rhs) { return lhs.m_value > rhs && lhs != rhs; } #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS #define DOCTEST_APPROX_PREFIX \ template friend typename detail::enable_if::value, bool>::type DOCTEST_APPROX_PREFIX operator==(const T& lhs, const Approx& rhs) { return operator==(double(lhs), rhs); } DOCTEST_APPROX_PREFIX operator==(const Approx& lhs, const T& rhs) { return operator==(rhs, lhs); } DOCTEST_APPROX_PREFIX operator!=(const T& lhs, const Approx& rhs) { return !operator==(lhs, rhs); } DOCTEST_APPROX_PREFIX operator!=(const Approx& lhs, const T& rhs) { return !operator==(rhs, lhs); } DOCTEST_APPROX_PREFIX operator<=(const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value || lhs == rhs; } DOCTEST_APPROX_PREFIX operator<=(const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) || lhs == rhs; } DOCTEST_APPROX_PREFIX operator>=(const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value || lhs == rhs; } DOCTEST_APPROX_PREFIX operator>=(const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) || lhs == rhs; } DOCTEST_APPROX_PREFIX operator< (const T& lhs, const Approx& rhs) { return double(lhs) < rhs.m_value && lhs != rhs; } DOCTEST_APPROX_PREFIX operator< (const Approx& lhs, const T& rhs) { return lhs.m_value < double(rhs) && lhs != rhs; } DOCTEST_APPROX_PREFIX operator> (const T& lhs, const Approx& rhs) { return double(lhs) > rhs.m_value && lhs != rhs; } DOCTEST_APPROX_PREFIX operator> (const Approx& lhs, const T& rhs) { return lhs.m_value > double(rhs) && lhs != rhs; } #undef DOCTEST_APPROX_PREFIX #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS // clang-format on Approx& epsilon(double newEpsilon) { m_epsilon = newEpsilon; return *this; } #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template typename detail::enable_if::value, Approx&>::type epsilon( const T& newEpsilon) { m_epsilon = static_cast(newEpsilon); return *this; } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS Approx& scale(double newScale) { m_scale = newScale; return *this; } #ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS template typename detail::enable_if::value, Approx&>::type scale( const T& newScale) { m_scale = static_cast(newScale); return *this; } #endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS String toString() const; private: double m_epsilon; double m_scale; double m_value; }; template <> inline String toString(const DOCTEST_REF_WRAP(Approx) value) { return value.toString(); } #if !defined(DOCTEST_CONFIG_DISABLE) namespace detail { // the function type this library works with typedef void (*funcType)(); namespace assertType { enum Enum { // macro traits is_warn = 1, is_check = 2, is_require = 4, is_throws = 8, is_throws_as = 16, is_nothrow = 32, is_fast = 64, // not checked anywhere - used just to distinguish the types is_false = 128, is_unary = 256, is_eq = 512, is_ne = 1024, is_lt = 2048, is_gt = 4096, is_ge = 8192, is_le = 16384, // macro types DT_WARN = is_warn, DT_CHECK = is_check, DT_REQUIRE = is_require, DT_WARN_FALSE = is_false | is_warn, DT_CHECK_FALSE = is_false | is_check, DT_REQUIRE_FALSE = is_false | is_require, DT_WARN_THROWS = is_throws | is_warn, DT_CHECK_THROWS = is_throws | is_check, DT_REQUIRE_THROWS = is_throws | is_require, DT_WARN_THROWS_AS = is_throws_as | is_warn, DT_CHECK_THROWS_AS = is_throws_as | is_check, DT_REQUIRE_THROWS_AS = is_throws_as | is_require, DT_WARN_NOTHROW = is_nothrow | is_warn, DT_CHECK_NOTHROW = is_nothrow | is_check, DT_REQUIRE_NOTHROW = is_nothrow | is_require, DT_WARN_EQ = is_eq | is_warn, DT_CHECK_EQ = is_eq | is_check, DT_REQUIRE_EQ = is_eq | is_require, DT_WARN_NE = is_ne | is_warn, DT_CHECK_NE = is_ne | is_check, DT_REQUIRE_NE = is_ne | is_require, DT_WARN_GT = is_gt | is_warn, DT_CHECK_GT = is_gt | is_check, DT_REQUIRE_GT = is_gt | is_require, DT_WARN_LT = is_lt | is_warn, DT_CHECK_LT = is_lt | is_check, DT_REQUIRE_LT = is_lt | is_require, DT_WARN_GE = is_ge | is_warn, DT_CHECK_GE = is_ge | is_check, DT_REQUIRE_GE = is_ge | is_require, DT_WARN_LE = is_le | is_warn, DT_CHECK_LE = is_le | is_check, DT_REQUIRE_LE = is_le | is_require, DT_WARN_UNARY = is_unary | is_warn, DT_CHECK_UNARY = is_unary | is_check, DT_REQUIRE_UNARY = is_unary | is_require, DT_WARN_UNARY_FALSE = is_false | is_unary | is_warn, DT_CHECK_UNARY_FALSE = is_false | is_unary | is_check, DT_REQUIRE_UNARY_FALSE = is_false | is_unary | is_require, DT_FAST_WARN_EQ = is_fast | is_eq | is_warn, DT_FAST_CHECK_EQ = is_fast | is_eq | is_check, DT_FAST_REQUIRE_EQ = is_fast | is_eq | is_require, DT_FAST_WARN_NE = is_fast | is_ne | is_warn, DT_FAST_CHECK_NE = is_fast | is_ne | is_check, DT_FAST_REQUIRE_NE = is_fast | is_ne | is_require, DT_FAST_WARN_GT = is_fast | is_gt | is_warn, DT_FAST_CHECK_GT = is_fast | is_gt | is_check, DT_FAST_REQUIRE_GT = is_fast | is_gt | is_require, DT_FAST_WARN_LT = is_fast | is_lt | is_warn, DT_FAST_CHECK_LT = is_fast | is_lt | is_check, DT_FAST_REQUIRE_LT = is_fast | is_lt | is_require, DT_FAST_WARN_GE = is_fast | is_ge | is_warn, DT_FAST_CHECK_GE = is_fast | is_ge | is_check, DT_FAST_REQUIRE_GE = is_fast | is_ge | is_require, DT_FAST_WARN_LE = is_fast | is_le | is_warn, DT_FAST_CHECK_LE = is_fast | is_le | is_check, DT_FAST_REQUIRE_LE = is_fast | is_le | is_require, DT_FAST_WARN_UNARY = is_fast | is_unary | is_warn, DT_FAST_CHECK_UNARY = is_fast | is_unary | is_check, DT_FAST_REQUIRE_UNARY = is_fast | is_unary | is_require, DT_FAST_WARN_UNARY_FALSE = is_fast | is_false | is_unary | is_warn, DT_FAST_CHECK_UNARY_FALSE = is_fast | is_false | is_unary | is_check, DT_FAST_REQUIRE_UNARY_FALSE = is_fast | is_false | is_unary | is_require }; } // namespace assertType DOCTEST_INTERFACE const char* assertString(assertType::Enum val); // clang-format off template struct decay_array { typedef T type; }; template struct decay_array { typedef T* type; }; template struct decay_array { typedef T* type; }; template struct not_char_pointer { enum { value = 1 }; }; template<> struct not_char_pointer { enum { value = 0 }; }; template<> struct not_char_pointer { enum { value = 0 }; }; template struct can_use_op : not_char_pointer::type> {}; // clang-format on struct TestFailureException { }; DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); DOCTEST_INTERFACE void fastAssertThrowIfFlagSet(int flags); struct TestAccessibleContextState { bool no_throw; // to skip exceptions-related assertion macros bool success; // include successful assertions in output }; struct ContextState; DOCTEST_INTERFACE TestAccessibleContextState* getTestsContextState(); struct DOCTEST_INTERFACE SubcaseSignature { const char* m_name; const char* m_file; int m_line; SubcaseSignature(const char* name, const char* file, int line) : m_name(name) , m_file(file) , m_line(line) {} bool operator<(const SubcaseSignature& other) const; }; // cppcheck-suppress copyCtorAndEqOperator struct DOCTEST_INTERFACE Subcase { SubcaseSignature m_signature; bool m_entered; Subcase(const char* name, const char* file, int line); Subcase(const Subcase& other); ~Subcase(); operator bool() const { return m_entered; } }; template String stringifyBinaryExpr(const DOCTEST_REF_WRAP(L) lhs, const char* op, const DOCTEST_REF_WRAP(R) rhs) { return toString(lhs) + op + toString(rhs); } struct DOCTEST_INTERFACE Result { bool m_passed; String m_decomposition; ~Result(); DOCTEST_NOINLINE Result(bool passed = false, const String& decomposition = String()) : m_passed(passed) , m_decomposition(decomposition) {} DOCTEST_NOINLINE Result(const Result& other) : m_passed(other.m_passed) , m_decomposition(other.m_decomposition) {} Result& operator=(const Result& other); operator bool() { return !m_passed; } // clang-format off // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence template Result& operator& (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator^ (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator| (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator&& (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator|| (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator== (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator!= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator< (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator> (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator<= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator>= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator+= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator-= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator*= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator/= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator%= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator<<=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator>>=(const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator&= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator^= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } template Result& operator|= (const R&) { DOCTEST_STATIC_ASSERT(deferred_false::value, Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); return *this; } // clang-format on }; #ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-compare") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wdouble-promotion") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wconversion") //DOCTEST_CLANG_SUPPRESS_WARNING("-Wfloat-equal") DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-compare") //DOCTEST_GCC_SUPPRESS_WARNING("-Wdouble-promotion") //DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") //DOCTEST_GCC_SUPPRESS_WARNING("-Wfloat-equal") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH // http://stackoverflow.com/questions/39479163 what's the difference between C4018 and C4389 DOCTEST_MSVC_SUPPRESS_WARNING(4388) // signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4389) // 'operator' : signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4018) // 'expression' : signed/unsigned mismatch //DOCTEST_MSVC_SUPPRESS_WARNING(4805) // 'operation' : unsafe mix of type 'type' and type 'type' in operation #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION // clang-format off #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE bool #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_COMPARISON_RETURN_TYPE typename enable_if::value || can_use_op::value, bool>::type inline bool eq(const char* lhs, const char* rhs) { return String(lhs) == String(rhs); } inline bool ne(const char* lhs, const char* rhs) { return String(lhs) != String(rhs); } inline bool lt(const char* lhs, const char* rhs) { return String(lhs) < String(rhs); } inline bool gt(const char* lhs, const char* rhs) { return String(lhs) > String(rhs); } inline bool le(const char* lhs, const char* rhs) { return String(lhs) <= String(rhs); } inline bool ge(const char* lhs, const char* rhs) { return String(lhs) >= String(rhs); } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING template DOCTEST_COMPARISON_RETURN_TYPE eq(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs == rhs; } template DOCTEST_COMPARISON_RETURN_TYPE ne(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs != rhs; } template DOCTEST_COMPARISON_RETURN_TYPE lt(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs < rhs; } template DOCTEST_COMPARISON_RETURN_TYPE gt(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs > rhs; } template DOCTEST_COMPARISON_RETURN_TYPE le(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs <= rhs; } template DOCTEST_COMPARISON_RETURN_TYPE ge(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { return lhs >= rhs; } // clang-format on #ifndef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_CMP_EQ(l, r) l == r #define DOCTEST_CMP_NE(l, r) l != r #define DOCTEST_CMP_GT(l, r) l > r #define DOCTEST_CMP_LT(l, r) l < r #define DOCTEST_CMP_GE(l, r) l >= r #define DOCTEST_CMP_LE(l, r) l <= r #else // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_CMP_EQ(l, r) eq(l, r) #define DOCTEST_CMP_NE(l, r) ne(l, r) #define DOCTEST_CMP_GT(l, r) gt(l, r) #define DOCTEST_CMP_LT(l, r) lt(l, r) #define DOCTEST_CMP_GE(l, r) ge(l, r) #define DOCTEST_CMP_LE(l, r) le(l, r) #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING #define DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(op, op_str, op_macro) \ template \ DOCTEST_NOINLINE Result operator op(const DOCTEST_REF_WRAP(R) rhs) { \ bool res = op_macro(lhs, rhs); \ if(m_assert_type & assertType::is_false) \ res = !res; \ if(!res || doctest::detail::getTestsContextState()->success) \ return Result(res, stringifyBinaryExpr(lhs, op_str, rhs)); \ return Result(res); \ } #define DOCTEST_FORBIT_EXPRESSION(op) \ template \ Expression_lhs& operator op(const R&) { \ DOCTEST_STATIC_ASSERT(deferred_false::value, \ Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison); \ return *this; \ } template // cppcheck-suppress copyCtorAndEqOperator struct Expression_lhs { L lhs; assertType::Enum m_assert_type; explicit Expression_lhs(L in, assertType::Enum at) : lhs(in) , m_assert_type(at) {} DOCTEST_NOINLINE operator Result() { bool res = !!lhs; if(m_assert_type & assertType::is_false) //!OCLINT bitwise operator in conditional res = !res; if(!res || getTestsContextState()->success) return Result(res, toString(lhs)); return Result(res); } // clang-format off DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(==, " == ", DOCTEST_CMP_EQ) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(!=, " != ", DOCTEST_CMP_NE) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>, " > ", DOCTEST_CMP_GT) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<, " < ", DOCTEST_CMP_LT) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(>=, " >= ", DOCTEST_CMP_GE) //!OCLINT bitwise operator in conditional DOCTEST_DO_BINARY_EXPRESSION_COMPARISON(<=, " <= ", DOCTEST_CMP_LE) //!OCLINT bitwise operator in conditional // clang-format on // forbidding some expressions based on this table: http://en.cppreference.com/w/cpp/language/operator_precedence DOCTEST_FORBIT_EXPRESSION(&) DOCTEST_FORBIT_EXPRESSION(^) DOCTEST_FORBIT_EXPRESSION(|) DOCTEST_FORBIT_EXPRESSION(&&) DOCTEST_FORBIT_EXPRESSION(||) DOCTEST_FORBIT_EXPRESSION(=) DOCTEST_FORBIT_EXPRESSION(+=) DOCTEST_FORBIT_EXPRESSION(-=) DOCTEST_FORBIT_EXPRESSION(*=) DOCTEST_FORBIT_EXPRESSION(/=) DOCTEST_FORBIT_EXPRESSION(%=) DOCTEST_FORBIT_EXPRESSION(<<=) DOCTEST_FORBIT_EXPRESSION(>>=) DOCTEST_FORBIT_EXPRESSION(&=) DOCTEST_FORBIT_EXPRESSION(^=) DOCTEST_FORBIT_EXPRESSION(|=) // these 2 are unfortunate because they should be allowed - they have higher precedence over the comparisons, but the // ExpressionDecomposer class uses the left shift operator to capture the left operand of the binary expression... DOCTEST_FORBIT_EXPRESSION(<<) DOCTEST_FORBIT_EXPRESSION(>>) }; #ifndef DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // DOCTEST_CONFIG_NO_COMPARISON_WARNING_SUPPRESSION struct ExpressionDecomposer { assertType::Enum m_assert_type; ExpressionDecomposer(assertType::Enum at) : m_assert_type(at) {} // The right operator for capturing expressions is "<=" instead of "<<" (based on the operator precedence table) // but then there will be warnings from GCC about "-Wparentheses" and since "_Pragma()" is problematic this will stay for now... // https://github.com/philsquared/Catch/issues/870 // https://github.com/philsquared/Catch/issues/565 template Expression_lhs operator<<(const DOCTEST_REF_WRAP(L) operand) { return Expression_lhs(operand, m_assert_type); } }; struct DOCTEST_INTERFACE TestCase { // not used for determining uniqueness funcType m_test; // a function pointer to the test case String m_full_name; // contains the name (only for templated test cases!) + the template type const char* m_name; // name of the test case const char* m_type; // for templated test cases - gets appended to the real name const char* m_test_suite; // the test suite in which the test was added const char* m_description; bool m_skip; bool m_may_fail; bool m_should_fail; int m_expected_failures; double m_timeout; // fields by which uniqueness of test cases shall be determined const char* m_file; // the file in which the test was registered unsigned m_line; // the line where the test was registered int m_template_id; // an ID used to distinguish between the different versions of a templated test case TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, const char* type = "", int template_id = -1); // for gcc 4.7 DOCTEST_NOINLINE ~TestCase() {} TestCase& operator*(const char* in); template TestCase& operator*(const T& in) { in.fill(*this); return *this; } TestCase(const TestCase& other) { *this = other; } TestCase& operator=(const TestCase& other); bool operator<(const TestCase& other) const; }; // forward declarations of functions used by the macros DOCTEST_INTERFACE int regTest(const TestCase& tc); DOCTEST_INTERFACE int setTestSuite(const TestSuite& ts); namespace binaryAssertComparison { enum Enum { eq = 0, ne, gt, lt, ge, le }; } // namespace binaryAssertComparison // clang-format off template struct RelationalComparator { bool operator()(const DOCTEST_REF_WRAP(L), const DOCTEST_REF_WRAP(R) ) const { return false; } }; template struct RelationalComparator<0, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return eq(lhs, rhs); } }; template struct RelationalComparator<1, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return ne(lhs, rhs); } }; template struct RelationalComparator<2, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return gt(lhs, rhs); } }; template struct RelationalComparator<3, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return lt(lhs, rhs); } }; template struct RelationalComparator<4, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return ge(lhs, rhs); } }; template struct RelationalComparator<5, L, R> { bool operator()(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) const { return le(lhs, rhs); } }; // clang-format on struct DOCTEST_INTERFACE ResultBuilder { assertType::Enum m_assert_type; const char* m_file; int m_line; const char* m_expr; const char* m_exception_type; Result m_result; bool m_threw; bool m_threw_as; bool m_failed; String m_exception; ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type = ""); ~ResultBuilder(); void setResult(const Result& res) { m_result = res; } template DOCTEST_NOINLINE void binary_assert(const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { m_result.m_passed = RelationalComparator()(lhs, rhs); if(!m_result.m_passed || getTestsContextState()->success) m_result.m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs); } template DOCTEST_NOINLINE void unary_assert(const DOCTEST_REF_WRAP(L) val) { m_result.m_passed = !!val; if(m_assert_type & assertType::is_false) //!OCLINT bitwise operator in conditional m_result.m_passed = !m_result.m_passed; if(!m_result.m_passed || getTestsContextState()->success) m_result.m_decomposition = toString(val); } void unexpectedExceptionOccurred(); bool log(); void react() const; }; namespace assertAction { enum Enum { nothing = 0, dbgbreak = 1, shouldthrow = 2 }; } // namespace assertAction template DOCTEST_NOINLINE int fast_binary_assert(assertType::Enum at, const char* file, int line, const char* expr, const DOCTEST_REF_WRAP(L) lhs, const DOCTEST_REF_WRAP(R) rhs) { ResultBuilder rb(at, file, line, expr); rb.m_result.m_passed = RelationalComparator()(lhs, rhs); if(!rb.m_result.m_passed || getTestsContextState()->success) rb.m_result.m_decomposition = stringifyBinaryExpr(lhs, ", ", rhs); int res = 0; if(rb.log()) res |= assertAction::dbgbreak; if(rb.m_failed && checkIfShouldThrow(at)) res |= assertAction::shouldthrow; #ifdef DOCTEST_CONFIG_SUPER_FAST_ASSERTS // ######################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK TO SEE THE FAILING ASSERTION // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ######################################################################################### if(res & assertAction::dbgbreak) DOCTEST_BREAK_INTO_DEBUGGER(); fastAssertThrowIfFlagSet(res); #endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS return res; } template DOCTEST_NOINLINE int fast_unary_assert(assertType::Enum at, const char* file, int line, const char* val_str, const DOCTEST_REF_WRAP(L) val) { ResultBuilder rb(at, file, line, val_str); rb.m_result.m_passed = !!val; if(at & assertType::is_false) //!OCLINT bitwise operator in conditional rb.m_result.m_passed = !rb.m_result.m_passed; if(!rb.m_result.m_passed || getTestsContextState()->success) rb.m_result.m_decomposition = toString(val); int res = 0; if(rb.log()) res |= assertAction::dbgbreak; if(rb.m_failed && checkIfShouldThrow(at)) res |= assertAction::shouldthrow; #ifdef DOCTEST_CONFIG_SUPER_FAST_ASSERTS // ######################################################################################### // IF THE DEBUGGER BREAKS HERE - GO 1 LEVEL UP IN THE CALLSTACK TO SEE THE FAILING ASSERTION // THIS IS THE EFFECT OF HAVING 'DOCTEST_CONFIG_SUPER_FAST_ASSERTS' DEFINED // ######################################################################################### if(res & assertAction::dbgbreak) DOCTEST_BREAK_INTO_DEBUGGER(); fastAssertThrowIfFlagSet(res); #endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS return res; } struct DOCTEST_INTERFACE IExceptionTranslator { virtual ~IExceptionTranslator() {} virtual bool translate(String&) const = 0; }; template class ExceptionTranslator : public IExceptionTranslator //!OCLINT destructor of virtual class { public: explicit ExceptionTranslator(String (*translateFunction)(T)) : m_translateFunction(translateFunction) {} bool translate(String& res) const { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS try { throw; // cppcheck-suppress catchExceptionByValue } catch(T ex) { // NOLINT res = m_translateFunction(ex); //!OCLINT parameter reassignment return true; } catch(...) {} //!OCLINT - empty catch statement #endif // DOCTEST_CONFIG_NO_EXCEPTIONS ((void)res); // to silence -Wunused-parameter return false; } protected: String (*m_translateFunction)(T); }; DOCTEST_INTERFACE void registerExceptionTranslatorImpl( const IExceptionTranslator* translateFunction); // FIX FOR VISUAL STUDIO VERSIONS PRIOR TO 2015 - they failed to compile the call to operator<< with // std::ostream passed as a reference noting that there is a use of an undefined type (which there isn't) DOCTEST_INTERFACE void writeStringToStream(std::ostream* s, const String& str); template struct StringStreamBase { template static void convert(std::ostream* s, const T& in) { writeStringToStream(s, toString(in)); } // always treat char* as a string in this context - no matter // if DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING is defined static void convert(std::ostream* s, const char* in) { writeStringToStream(s, String(in)); } }; template <> struct StringStreamBase { template static void convert(std::ostream* s, const T& in) { *s << in; } }; template struct StringStream : StringStreamBase::value> {}; template void toStream(std::ostream* s, const T& value) { StringStream::convert(s, value); } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE void toStream(std::ostream* s, char* in); DOCTEST_INTERFACE void toStream(std::ostream* s, const char* in); #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING DOCTEST_INTERFACE void toStream(std::ostream* s, bool in); DOCTEST_INTERFACE void toStream(std::ostream* s, float in); DOCTEST_INTERFACE void toStream(std::ostream* s, double in); DOCTEST_INTERFACE void toStream(std::ostream* s, double long in); DOCTEST_INTERFACE void toStream(std::ostream* s, char in); DOCTEST_INTERFACE void toStream(std::ostream* s, char signed in); DOCTEST_INTERFACE void toStream(std::ostream* s, char unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int short in); DOCTEST_INTERFACE void toStream(std::ostream* s, int short unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int in); DOCTEST_INTERFACE void toStream(std::ostream* s, int unsigned in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long unsigned in); #ifdef DOCTEST_CONFIG_WITH_LONG_LONG DOCTEST_INTERFACE void toStream(std::ostream* s, int long long in); DOCTEST_INTERFACE void toStream(std::ostream* s, int long long unsigned in); #endif // DOCTEST_CONFIG_WITH_LONG_LONG struct IContextScope { virtual ~IContextScope() {} virtual void build(std::ostream*) = 0; }; DOCTEST_INTERFACE void addToContexts(IContextScope* ptr); DOCTEST_INTERFACE void popFromContexts(); DOCTEST_INTERFACE void useContextIfExceptionOccurred(IContextScope* ptr); // cppcheck-suppress copyCtorAndEqOperator class ContextBuilder { friend class ContextScope; struct ICapture { virtual ~ICapture() {} virtual void toStream(std::ostream*) const = 0; }; template struct Capture : ICapture //!OCLINT destructor of virtual class { const T* capture; explicit Capture(const T* in) : capture(in) {} virtual void toStream(std::ostream* s) const { // override detail::toStream(s, *capture); } }; struct Chunk { char buf[sizeof(Capture)] DOCTEST_ALIGNMENT( 2 * sizeof(void*)); // place to construct a Capture }; struct Node { Chunk chunk; Node* next; }; Chunk stackChunks[DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK]; int numCaptures; Node* head; Node* tail; DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wcast-align") void build(std::ostream* s) const { int curr = 0; // iterate over small buffer while(curr < numCaptures && curr < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK) reinterpret_cast(stackChunks[curr++].buf)->toStream(s); // iterate over list Node* curr_elem = head; while(curr < numCaptures) { reinterpret_cast(curr_elem->chunk.buf)->toStream(s); curr_elem = curr_elem->next; ++curr; } } DOCTEST_GCC_SUPPRESS_WARNING_POP // steal the contents of the other - acting as a move constructor... DOCTEST_NOINLINE ContextBuilder(ContextBuilder& other) : numCaptures(other.numCaptures) , head(other.head) , tail(other.tail) { other.numCaptures = 0; other.head = 0; other.tail = 0; my_memcpy(stackChunks, other.stackChunks, unsigned(int(sizeof(Chunk)) * DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK)); } ContextBuilder& operator=(const ContextBuilder&); // NOLINT public: // cppcheck-suppress uninitMemberVar DOCTEST_NOINLINE ContextBuilder() // NOLINT : numCaptures(0) , head(0) , tail(0) {} template DOCTEST_NOINLINE ContextBuilder& operator<<(T& in) { Capture temp(&in); // construct either on stack or on heap // copy the bytes for the whole object - including the vtable because we cant construct // the object directly in the buffer using placement new - need the header... if(numCaptures < DOCTEST_CONFIG_NUM_CAPTURES_ON_STACK) { my_memcpy(stackChunks[numCaptures].buf, &temp, sizeof(Chunk)); } else { Node* curr = new Node; curr->next = 0; if(tail) { tail->next = curr; tail = curr; } else { head = tail = curr; } my_memcpy(tail->chunk.buf, &temp, sizeof(Chunk)); } ++numCaptures; return *this; } DOCTEST_NOINLINE ~ContextBuilder() { // free the linked list - the ones on the stack are left as-is // no destructors are called at all - there is no need while(head) { Node* next = head->next; delete head; head = next; } } #ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES template ContextBuilder& operator<<(const T&&) { DOCTEST_STATIC_ASSERT( deferred_false::value, Cannot_pass_temporaries_or_rvalues_to_the_streaming_operator_because_it_caches_pointers_to_the_passed_objects_for_lazy_evaluation); return *this; } #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES }; class ContextScope : public IContextScope { ContextBuilder contextBuilder; public: DOCTEST_NOINLINE explicit ContextScope(ContextBuilder& temp) : contextBuilder(temp) { addToContexts(this); } DOCTEST_NOINLINE ~ContextScope() { useContextIfExceptionOccurred(this); popFromContexts(); } void build(std::ostream* s) { contextBuilder.build(s); } }; class DOCTEST_INTERFACE MessageBuilder { std::ostream* m_stream; const char* m_file; int m_line; assertType::Enum m_severity; public: MessageBuilder(const char* file, int line, assertType::Enum severity); ~MessageBuilder(); template MessageBuilder& operator<<(const T& in) { toStream(m_stream, in); return *this; } void log(std::ostream&); bool log(); void react(); }; } // namespace detail struct test_suite { const char* data; test_suite(const char* in) : data(in) {} void fill(detail::TestCase& state) const { state.m_test_suite = data; } void fill(detail::TestSuite& state) const { state.m_test_suite = data; } }; struct description { const char* data; description(const char* in) : data(in) {} void fill(detail::TestCase& state) const { state.m_description = data; } void fill(detail::TestSuite& state) const { state.m_description = data; } }; struct skip { bool data; skip(bool in = true) : data(in) {} void fill(detail::TestCase& state) const { state.m_skip = data; } void fill(detail::TestSuite& state) const { state.m_skip = data; } }; struct timeout { double data; timeout(double in) : data(in) {} void fill(detail::TestCase& state) const { state.m_timeout = data; } void fill(detail::TestSuite& state) const { state.m_timeout = data; } }; struct may_fail { bool data; may_fail(bool in = true) : data(in) {} void fill(detail::TestCase& state) const { state.m_may_fail = data; } void fill(detail::TestSuite& state) const { state.m_may_fail = data; } }; struct should_fail { bool data; should_fail(bool in = true) : data(in) {} void fill(detail::TestCase& state) const { state.m_should_fail = data; } void fill(detail::TestSuite& state) const { state.m_should_fail = data; } }; struct expected_failures { int data; expected_failures(int in) : data(in) {} void fill(detail::TestCase& state) const { state.m_expected_failures = data; } void fill(detail::TestSuite& state) const { state.m_expected_failures = data; } }; #endif // DOCTEST_CONFIG_DISABLE #ifndef DOCTEST_CONFIG_DISABLE template int registerExceptionTranslator(String (*translateFunction)(T)) { DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wexit-time-destructors") static detail::ExceptionTranslator exceptionTranslator(translateFunction); DOCTEST_CLANG_SUPPRESS_WARNING_POP detail::registerExceptionTranslatorImpl(&exceptionTranslator); return 0; } #else // DOCTEST_CONFIG_DISABLE template int registerExceptionTranslator(String (*)(T)) { return 0; } #endif // DOCTEST_CONFIG_DISABLE DOCTEST_INTERFACE bool isRunningInTest(); // cppcheck-suppress noCopyConstructor class DOCTEST_INTERFACE Context { #if !defined(DOCTEST_CONFIG_DISABLE) detail::ContextState* p; void parseArgs(int argc, const char* const* argv, bool withDefaults = false); #endif // DOCTEST_CONFIG_DISABLE public: explicit Context(int argc = 0, const char* const* argv = 0); ~Context(); void applyCommandLine(int argc, const char* const* argv); void addFilter(const char* filter, const char* value); void clearFilters(); void setOption(const char* option, int value); void setOption(const char* option, const char* value); bool shouldExit(); int run(); }; } // namespace doctest // if registering is not disabled #if !defined(DOCTEST_CONFIG_DISABLE) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_EXPAND_VA_ARGS(...) __VA_ARGS__ #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_EXPAND_VA_ARGS #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_STRIP_PARENS(x) x #define DOCTEST_HANDLE_BRACED_VA_ARGS(expr) DOCTEST_STRIP_PARENS(DOCTEST_EXPAND_VA_ARGS expr) // registers the test by initializing a dummy var with a function #define DOCTEST_REGISTER_FUNCTION(f, decorators) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = doctest::detail::regTest( \ doctest::detail::TestCase(f, __FILE__, __LINE__, \ doctest_detail_test_suite_ns::getCurrentTestSuite()) * \ decorators); \ DOCTEST_GLOBAL_NO_WARNINGS_END() #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, decorators) \ namespace \ { \ struct der : base \ { \ void f(); \ }; \ static void func() { \ der v; \ v.f(); \ } \ DOCTEST_REGISTER_FUNCTION(func, decorators) \ } \ inline DOCTEST_NOINLINE void der::f() #define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, decorators) \ static void f(); \ DOCTEST_REGISTER_FUNCTION(f, decorators) \ static void f() // for registering tests #define DOCTEST_TEST_CASE(decorators) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) // for registering tests with a fixture #define DOCTEST_TEST_CASE_FIXTURE(c, decorators) \ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), c, \ DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), decorators) // for converting types to strings without the header and demangling #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TYPE_TO_STRING_IMPL(...) \ template <> \ inline const char* type_to_string<__VA_ARGS__>() { \ return "<" #__VA_ARGS__ ">"; \ } #define DOCTEST_TYPE_TO_STRING(...) \ namespace doctest \ { \ namespace detail \ { \ DOCTEST_TYPE_TO_STRING_IMPL(__VA_ARGS__) \ } \ } \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TYPE_TO_STRING_IMPL(x) \ template <> \ inline const char* type_to_string() { \ return "<" #x ">"; \ } #define DOCTEST_TYPE_TO_STRING(x) \ namespace doctest \ { \ namespace detail \ { \ DOCTEST_TYPE_TO_STRING_IMPL(x) \ } \ } \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // for typed tests #define DOCTEST_TEST_CASE_TEMPLATE_IMPL(decorators, T, types, anon) \ template \ inline void anon(); \ struct DOCTEST_CAT(anon, FUNCTOR) \ { \ template \ void operator()() { \ doctest::detail::regTest( \ doctest::detail::TestCase(anon, __FILE__, __LINE__, \ doctest_detail_test_suite_ns::getCurrentTestSuite(), \ doctest::detail::type_to_string(), Index) * \ decorators); \ } \ }; \ inline int DOCTEST_CAT(anon, REG_FUNC)() { \ DOCTEST_CAT(anon, FUNCTOR) registrar; \ doctest::detail::ForEachType \ doIt(registrar); \ return 0; \ } \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = DOCTEST_CAT(anon, REG_FUNC)(); \ DOCTEST_GLOBAL_NO_WARNINGS_END() \ template \ inline void anon() #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE(decorators, T, ...) \ DOCTEST_TEST_CASE_TEMPLATE_IMPL(decorators, T, (__VA_ARGS__), \ DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE(decorators, T, types) \ DOCTEST_TEST_CASE_TEMPLATE_IMPL(decorators, T, types, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(decorators, T, id, anon) \ template \ inline void anon(); \ struct DOCTEST_CAT(id, _FUNCTOR) \ { \ int m_line; \ DOCTEST_CAT(id, _FUNCTOR) \ (int line) \ : m_line(line) {} \ template \ void operator()() { \ doctest::detail::regTest( \ doctest::detail::TestCase(anon, __FILE__, __LINE__, \ doctest_detail_test_suite_ns::getCurrentTestSuite(), \ doctest::detail::type_to_string(), \ m_line * 1000 + Index) * \ decorators); \ } \ }; \ template \ inline void anon() #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(decorators, T, id) \ DOCTEST_TEST_CASE_TEMPLATE_DEFINE_IMPL(decorators, T, id, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, types, anon) \ static int DOCTEST_CAT(anon, REG_FUNC)() { \ DOCTEST_CAT(id, _FUNCTOR) registrar(__LINE__); \ doctest::detail::ForEachType \ doIt(registrar); \ return 0; \ } \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_CAT(anon, DUMMY)) = DOCTEST_CAT(anon, REG_FUNC)(); \ DOCTEST_GLOBAL_NO_WARNINGS_END() typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, ...) \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, (__VA_ARGS__), \ DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, types) \ DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE_IMPL(id, types, DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // for subcases #define DOCTEST_SUBCASE(name) \ if(const doctest::detail::Subcase & DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUBCASE_) DOCTEST_UNUSED = \ doctest::detail::Subcase(name, __FILE__, __LINE__)) // for grouping tests in test suites by using code blocks #define DOCTEST_TEST_SUITE_IMPL(decorators, ns_name) \ namespace ns_name \ { \ namespace doctest_detail_test_suite_ns \ { \ static DOCTEST_NOINLINE doctest::detail::TestSuite& getCurrentTestSuite() { \ static doctest::detail::TestSuite data; \ static bool inited = false; \ if(!inited) { \ data* decorators; \ inited = true; \ } \ return data; \ } \ } \ } \ namespace ns_name #define DOCTEST_TEST_SUITE(decorators) \ DOCTEST_TEST_SUITE_IMPL(decorators, DOCTEST_ANONYMOUS(_DOCTEST_ANON_SUITE_)) // for starting a testsuite block #define DOCTEST_TEST_SUITE_BEGIN(decorators) \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ doctest::detail::setTestSuite(doctest::detail::TestSuite() * decorators); \ DOCTEST_GLOBAL_NO_WARNINGS_END() \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) // for ending a testsuite block #define DOCTEST_TEST_SUITE_END \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ doctest::detail::setTestSuite(doctest::detail::TestSuite() * ""); \ DOCTEST_GLOBAL_NO_WARNINGS_END() \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) // for registering exception translators #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(translatorName, signature) \ inline doctest::String translatorName(signature); \ DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)) = \ doctest::registerExceptionTranslator(translatorName); \ DOCTEST_GLOBAL_NO_WARNINGS_END() \ doctest::String translatorName(signature) #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ DOCTEST_REGISTER_EXCEPTION_TRANSLATOR_IMPL(DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_), \ signature) // for logging #define DOCTEST_INFO(x) \ doctest::detail::ContextScope DOCTEST_ANONYMOUS(_DOCTEST_CAPTURE_)( \ doctest::detail::ContextBuilder() << x) #define DOCTEST_CAPTURE(x) DOCTEST_INFO(#x " := " << x) #define DOCTEST_ADD_AT_IMPL(type, file, line, mb, x) \ do { \ doctest::detail::MessageBuilder mb(file, line, doctest::detail::assertType::type); \ mb << x; \ if(mb.log()) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ mb.react(); \ } while((void)0, 0) // clang-format off #define DOCTEST_ADD_MESSAGE_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_warn, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) #define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_check, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) #define DOCTEST_ADD_FAIL_AT(file, line, x) DOCTEST_ADD_AT_IMPL(is_require, file, line, DOCTEST_ANONYMOUS(_DOCTEST_MESSAGE_), x) // clang-format on #define DOCTEST_MESSAGE(x) DOCTEST_ADD_MESSAGE_AT(__FILE__, __LINE__, x) #define DOCTEST_FAIL_CHECK(x) DOCTEST_ADD_FAIL_CHECK_AT(__FILE__, __LINE__, x) #define DOCTEST_FAIL(x) DOCTEST_ADD_FAIL_AT(__FILE__, __LINE__, x) #if __cplusplus >= 201402L || (DOCTEST_MSVC >= DOCTEST_COMPILER(19, 10, 0)) template constexpr T to_lvalue = x; #define DOCTEST_TO_LVALUE(...) to_lvalue #else #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TO_LVALUE(...) TO_LVALUE_CAN_BE_USED_ONLY_IN_CPP14_MODE_OR_WITH_VS_2017_OR_NEWER #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TO_LVALUE(x) TO_LVALUE_CAN_BE_USED_ONLY_IN_CPP14_MODE_OR_WITH_VS_2017_OR_NEWER #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // TO_LVALUE hack for logging macros like INFO() // common code in asserts - for convenience #define DOCTEST_ASSERT_LOG_AND_REACT(rb) \ if(rb.log()) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ rb.react() #ifdef DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_WRAP_IN_TRY(x) x; #else // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_WRAP_IN_TRY(x) \ try { \ x; \ } catch(...) { _DOCTEST_RB.unexpectedExceptionOccurred(); } #endif // DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS #define DOCTEST_ASSERT_IMPLEMENT_2(expr, assert_type) \ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Woverloaded-shift-op-parentheses") \ doctest::detail::ResultBuilder _DOCTEST_RB( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr))); \ DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.setResult( \ doctest::detail::ExpressionDecomposer(doctest::detail::assertType::assert_type) \ << DOCTEST_HANDLE_BRACED_VA_ARGS(expr))) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB) \ DOCTEST_CLANG_SUPPRESS_WARNING_POP #define DOCTEST_ASSERT_IMPLEMENT_1(expr, assert_type) \ do { \ DOCTEST_ASSERT_IMPLEMENT_2(expr, assert_type); \ } while((void)0, 0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_WARN) #define DOCTEST_CHECK(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_CHECK) #define DOCTEST_REQUIRE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_REQUIRE) #define DOCTEST_WARN_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_WARN_FALSE) #define DOCTEST_CHECK_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_CHECK_FALSE) #define DOCTEST_REQUIRE_FALSE(...) DOCTEST_ASSERT_IMPLEMENT_1((__VA_ARGS__), DT_REQUIRE_FALSE) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_WARN) #define DOCTEST_CHECK(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_CHECK) #define DOCTEST_REQUIRE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_REQUIRE) #define DOCTEST_WARN_FALSE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_WARN_FALSE) #define DOCTEST_CHECK_FALSE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_CHECK_FALSE) #define DOCTEST_REQUIRE_FALSE(expr) DOCTEST_ASSERT_IMPLEMENT_1(expr, DT_REQUIRE_FALSE) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // clang-format off #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_WARN); } while((void)0, 0) #define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_CHECK); } while((void)0, 0) #define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_REQUIRE); } while((void)0, 0) #define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_WARN_FALSE); } while((void)0, 0) #define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_CHECK_FALSE); } while((void)0, 0) #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2((cond), DT_REQUIRE_FALSE); } while((void)0, 0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_WARN); } while((void)0, 0) #define DOCTEST_CHECK_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_CHECK); } while((void)0, 0) #define DOCTEST_REQUIRE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_REQUIRE); } while((void)0, 0) #define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_WARN_FALSE); } while((void)0, 0) #define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_CHECK_FALSE); } while((void)0, 0) #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) do { DOCTEST_INFO(msg); DOCTEST_ASSERT_IMPLEMENT_2(cond, DT_REQUIRE_FALSE); } while((void)0, 0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // clang-format on #define DOCTEST_ASSERT_THROWS(expr, assert_type) \ do { \ if(!doctest::detail::getTestsContextState()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ __FILE__, __LINE__, #expr); \ try { \ expr; \ } catch(...) { _DOCTEST_RB.m_threw = true; } \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } \ } while((void)0, 0) #define DOCTEST_ASSERT_THROWS_AS(expr, as, assert_type) \ do { \ if(!doctest::detail::getTestsContextState()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, #expr, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(as))); \ try { \ expr; \ } catch(const DOCTEST_HANDLE_BRACED_VA_ARGS(as)&) { \ _DOCTEST_RB.m_threw = true; \ _DOCTEST_RB.m_threw_as = true; \ } catch(...) { _DOCTEST_RB.unexpectedExceptionOccurred(); } \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } \ } while((void)0, 0) #define DOCTEST_ASSERT_NOTHROW(expr, assert_type) \ do { \ if(!doctest::detail::getTestsContextState()->no_throw) { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ __FILE__, __LINE__, #expr); \ try { \ expr; \ } catch(...) { _DOCTEST_RB.unexpectedExceptionOccurred(); } \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } \ } while((void)0, 0) #define DOCTEST_WARN_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_WARN_THROWS) #define DOCTEST_CHECK_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_CHECK_THROWS) #define DOCTEST_REQUIRE_THROWS(expr) DOCTEST_ASSERT_THROWS(expr, DT_REQUIRE_THROWS) // clang-format off #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, (__VA_ARGS__), DT_WARN_THROWS_AS) #define DOCTEST_CHECK_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, (__VA_ARGS__), DT_CHECK_THROWS_AS) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) DOCTEST_ASSERT_THROWS_AS(expr, (__VA_ARGS__), DT_REQUIRE_THROWS_AS) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_WARN_THROWS_AS) #define DOCTEST_CHECK_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_CHECK_THROWS_AS) #define DOCTEST_REQUIRE_THROWS_AS(expr, ex) DOCTEST_ASSERT_THROWS_AS(expr, ex, DT_REQUIRE_THROWS_AS) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // clang-format on #define DOCTEST_WARN_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_WARN_NOTHROW) #define DOCTEST_CHECK_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_CHECK_NOTHROW) #define DOCTEST_REQUIRE_NOTHROW(expr) DOCTEST_ASSERT_NOTHROW(expr, DT_REQUIRE_NOTHROW) // clang-format off #define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS(expr); } while((void)0, 0) #define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS(expr); } while((void)0, 0) #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS(expr); } while((void)0, 0) #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_THROWS_AS(expr, ex); } while((void)0, 0) #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_THROWS_AS(expr, ex); } while((void)0, 0) #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_THROWS_AS(expr, ex); } while((void)0, 0) #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_WARN_NOTHROW(expr); } while((void)0, 0) #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_CHECK_NOTHROW(expr); } while((void)0, 0) #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) do { DOCTEST_INFO(msg); DOCTEST_REQUIRE_NOTHROW(expr); } while((void)0, 0) // clang-format on #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_BINARY_ASSERT(assert_type, expr, comp) \ do { \ doctest::detail::ResultBuilder _DOCTEST_RB( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr))); \ DOCTEST_WRAP_IN_TRY( \ _DOCTEST_RB.binary_assert( \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr))) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } while((void)0, 0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_BINARY_ASSERT(assert_type, lhs, rhs, comp) \ do { \ doctest::detail::ResultBuilder _DOCTEST_RB(doctest::detail::assertType::assert_type, \ __FILE__, __LINE__, #lhs ", " #rhs); \ DOCTEST_WRAP_IN_TRY( \ _DOCTEST_RB.binary_assert(lhs, \ rhs)) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } while((void)0, 0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_UNARY_ASSERT(assert_type, expr) \ do { \ doctest::detail::ResultBuilder _DOCTEST_RB( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr))); \ DOCTEST_WRAP_IN_TRY(_DOCTEST_RB.unary_assert(DOCTEST_HANDLE_BRACED_VA_ARGS(expr))) \ DOCTEST_ASSERT_LOG_AND_REACT(_DOCTEST_RB); \ } while((void)0, 0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_EQ(...) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, (__VA_ARGS__), eq) #define DOCTEST_CHECK_EQ(...) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, (__VA_ARGS__), eq) #define DOCTEST_REQUIRE_EQ(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, (__VA_ARGS__), eq) #define DOCTEST_WARN_NE(...) DOCTEST_BINARY_ASSERT(DT_WARN_NE, (__VA_ARGS__), ne) #define DOCTEST_CHECK_NE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, (__VA_ARGS__), ne) #define DOCTEST_REQUIRE_NE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, (__VA_ARGS__), ne) #define DOCTEST_WARN_GT(...) DOCTEST_BINARY_ASSERT(DT_WARN_GT, (__VA_ARGS__), gt) #define DOCTEST_CHECK_GT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, (__VA_ARGS__), gt) #define DOCTEST_REQUIRE_GT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, (__VA_ARGS__), gt) #define DOCTEST_WARN_LT(...) DOCTEST_BINARY_ASSERT(DT_WARN_LT, (__VA_ARGS__), lt) #define DOCTEST_CHECK_LT(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, (__VA_ARGS__), lt) #define DOCTEST_REQUIRE_LT(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, (__VA_ARGS__), lt) #define DOCTEST_WARN_GE(...) DOCTEST_BINARY_ASSERT(DT_WARN_GE, (__VA_ARGS__), ge) #define DOCTEST_CHECK_GE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, (__VA_ARGS__), ge) #define DOCTEST_REQUIRE_GE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, (__VA_ARGS__), ge) #define DOCTEST_WARN_LE(...) DOCTEST_BINARY_ASSERT(DT_WARN_LE, (__VA_ARGS__), le) #define DOCTEST_CHECK_LE(...) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, (__VA_ARGS__), le) #define DOCTEST_REQUIRE_LE(...) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, (__VA_ARGS__), le) #define DOCTEST_WARN_UNARY(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, (__VA_ARGS__)) #define DOCTEST_CHECK_UNARY(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, (__VA_ARGS__)) #define DOCTEST_REQUIRE_UNARY(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, (__VA_ARGS__)) #define DOCTEST_WARN_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, (__VA_ARGS__)) #define DOCTEST_CHECK_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, (__VA_ARGS__)) #define DOCTEST_REQUIRE_UNARY_FALSE(...) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, (__VA_ARGS__)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_EQ, lhs, rhs, eq) #define DOCTEST_CHECK_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_EQ, lhs, rhs, eq) #define DOCTEST_REQUIRE_EQ(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_EQ, lhs, rhs, eq) #define DOCTEST_WARN_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_NE, lhs, rhs, ne) #define DOCTEST_CHECK_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_NE, lhs, rhs, ne) #define DOCTEST_REQUIRE_NE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_NE, lhs, rhs, ne) #define DOCTEST_WARN_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_GT, lhs, rhs, gt) #define DOCTEST_CHECK_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_GT, lhs, rhs, gt) #define DOCTEST_REQUIRE_GT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GT, lhs, rhs, gt) #define DOCTEST_WARN_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_LT, lhs, rhs, lt) #define DOCTEST_CHECK_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_LT, lhs, rhs, lt) #define DOCTEST_REQUIRE_LT(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LT, lhs, rhs, lt) #define DOCTEST_WARN_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_GE, lhs, rhs, ge) #define DOCTEST_CHECK_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_GE, lhs, rhs, ge) #define DOCTEST_REQUIRE_GE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_GE, lhs, rhs, ge) #define DOCTEST_WARN_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_WARN_LE, lhs, rhs, le) #define DOCTEST_CHECK_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_CHECK_LE, lhs, rhs, le) #define DOCTEST_REQUIRE_LE(lhs, rhs) DOCTEST_BINARY_ASSERT(DT_REQUIRE_LE, lhs, rhs, le) #define DOCTEST_WARN_UNARY(v) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY, v) #define DOCTEST_CHECK_UNARY(v) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY, v) #define DOCTEST_REQUIRE_UNARY(v) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY, v) #define DOCTEST_WARN_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_WARN_UNARY_FALSE, v) #define DOCTEST_CHECK_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_CHECK_UNARY_FALSE, v) #define DOCTEST_REQUIRE_UNARY_FALSE(v) DOCTEST_UNARY_ASSERT(DT_REQUIRE_UNARY_FALSE, v) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #ifndef DOCTEST_CONFIG_SUPER_FAST_ASSERTS #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_BINARY_ASSERT(assert_type, expr, comparison) \ do { \ int _DOCTEST_FAST_RES = doctest::detail::fast_binary_assert< \ doctest::detail::binaryAssertComparison::comparison>( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)), \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr)); \ if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES); \ } while((void)0, 0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_BINARY_ASSERT(assert_type, lhs, rhs, comparison) \ do { \ int _DOCTEST_FAST_RES = doctest::detail::fast_binary_assert< \ doctest::detail::binaryAssertComparison::comparison>( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, #lhs ", " #rhs, lhs, \ rhs); \ if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES); \ } while((void)0, 0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_UNARY_ASSERT(assert_type, expr) \ do { \ int _DOCTEST_FAST_RES = doctest::detail::fast_unary_assert( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)), \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr)); \ if(_DOCTEST_FAST_RES & doctest::detail::assertAction::dbgbreak) \ DOCTEST_BREAK_INTO_DEBUGGER(); \ doctest::detail::fastAssertThrowIfFlagSet(_DOCTEST_FAST_RES); \ } while((void)0, 0) #else // DOCTEST_CONFIG_SUPER_FAST_ASSERTS #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_BINARY_ASSERT(assert_type, expr, comparison) \ doctest::detail::fast_binary_assert( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)), \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_BINARY_ASSERT(assert_type, lhs, rhs, comparison) \ doctest::detail::fast_binary_assert( \ doctest::detail::assertType::assert_type, __FILE__, __LINE__, #lhs ", " #rhs, lhs, \ rhs) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_UNARY_ASSERT(assert_type, expr) \ doctest::detail::fast_unary_assert(doctest::detail::assertType::assert_type, __FILE__, \ __LINE__, \ DOCTEST_TOSTR(DOCTEST_HANDLE_BRACED_VA_ARGS(expr)), \ DOCTEST_HANDLE_BRACED_VA_ARGS(expr)) #endif // DOCTEST_CONFIG_SUPER_FAST_ASSERTS // clang-format off #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_WARN_EQ(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_EQ, (__VA_ARGS__), eq) #define DOCTEST_FAST_CHECK_EQ(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_EQ, (__VA_ARGS__), eq) #define DOCTEST_FAST_REQUIRE_EQ(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_EQ, (__VA_ARGS__), eq) #define DOCTEST_FAST_WARN_NE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_NE, (__VA_ARGS__), ne) #define DOCTEST_FAST_CHECK_NE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_NE, (__VA_ARGS__), ne) #define DOCTEST_FAST_REQUIRE_NE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_NE, (__VA_ARGS__), ne) #define DOCTEST_FAST_WARN_GT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GT, (__VA_ARGS__), gt) #define DOCTEST_FAST_CHECK_GT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GT, (__VA_ARGS__), gt) #define DOCTEST_FAST_REQUIRE_GT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GT, (__VA_ARGS__), gt) #define DOCTEST_FAST_WARN_LT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LT, (__VA_ARGS__), lt) #define DOCTEST_FAST_CHECK_LT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LT, (__VA_ARGS__), lt) #define DOCTEST_FAST_REQUIRE_LT(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LT, (__VA_ARGS__), lt) #define DOCTEST_FAST_WARN_GE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GE, (__VA_ARGS__), ge) #define DOCTEST_FAST_CHECK_GE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GE, (__VA_ARGS__), ge) #define DOCTEST_FAST_REQUIRE_GE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GE, (__VA_ARGS__), ge) #define DOCTEST_FAST_WARN_LE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LE, (__VA_ARGS__), le) #define DOCTEST_FAST_CHECK_LE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LE, (__VA_ARGS__), le) #define DOCTEST_FAST_REQUIRE_LE(...) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LE, (__VA_ARGS__), le) #define DOCTEST_FAST_WARN_UNARY(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY, (__VA_ARGS__)) #define DOCTEST_FAST_CHECK_UNARY(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY, (__VA_ARGS__)) #define DOCTEST_FAST_REQUIRE_UNARY(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY, (__VA_ARGS__)) #define DOCTEST_FAST_WARN_UNARY_FALSE(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY_FALSE, (__VA_ARGS__)) #define DOCTEST_FAST_CHECK_UNARY_FALSE(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY_FALSE, (__VA_ARGS__)) #define DOCTEST_FAST_REQUIRE_UNARY_FALSE(...) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY_FALSE, (__VA_ARGS__)) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_FAST_WARN_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_EQ, l, r, eq) #define DOCTEST_FAST_CHECK_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_EQ, l, r, eq) #define DOCTEST_FAST_REQUIRE_EQ(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_EQ, l, r, eq) #define DOCTEST_FAST_WARN_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_NE, l, r, ne) #define DOCTEST_FAST_CHECK_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_NE, l, r, ne) #define DOCTEST_FAST_REQUIRE_NE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_NE, l, r, ne) #define DOCTEST_FAST_WARN_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GT, l, r, gt) #define DOCTEST_FAST_CHECK_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GT, l, r, gt) #define DOCTEST_FAST_REQUIRE_GT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GT, l, r, gt) #define DOCTEST_FAST_WARN_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LT, l, r, lt) #define DOCTEST_FAST_CHECK_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LT, l, r, lt) #define DOCTEST_FAST_REQUIRE_LT(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LT, l, r, lt) #define DOCTEST_FAST_WARN_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_GE, l, r, ge) #define DOCTEST_FAST_CHECK_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_GE, l, r, ge) #define DOCTEST_FAST_REQUIRE_GE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_GE, l, r, ge) #define DOCTEST_FAST_WARN_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_WARN_LE, l, r, le) #define DOCTEST_FAST_CHECK_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_CHECK_LE, l, r, le) #define DOCTEST_FAST_REQUIRE_LE(l, r) DOCTEST_FAST_BINARY_ASSERT(DT_FAST_REQUIRE_LE, l, r, le) #define DOCTEST_FAST_WARN_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY, v) #define DOCTEST_FAST_CHECK_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY, v) #define DOCTEST_FAST_REQUIRE_UNARY(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY, v) #define DOCTEST_FAST_WARN_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_WARN_UNARY_FALSE, v) #define DOCTEST_FAST_CHECK_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_CHECK_UNARY_FALSE, v) #define DOCTEST_FAST_REQUIRE_UNARY_FALSE(v) DOCTEST_FAST_UNARY_ASSERT(DT_FAST_REQUIRE_UNARY_FALSE, v) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // clang-format on #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS #undef DOCTEST_WARN_THROWS #undef DOCTEST_CHECK_THROWS #undef DOCTEST_REQUIRE_THROWS #undef DOCTEST_WARN_THROWS_AS #undef DOCTEST_CHECK_THROWS_AS #undef DOCTEST_REQUIRE_THROWS_AS #undef DOCTEST_WARN_NOTHROW #undef DOCTEST_CHECK_NOTHROW #undef DOCTEST_REQUIRE_NOTHROW #undef DOCTEST_WARN_THROWS_MESSAGE #undef DOCTEST_CHECK_THROWS_MESSAGE #undef DOCTEST_REQUIRE_THROWS_MESSAGE #undef DOCTEST_WARN_THROWS_AS_MESSAGE #undef DOCTEST_CHECK_THROWS_AS_MESSAGE #undef DOCTEST_REQUIRE_THROWS_AS_MESSAGE #undef DOCTEST_WARN_NOTHROW_MESSAGE #undef DOCTEST_CHECK_NOTHROW_MESSAGE #undef DOCTEST_REQUIRE_NOTHROW_MESSAGE #ifdef DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #define DOCTEST_WARN_THROWS(expr) ((void)0) #define DOCTEST_CHECK_THROWS(expr) ((void)0) #define DOCTEST_REQUIRE_THROWS(expr) ((void)0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) #define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ex) ((void)0) #define DOCTEST_CHECK_THROWS_AS(expr, ex) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS(expr, ex) ((void)0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_NOTHROW(expr) ((void)0) #define DOCTEST_CHECK_NOTHROW(expr) ((void)0) #define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0) #define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) #else // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #undef DOCTEST_REQUIRE #undef DOCTEST_REQUIRE_FALSE #undef DOCTEST_REQUIRE_MESSAGE #undef DOCTEST_REQUIRE_FALSE_MESSAGE #undef DOCTEST_REQUIRE_EQ #undef DOCTEST_REQUIRE_NE #undef DOCTEST_REQUIRE_GT #undef DOCTEST_REQUIRE_LT #undef DOCTEST_REQUIRE_GE #undef DOCTEST_REQUIRE_LE #undef DOCTEST_REQUIRE_UNARY #undef DOCTEST_REQUIRE_UNARY_FALSE #undef DOCTEST_FAST_REQUIRE_EQ #undef DOCTEST_FAST_REQUIRE_NE #undef DOCTEST_FAST_REQUIRE_GT #undef DOCTEST_FAST_REQUIRE_LT #undef DOCTEST_FAST_REQUIRE_GE #undef DOCTEST_FAST_REQUIRE_LE #undef DOCTEST_FAST_REQUIRE_UNARY #undef DOCTEST_FAST_REQUIRE_UNARY_FALSE #endif // DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS #endif // DOCTEST_CONFIG_NO_EXCEPTIONS // ================================================================================================= // == WHAT FOLLOWS IS VERSIONS OF THE MACROS THAT DO NOT DO ANY REGISTERING! == // == THIS CAN BE ENABLED BY DEFINING DOCTEST_CONFIG_DISABLE GLOBALLY! == // ================================================================================================= #else // DOCTEST_CONFIG_DISABLE #define DOCTEST_IMPLEMENT_FIXTURE(der, base, func, name) \ namespace \ { \ template \ struct der : base \ { void f(); }; \ } \ template \ inline void der::f() #define DOCTEST_CREATE_AND_REGISTER_FUNCTION(f, name) \ template \ static inline void f() // for registering tests #define DOCTEST_TEST_CASE(name) \ DOCTEST_CREATE_AND_REGISTER_FUNCTION(DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) // for registering tests with a fixture #define DOCTEST_TEST_CASE_FIXTURE(x, name) \ DOCTEST_IMPLEMENT_FIXTURE(DOCTEST_ANONYMOUS(_DOCTEST_ANON_CLASS_), x, \ DOCTEST_ANONYMOUS(_DOCTEST_ANON_FUNC_), name) // for converting types to strings without the header and demangling #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TYPE_TO_STRING(...) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #define DOCTEST_TYPE_TO_STRING_IMPL(...) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_TYPE_TO_STRING(x) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #define DOCTEST_TYPE_TO_STRING_IMPL(x) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS // for typed tests #define DOCTEST_TEST_CASE_TEMPLATE(name, type, types) \ template \ inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() #define DOCTEST_TEST_CASE_TEMPLATE_DEFINE(name, type, id) \ template \ inline void DOCTEST_ANONYMOUS(_DOCTEST_ANON_TMP_)() #define DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE(id, types) \ typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) // for subcases #define DOCTEST_SUBCASE(name) // for a testsuite block #define DOCTEST_TEST_SUITE(name) namespace // for starting a testsuite block #define DOCTEST_TEST_SUITE_BEGIN(name) typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) // for ending a testsuite block #define DOCTEST_TEST_SUITE_END typedef int DOCTEST_ANONYMOUS(_DOCTEST_ANON_FOR_SEMICOLON_) #define DOCTEST_REGISTER_EXCEPTION_TRANSLATOR(signature) \ template \ static inline doctest::String DOCTEST_ANONYMOUS(_DOCTEST_ANON_TRANSLATOR_)(signature) #define DOCTEST_INFO(x) ((void)0) #define DOCTEST_CAPTURE(x) ((void)0) #define DOCTEST_ADD_MESSAGE_AT(file, line, x) ((void)0) #define DOCTEST_ADD_FAIL_CHECK_AT(file, line, x) ((void)0) #define DOCTEST_ADD_FAIL_AT(file, line, x) ((void)0) #define DOCTEST_MESSAGE(x) ((void)0) #define DOCTEST_FAIL_CHECK(x) ((void)0) #define DOCTEST_FAIL(x) ((void)0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN(...) ((void)0) #define DOCTEST_CHECK(...) ((void)0) #define DOCTEST_REQUIRE(...) ((void)0) #define DOCTEST_WARN_FALSE(...) ((void)0) #define DOCTEST_CHECK_FALSE(...) ((void)0) #define DOCTEST_REQUIRE_FALSE(...) ((void)0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN(expr) ((void)0) #define DOCTEST_CHECK(expr) ((void)0) #define DOCTEST_REQUIRE(expr) ((void)0) #define DOCTEST_WARN_FALSE(expr) ((void)0) #define DOCTEST_CHECK_FALSE(expr) ((void)0) #define DOCTEST_REQUIRE_FALSE(expr) ((void)0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_MESSAGE(cond, msg) ((void)0) #define DOCTEST_CHECK_MESSAGE(cond, msg) ((void)0) #define DOCTEST_REQUIRE_MESSAGE(cond, msg) ((void)0) #define DOCTEST_WARN_FALSE_MESSAGE(cond, msg) ((void)0) #define DOCTEST_CHECK_FALSE_MESSAGE(cond, msg) ((void)0) #define DOCTEST_REQUIRE_FALSE_MESSAGE(cond, msg) ((void)0) #define DOCTEST_WARN_THROWS(expr) ((void)0) #define DOCTEST_CHECK_THROWS(expr) ((void)0) #define DOCTEST_REQUIRE_THROWS(expr) ((void)0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ...) ((void)0) #define DOCTEST_CHECK_THROWS_AS(expr, ...) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS(expr, ...) ((void)0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_THROWS_AS(expr, ex) ((void)0) #define DOCTEST_CHECK_THROWS_AS(expr, ex) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS(expr, ex) ((void)0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_NOTHROW(expr) ((void)0) #define DOCTEST_CHECK_NOTHROW(expr) ((void)0) #define DOCTEST_REQUIRE_NOTHROW(expr) ((void)0) #define DOCTEST_WARN_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_CHECK_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_REQUIRE_THROWS_MESSAGE(expr, msg) ((void)0) #define DOCTEST_WARN_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_CHECK_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_REQUIRE_THROWS_AS_MESSAGE(expr, ex, msg) ((void)0) #define DOCTEST_WARN_NOTHROW_MESSAGE(expr, msg) ((void)0) #define DOCTEST_CHECK_NOTHROW_MESSAGE(expr, msg) ((void)0) #define DOCTEST_REQUIRE_NOTHROW_MESSAGE(expr, msg) ((void)0) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_EQ(...) ((void)0) #define DOCTEST_CHECK_EQ(...) ((void)0) #define DOCTEST_REQUIRE_EQ(...) ((void)0) #define DOCTEST_WARN_NE(...) ((void)0) #define DOCTEST_CHECK_NE(...) ((void)0) #define DOCTEST_REQUIRE_NE(...) ((void)0) #define DOCTEST_WARN_GT(...) ((void)0) #define DOCTEST_CHECK_GT(...) ((void)0) #define DOCTEST_REQUIRE_GT(...) ((void)0) #define DOCTEST_WARN_LT(...) ((void)0) #define DOCTEST_CHECK_LT(...) ((void)0) #define DOCTEST_REQUIRE_LT(...) ((void)0) #define DOCTEST_WARN_GE(...) ((void)0) #define DOCTEST_CHECK_GE(...) ((void)0) #define DOCTEST_REQUIRE_GE(...) ((void)0) #define DOCTEST_WARN_LE(...) ((void)0) #define DOCTEST_CHECK_LE(...) ((void)0) #define DOCTEST_REQUIRE_LE(...) ((void)0) #define DOCTEST_WARN_UNARY(...) ((void)0) #define DOCTEST_CHECK_UNARY(...) ((void)0) #define DOCTEST_REQUIRE_UNARY(...) ((void)0) #define DOCTEST_WARN_UNARY_FALSE(...) ((void)0) #define DOCTEST_CHECK_UNARY_FALSE(...) ((void)0) #define DOCTEST_REQUIRE_UNARY_FALSE(...) ((void)0) #define DOCTEST_FAST_WARN_EQ(...) ((void)0) #define DOCTEST_FAST_CHECK_EQ(...) ((void)0) #define DOCTEST_FAST_REQUIRE_EQ(...) ((void)0) #define DOCTEST_FAST_WARN_NE(...) ((void)0) #define DOCTEST_FAST_CHECK_NE(...) ((void)0) #define DOCTEST_FAST_REQUIRE_NE(...) ((void)0) #define DOCTEST_FAST_WARN_GT(...) ((void)0) #define DOCTEST_FAST_CHECK_GT(...) ((void)0) #define DOCTEST_FAST_REQUIRE_GT(...) ((void)0) #define DOCTEST_FAST_WARN_LT(...) ((void)0) #define DOCTEST_FAST_CHECK_LT(...) ((void)0) #define DOCTEST_FAST_REQUIRE_LT(...) ((void)0) #define DOCTEST_FAST_WARN_GE(...) ((void)0) #define DOCTEST_FAST_CHECK_GE(...) ((void)0) #define DOCTEST_FAST_REQUIRE_GE(...) ((void)0) #define DOCTEST_FAST_WARN_LE(...) ((void)0) #define DOCTEST_FAST_CHECK_LE(...) ((void)0) #define DOCTEST_FAST_REQUIRE_LE(...) ((void)0) #define DOCTEST_FAST_WARN_UNARY(...) ((void)0) #define DOCTEST_FAST_CHECK_UNARY(...) ((void)0) #define DOCTEST_FAST_REQUIRE_UNARY(...) ((void)0) #define DOCTEST_FAST_WARN_UNARY_FALSE(...) ((void)0) #define DOCTEST_FAST_CHECK_UNARY_FALSE(...) ((void)0) #define DOCTEST_FAST_REQUIRE_UNARY_FALSE(...) ((void)0) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_WARN_EQ(lhs, rhs) ((void)0) #define DOCTEST_CHECK_EQ(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_EQ(lhs, rhs) ((void)0) #define DOCTEST_WARN_NE(lhs, rhs) ((void)0) #define DOCTEST_CHECK_NE(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_NE(lhs, rhs) ((void)0) #define DOCTEST_WARN_GT(lhs, rhs) ((void)0) #define DOCTEST_CHECK_GT(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_GT(lhs, rhs) ((void)0) #define DOCTEST_WARN_LT(lhs, rhs) ((void)0) #define DOCTEST_CHECK_LT(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_LT(lhs, rhs) ((void)0) #define DOCTEST_WARN_GE(lhs, rhs) ((void)0) #define DOCTEST_CHECK_GE(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_GE(lhs, rhs) ((void)0) #define DOCTEST_WARN_LE(lhs, rhs) ((void)0) #define DOCTEST_CHECK_LE(lhs, rhs) ((void)0) #define DOCTEST_REQUIRE_LE(lhs, rhs) ((void)0) #define DOCTEST_WARN_UNARY(val) ((void)0) #define DOCTEST_CHECK_UNARY(val) ((void)0) #define DOCTEST_REQUIRE_UNARY(val) ((void)0) #define DOCTEST_WARN_UNARY_FALSE(val) ((void)0) #define DOCTEST_CHECK_UNARY_FALSE(val) ((void)0) #define DOCTEST_REQUIRE_UNARY_FALSE(val) ((void)0) #define DOCTEST_FAST_WARN_EQ(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_EQ(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_EQ(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_NE(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_NE(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_NE(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_GT(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_GT(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_GT(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_LT(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_LT(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_LT(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_GE(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_GE(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_GE(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_LE(lhs, rhs) ((void)0) #define DOCTEST_FAST_CHECK_LE(lhs, rhs) ((void)0) #define DOCTEST_FAST_REQUIRE_LE(lhs, rhs) ((void)0) #define DOCTEST_FAST_WARN_UNARY(val) ((void)0) #define DOCTEST_FAST_CHECK_UNARY(val) ((void)0) #define DOCTEST_FAST_REQUIRE_UNARY(val) ((void)0) #define DOCTEST_FAST_WARN_UNARY_FALSE(val) ((void)0) #define DOCTEST_FAST_CHECK_UNARY_FALSE(val) ((void)0) #define DOCTEST_FAST_REQUIRE_UNARY_FALSE(val) ((void)0) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #endif // DOCTEST_CONFIG_DISABLE // BDD style macros // clang-format off #define DOCTEST_SCENARIO(name) TEST_CASE(" Scenario: " name) #ifdef DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_SCENARIO_TEMPLATE(name, T, ...) TEST_CASE_TEMPLATE(" Scenario: " name, T, __VA_ARGS__) #else // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_SCENARIO_TEMPLATE(name, T, types) TEST_CASE_TEMPLATE(" Scenario: " name, T, types) #endif // DOCTEST_CONFIG_WITH_VARIADIC_MACROS #define DOCTEST_SCENARIO_TEMPLATE_DEFINE(name, T, id) DOCTEST_TEST_CASE_TEMPLATE_DEFINE(" Scenario: " name, T, id) #define DOCTEST_GIVEN(name) SUBCASE(" Given: " name) #define DOCTEST_WHEN(name) SUBCASE(" When: " name) #define DOCTEST_AND_WHEN(name) SUBCASE("And when: " name) #define DOCTEST_THEN(name) SUBCASE(" Then: " name) #define DOCTEST_AND_THEN(name) SUBCASE(" And: " name) // clang-format on // == SHORT VERSIONS OF THE MACROS #if !defined(DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES) #define TEST_CASE DOCTEST_TEST_CASE #define TEST_CASE_FIXTURE DOCTEST_TEST_CASE_FIXTURE #define TYPE_TO_STRING DOCTEST_TYPE_TO_STRING #define TEST_CASE_TEMPLATE DOCTEST_TEST_CASE_TEMPLATE #define TEST_CASE_TEMPLATE_DEFINE DOCTEST_TEST_CASE_TEMPLATE_DEFINE #define TEST_CASE_TEMPLATE_INSTANTIATE DOCTEST_TEST_CASE_TEMPLATE_INSTANTIATE #define SUBCASE DOCTEST_SUBCASE #define TEST_SUITE DOCTEST_TEST_SUITE #define TEST_SUITE_BEGIN DOCTEST_TEST_SUITE_BEGIN #define TEST_SUITE_END DOCTEST_TEST_SUITE_END #define REGISTER_EXCEPTION_TRANSLATOR DOCTEST_REGISTER_EXCEPTION_TRANSLATOR #define INFO DOCTEST_INFO #define CAPTURE DOCTEST_CAPTURE #define ADD_MESSAGE_AT DOCTEST_ADD_MESSAGE_AT #define ADD_FAIL_CHECK_AT DOCTEST_ADD_FAIL_CHECK_AT #define ADD_FAIL_AT DOCTEST_ADD_FAIL_AT #define MESSAGE DOCTEST_MESSAGE #define FAIL_CHECK DOCTEST_FAIL_CHECK #define FAIL DOCTEST_FAIL #define TO_LVALUE DOCTEST_TO_LVALUE #define WARN DOCTEST_WARN #define WARN_FALSE DOCTEST_WARN_FALSE #define WARN_THROWS DOCTEST_WARN_THROWS #define WARN_THROWS_AS DOCTEST_WARN_THROWS_AS #define WARN_NOTHROW DOCTEST_WARN_NOTHROW #define CHECK DOCTEST_CHECK #define CHECK_FALSE DOCTEST_CHECK_FALSE #define CHECK_THROWS DOCTEST_CHECK_THROWS #define CHECK_THROWS_AS DOCTEST_CHECK_THROWS_AS #define CHECK_NOTHROW DOCTEST_CHECK_NOTHROW #define REQUIRE DOCTEST_REQUIRE #define REQUIRE_FALSE DOCTEST_REQUIRE_FALSE #define REQUIRE_THROWS DOCTEST_REQUIRE_THROWS #define REQUIRE_THROWS_AS DOCTEST_REQUIRE_THROWS_AS #define REQUIRE_NOTHROW DOCTEST_REQUIRE_NOTHROW #define WARN_MESSAGE DOCTEST_WARN_MESSAGE #define WARN_FALSE_MESSAGE DOCTEST_WARN_FALSE_MESSAGE #define WARN_THROWS_MESSAGE DOCTEST_WARN_THROWS_MESSAGE #define WARN_THROWS_AS_MESSAGE DOCTEST_WARN_THROWS_AS_MESSAGE #define WARN_NOTHROW_MESSAGE DOCTEST_WARN_NOTHROW_MESSAGE #define CHECK_MESSAGE DOCTEST_CHECK_MESSAGE #define CHECK_FALSE_MESSAGE DOCTEST_CHECK_FALSE_MESSAGE #define CHECK_THROWS_MESSAGE DOCTEST_CHECK_THROWS_MESSAGE #define CHECK_THROWS_AS_MESSAGE DOCTEST_CHECK_THROWS_AS_MESSAGE #define CHECK_NOTHROW_MESSAGE DOCTEST_CHECK_NOTHROW_MESSAGE #define REQUIRE_MESSAGE DOCTEST_REQUIRE_MESSAGE #define REQUIRE_FALSE_MESSAGE DOCTEST_REQUIRE_FALSE_MESSAGE #define REQUIRE_THROWS_MESSAGE DOCTEST_REQUIRE_THROWS_MESSAGE #define REQUIRE_THROWS_AS_MESSAGE DOCTEST_REQUIRE_THROWS_AS_MESSAGE #define REQUIRE_NOTHROW_MESSAGE DOCTEST_REQUIRE_NOTHROW_MESSAGE #define SCENARIO DOCTEST_SCENARIO #define SCENARIO_TEMPLATE DOCTEST_SCENARIO_TEMPLATE #define SCENARIO_TEMPLATE_DEFINE DOCTEST_SCENARIO_TEMPLATE_DEFINE #define GIVEN DOCTEST_GIVEN #define WHEN DOCTEST_WHEN #define AND_WHEN DOCTEST_AND_WHEN #define THEN DOCTEST_THEN #define AND_THEN DOCTEST_AND_THEN #define WARN_EQ DOCTEST_WARN_EQ #define CHECK_EQ DOCTEST_CHECK_EQ #define REQUIRE_EQ DOCTEST_REQUIRE_EQ #define WARN_NE DOCTEST_WARN_NE #define CHECK_NE DOCTEST_CHECK_NE #define REQUIRE_NE DOCTEST_REQUIRE_NE #define WARN_GT DOCTEST_WARN_GT #define CHECK_GT DOCTEST_CHECK_GT #define REQUIRE_GT DOCTEST_REQUIRE_GT #define WARN_LT DOCTEST_WARN_LT #define CHECK_LT DOCTEST_CHECK_LT #define REQUIRE_LT DOCTEST_REQUIRE_LT #define WARN_GE DOCTEST_WARN_GE #define CHECK_GE DOCTEST_CHECK_GE #define REQUIRE_GE DOCTEST_REQUIRE_GE #define WARN_LE DOCTEST_WARN_LE #define CHECK_LE DOCTEST_CHECK_LE #define REQUIRE_LE DOCTEST_REQUIRE_LE #define WARN_UNARY DOCTEST_WARN_UNARY #define CHECK_UNARY DOCTEST_CHECK_UNARY #define REQUIRE_UNARY DOCTEST_REQUIRE_UNARY #define WARN_UNARY_FALSE DOCTEST_WARN_UNARY_FALSE #define CHECK_UNARY_FALSE DOCTEST_CHECK_UNARY_FALSE #define REQUIRE_UNARY_FALSE DOCTEST_REQUIRE_UNARY_FALSE #define FAST_WARN_EQ DOCTEST_FAST_WARN_EQ #define FAST_CHECK_EQ DOCTEST_FAST_CHECK_EQ #define FAST_REQUIRE_EQ DOCTEST_FAST_REQUIRE_EQ #define FAST_WARN_NE DOCTEST_FAST_WARN_NE #define FAST_CHECK_NE DOCTEST_FAST_CHECK_NE #define FAST_REQUIRE_NE DOCTEST_FAST_REQUIRE_NE #define FAST_WARN_GT DOCTEST_FAST_WARN_GT #define FAST_CHECK_GT DOCTEST_FAST_CHECK_GT #define FAST_REQUIRE_GT DOCTEST_FAST_REQUIRE_GT #define FAST_WARN_LT DOCTEST_FAST_WARN_LT #define FAST_CHECK_LT DOCTEST_FAST_CHECK_LT #define FAST_REQUIRE_LT DOCTEST_FAST_REQUIRE_LT #define FAST_WARN_GE DOCTEST_FAST_WARN_GE #define FAST_CHECK_GE DOCTEST_FAST_CHECK_GE #define FAST_REQUIRE_GE DOCTEST_FAST_REQUIRE_GE #define FAST_WARN_LE DOCTEST_FAST_WARN_LE #define FAST_CHECK_LE DOCTEST_FAST_CHECK_LE #define FAST_REQUIRE_LE DOCTEST_FAST_REQUIRE_LE #define FAST_WARN_UNARY DOCTEST_FAST_WARN_UNARY #define FAST_CHECK_UNARY DOCTEST_FAST_CHECK_UNARY #define FAST_REQUIRE_UNARY DOCTEST_FAST_REQUIRE_UNARY #define FAST_WARN_UNARY_FALSE DOCTEST_FAST_WARN_UNARY_FALSE #define FAST_CHECK_UNARY_FALSE DOCTEST_FAST_CHECK_UNARY_FALSE #define FAST_REQUIRE_UNARY_FALSE DOCTEST_FAST_REQUIRE_UNARY_FALSE #endif // DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES // this is here to clear the 'current test suite' for the current translation unit - at the top DOCTEST_TEST_SUITE_END(); // add stringification for primitive/fundamental types namespace doctest { namespace detail { DOCTEST_TYPE_TO_STRING_IMPL(bool) DOCTEST_TYPE_TO_STRING_IMPL(float) DOCTEST_TYPE_TO_STRING_IMPL(double) DOCTEST_TYPE_TO_STRING_IMPL(long double) DOCTEST_TYPE_TO_STRING_IMPL(char) DOCTEST_TYPE_TO_STRING_IMPL(signed char) DOCTEST_TYPE_TO_STRING_IMPL(unsigned char) DOCTEST_TYPE_TO_STRING_IMPL(wchar_t) DOCTEST_TYPE_TO_STRING_IMPL(short int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned short int) DOCTEST_TYPE_TO_STRING_IMPL(int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned int) DOCTEST_TYPE_TO_STRING_IMPL(long int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned long int) #ifdef DOCTEST_CONFIG_WITH_LONG_LONG DOCTEST_TYPE_TO_STRING_IMPL(long long int) DOCTEST_TYPE_TO_STRING_IMPL(unsigned long long int) #endif // DOCTEST_CONFIG_WITH_LONG_LONG } // namespace detail } // namespace doctest DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // DOCTEST_LIBRARY_INCLUDED #ifndef DOCTEST_SINGLE_HEADER #define DOCTEST_SINGLE_HEADER #endif // DOCTEST_SINGLE_HEADER #if defined(DOCTEST_CONFIG_IMPLEMENT) || !defined(DOCTEST_SINGLE_HEADER) #ifndef DOCTEST_LIBRARY_IMPLEMENTATION #define DOCTEST_LIBRARY_IMPLEMENTATION #ifndef DOCTEST_SINGLE_HEADER #include "doctest_fwd.h" #endif // DOCTEST_SINGLE_HEADER DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") DOCTEST_CLANG_SUPPRESS_WARNING("-Wglobal-constructors") DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") DOCTEST_CLANG_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wshorten-64-to-32") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-variable-declarations") DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch") DOCTEST_CLANG_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_CLANG_SUPPRESS_WARNING("-Wcovered-switch-default") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-noreturn") DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") DOCTEST_CLANG_SUPPRESS_WARNING("-Wdisabled-macro-expansion") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-braces") DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-field-initializers") DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++11-long-long") #if DOCTEST_CLANG && DOCTEST_CLANG_HAS_WARNING("-Wzero-as-null-pointer-constant") DOCTEST_CLANG_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant") #endif // clang - 0 as null DOCTEST_GCC_SUPPRESS_WARNING_PUSH DOCTEST_GCC_SUPPRESS_WARNING("-Wunknown-pragmas") DOCTEST_GCC_SUPPRESS_WARNING("-Wpragmas") DOCTEST_GCC_SUPPRESS_WARNING("-Wconversion") DOCTEST_GCC_SUPPRESS_WARNING("-Weffc++") DOCTEST_GCC_SUPPRESS_WARNING("-Wsign-conversion") DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-field-initializers") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-braces") DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") DOCTEST_GCC_SUPPRESS_WARNING("-Winline") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-enum") DOCTEST_GCC_SUPPRESS_WARNING("-Wswitch-default") DOCTEST_GCC_SUPPRESS_WARNING("-Wunsafe-loop-optimizations") DOCTEST_GCC_SUPPRESS_WARNING("-Wlong-long") DOCTEST_GCC_SUPPRESS_WARNING("-Wold-style-cast") DOCTEST_GCC_SUPPRESS_WARNING("-Wzero-as-null-pointer-constant") DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") DOCTEST_MSVC_SUPPRESS_WARNING_PUSH DOCTEST_MSVC_SUPPRESS_WARNING(4616) // invalid compiler warning DOCTEST_MSVC_SUPPRESS_WARNING(4619) // invalid compiler warning DOCTEST_MSVC_SUPPRESS_WARNING(4996) // The compiler encountered a deprecated declaration DOCTEST_MSVC_SUPPRESS_WARNING(4267) // 'var' : conversion from 'x' to 'y', possible loss of data DOCTEST_MSVC_SUPPRESS_WARNING(4706) // assignment within conditional expression DOCTEST_MSVC_SUPPRESS_WARNING(4512) // 'class' : assignment operator could not be generated DOCTEST_MSVC_SUPPRESS_WARNING(4127) // conditional expression is constant DOCTEST_MSVC_SUPPRESS_WARNING(4530) // C++ exception handler used, but unwind semantics not enabled DOCTEST_MSVC_SUPPRESS_WARNING(4577) // 'noexcept' used with no exception handling mode specified DOCTEST_MSVC_SUPPRESS_WARNING(4774) // format string expected in argument is not a string literal DOCTEST_MSVC_SUPPRESS_WARNING(4365) // conversion from 'int' to 'unsigned', signed/unsigned mismatch DOCTEST_MSVC_SUPPRESS_WARNING(4820) // padding in structs DOCTEST_MSVC_SUPPRESS_WARNING(4640) // construction of local static object is not thread-safe DOCTEST_MSVC_SUPPRESS_WARNING(5039) // pointer to potentially throwing function passed to extern C DOCTEST_MSVC_SUPPRESS_WARNING(5045) // Spectre mitigation stuff #if defined(DOCTEST_NO_CPP11_COMPAT) DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") #endif // DOCTEST_NO_CPP11_COMPAT #define DOCTEST_LOG_START(s) \ do { \ if(!contextState->hasLoggedCurrentTestStart) { \ logTestStart(s, *contextState->currentTest); \ DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; \ logTestStart(oss, *contextState->currentTest); \ DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; \ contextState->hasLoggedCurrentTestStart = true; \ } \ } while(false) #define DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN \ if(isDebuggerActive()) { \ ContextState* p_cs = contextState; \ bool with_col = p_cs->no_colors; \ p_cs->no_colors = false; \ std::ostringstream oss #define DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END \ printToDebugConsole(oss.str().c_str()); \ p_cs->no_colors = with_col; \ } \ ((void)0) DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN // required includes - will go only in one translation unit! #include #include // borland (Embarcadero) compiler requires math.h and not cmath - https://github.com/onqtam/doctest/pull/37 #ifdef __BORLANDC__ #include #endif // __BORLANDC__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !DOCTEST_MSVC #include #endif // !MSVC DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END namespace doctest { namespace detail { // case insensitive strcmp int stricmp(char const* a, char const* b) { for(;; a++, b++) { const int d = tolower(*a) - tolower(*b); if(d != 0 || !*a) return d; } } void my_memcpy(void* dest, const void* src, unsigned num) { memcpy(dest, src, num); } template String fpToString(T value, int precision) { std::ostringstream oss; oss << std::setprecision(precision) << std::fixed << value; std::string d = oss.str(); size_t i = d.find_last_not_of('0'); if(i != std::string::npos && i != d.size() - 1) { if(d[i] == '.') i++; d = d.substr(0, i + 1); } return d.c_str(); } struct Endianness { enum Arch { Big, Little }; static Arch which() { union _ { int asInt; char asChar[sizeof(int)]; } u; u.asInt = 1; // NOLINT return (u.asChar[sizeof(int) - 1] == 1) ? Big : Little; // NOLINT } }; String rawMemoryToString(const void* object, unsigned size) { // Reverse order for little endian architectures int i = 0, end = static_cast(size), inc = 1; if(Endianness::which() == Endianness::Little) { i = end - 1; end = inc = -1; } unsigned char const* bytes = static_cast(object); std::ostringstream oss; oss << "0x" << std::setfill('0') << std::hex; for(; i != end; i += inc) oss << std::setw(2) << static_cast(bytes[i]); return oss.str().c_str(); } std::ostream* createStream() { return new std::ostringstream(); } String getStreamResult(std::ostream* s) { return static_cast(s)->str().c_str(); // NOLINT } void freeStream(std::ostream* s) { delete s; } #ifndef DOCTEST_CONFIG_DISABLE // this holds both parameters for the command line and runtime data for tests struct ContextState : TestAccessibleContextState //!OCLINT too many fields { // == parameters from the command line std::vector > filters; String order_by; // how tests should be ordered unsigned rand_seed; // the seed for rand ordering unsigned first; // the first (matching) test to be executed unsigned last; // the last (matching) test to be executed int abort_after; // stop tests after this many failed assertions int subcase_filter_levels; // apply the subcase filters for the first N levels bool case_sensitive; // if filtering should be case sensitive bool exit; // if the program should be exited after the tests are ran/whatever bool duration; // print the time duration of each test case bool no_exitcode; // if the framework should return 0 as the exitcode bool no_run; // to not run the tests at all (can be done with an "*" exclude) bool no_version; // to not print the version of the framework bool no_colors; // if output to the console should be colorized bool force_colors; // forces the use of colors even when a tty cannot be detected bool no_breaks; // to not break into the debugger bool no_skip; // don't skip test cases which are marked to be skipped bool gnu_file_line; // if line numbers should be surrounded with :x: and not (x): bool no_path_in_filenames; // if the path to files should be removed from the output bool no_line_numbers; // if source code line numbers should be omitted from the output bool no_skipped_summary; // don't print "skipped" in the summary !!! UNDOCUMENTED !!! bool help; // to print the help bool version; // to print the version bool count; // if only the count of matching tests is to be retreived bool list_test_cases; // to list all tests matching the filters bool list_test_suites; // to list all suites matching the filters // == data for the tests being ran unsigned numTestsPassingFilters; unsigned numTestSuitesPassingFilters; unsigned numFailed; const TestCase* currentTest; bool hasLoggedCurrentTestStart; int numAssertionsForCurrentTestcase; int numAssertions; int numFailedAssertionsForCurrentTestcase; int numFailedAssertions; bool hasCurrentTestFailed; std::vector contexts; // for logging with INFO() and friends std::vector exceptionalContexts; // logging from INFO() due to an exception // stuff for subcases std::set subcasesPassed; std::set subcasesEnteredLevels; std::vector subcasesStack; int subcasesCurrentLevel; bool subcasesHasSkipped; void resetRunData() { numTestsPassingFilters = 0; numTestSuitesPassingFilters = 0; numFailed = 0; numAssertions = 0; numFailedAssertions = 0; numFailedAssertionsForCurrentTestcase = 0; } // cppcheck-suppress uninitMemberVar ContextState() : filters(8) // 8 different filters total { resetRunData(); } }; ContextState* contextState = 0; #endif // DOCTEST_CONFIG_DISABLE } // namespace detail void String::copy(const String& other) { if(other.isOnStack()) { memcpy(buf, other.buf, len); } else { setOnHeap(); data.size = other.data.size; data.capacity = data.size + 1; data.ptr = new char[data.capacity]; memcpy(data.ptr, other.data.ptr, data.size + 1); } } String::String(const char* in) { unsigned in_len = strlen(in); if(in_len <= last) { memcpy(buf, in, in_len + 1); setLast(last - in_len); } else { setOnHeap(); data.size = in_len; data.capacity = data.size + 1; data.ptr = new char[data.capacity]; memcpy(data.ptr, in, in_len + 1); } } String& String::operator+=(const String& other) { const unsigned my_old_size = size(); const unsigned other_size = other.size(); const unsigned total_size = my_old_size + other_size; if(isOnStack()) { if(total_size < len) { // append to the current stack space memcpy(buf + my_old_size, other.c_str(), other_size + 1); setLast(last - total_size); } else { // alloc new chunk char* temp = new char[total_size + 1]; // copy current data to new location before writing in the union memcpy(temp, buf, my_old_size); // skip the +1 ('\0') for speed // update data in union setOnHeap(); data.size = total_size; data.capacity = data.size + 1; data.ptr = temp; // transfer the rest of the data memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } } else { if(data.capacity > total_size) { // append to the current heap block data.size = total_size; memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } else { // resize data.capacity *= 2; if(data.capacity <= total_size) data.capacity = total_size + 1; // alloc new chunk char* temp = new char[data.capacity]; // copy current data to new location before releasing it memcpy(temp, data.ptr, my_old_size); // skip the +1 ('\0') for speed // release old chunk delete[] data.ptr; // update the rest of the union members data.size = total_size; data.ptr = temp; // transfer the rest of the data memcpy(data.ptr + my_old_size, other.c_str(), other_size + 1); } } return *this; } #ifdef DOCTEST_CONFIG_WITH_RVALUE_REFERENCES String::String(String&& other) { memcpy(buf, other.buf, len); other.buf[0] = '\0'; other.setLast(); } String& String::operator=(String&& other) { if(this != &other) { if(!isOnStack()) delete[] data.ptr; memcpy(buf, other.buf, len); other.buf[0] = '\0'; other.setLast(); } return *this; } #endif // DOCTEST_CONFIG_WITH_RVALUE_REFERENCES int String::compare(const char* other, bool no_case) const { if(no_case) return detail::stricmp(c_str(), other); return std::strcmp(c_str(), other); } int String::compare(const String& other, bool no_case) const { return compare(other.c_str(), no_case); } std::ostream& operator<<(std::ostream& s, const String& in) { return s << in.c_str(); } Approx::Approx(double value) : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100) , m_scale(1.0) , m_value(value) {} bool operator==(double lhs, Approx const& rhs) { // Thanks to Richard Harris for his help refining this formula return std::fabs(lhs - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + std::max(std::fabs(lhs), std::fabs(rhs.m_value))); } String Approx::toString() const { return String("Approx( ") + doctest::toString(m_value) + " )"; } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING String toString(char* in) { return toString(static_cast(in)); } String toString(const char* in) { return String("\"") + (in ? in : "{null string}") + "\""; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING String toString(bool in) { return in ? "true" : "false"; } String toString(float in) { return detail::fpToString(in, 5) + "f"; } String toString(double in) { return detail::fpToString(in, 10); } String toString(double long in) { return detail::fpToString(in, 15); } String toString(char in) { char buf[64]; std::sprintf(buf, "%d", in); return buf; } String toString(char signed in) { char buf[64]; std::sprintf(buf, "%d", in); return buf; } String toString(char unsigned in) { char buf[64]; std::sprintf(buf, "%ud", in); return buf; } String toString(int short in) { char buf[64]; std::sprintf(buf, "%d", in); return buf; } String toString(int short unsigned in) { char buf[64]; std::sprintf(buf, "%u", in); return buf; } String toString(int in) { char buf[64]; std::sprintf(buf, "%d", in); return buf; } String toString(int unsigned in) { char buf[64]; std::sprintf(buf, "%u", in); return buf; } String toString(int long in) { char buf[64]; std::sprintf(buf, "%ld", in); return buf; } String toString(int long unsigned in) { char buf[64]; std::sprintf(buf, "%lu", in); return buf; } #ifdef DOCTEST_CONFIG_WITH_LONG_LONG String toString(int long long in) { char buf[64]; std::sprintf(buf, "%lld", in); return buf; } String toString(int long long unsigned in) { char buf[64]; std::sprintf(buf, "%llu", in); return buf; } #endif // DOCTEST_CONFIG_WITH_LONG_LONG #ifdef DOCTEST_CONFIG_WITH_NULLPTR String toString(std::nullptr_t) { return "nullptr"; } #endif // DOCTEST_CONFIG_WITH_NULLPTR } // namespace doctest #ifdef DOCTEST_CONFIG_DISABLE namespace doctest { bool isRunningInTest() { return false; } Context::Context(int, const char* const*) {} Context::~Context() {} void Context::applyCommandLine(int, const char* const*) {} void Context::addFilter(const char*, const char*) {} void Context::clearFilters() {} void Context::setOption(const char*, int) {} void Context::setOption(const char*, const char*) {} bool Context::shouldExit() { return false; } int Context::run() { return 0; } } // namespace doctest #else // DOCTEST_CONFIG_DISABLE #if !defined(DOCTEST_CONFIG_COLORS_NONE) #if !defined(DOCTEST_CONFIG_COLORS_WINDOWS) && !defined(DOCTEST_CONFIG_COLORS_ANSI) #ifdef DOCTEST_PLATFORM_WINDOWS #define DOCTEST_CONFIG_COLORS_WINDOWS #else // linux #define DOCTEST_CONFIG_COLORS_ANSI #endif // platform #endif // DOCTEST_CONFIG_COLORS_WINDOWS && DOCTEST_CONFIG_COLORS_ANSI #endif // DOCTEST_CONFIG_COLORS_NONE #if DOCTEST_MSVC || defined(__MINGW32__) #if DOCTEST_MSVC >= DOCTEST_COMPILER(17, 0, 0) #define DOCTEST_WINDOWS_SAL_IN_OPT _In_opt_ #else // MSVC #define DOCTEST_WINDOWS_SAL_IN_OPT #endif // MSVC extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( DOCTEST_WINDOWS_SAL_IN_OPT const char*); extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); #endif // MSVC || __MINGW32__ #ifdef DOCTEST_CONFIG_COLORS_ANSI #include #endif // DOCTEST_CONFIG_COLORS_ANSI #ifdef DOCTEST_PLATFORM_WINDOWS // defines for a leaner windows.h #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif // WIN32_LEAN_AND_MEAN #ifndef VC_EXTRA_LEAN #define VC_EXTRA_LEAN #endif // VC_EXTRA_LEAN #ifndef NOMINMAX #define NOMINMAX #endif // NOMINMAX DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN // not sure what AfxWin.h is for - here I do what Catch does #ifdef __AFXDLL #include #else #include #endif #include DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END #else // DOCTEST_PLATFORM_WINDOWS #include #endif // DOCTEST_PLATFORM_WINDOWS namespace doctest_detail_test_suite_ns { // holds the current test suite doctest::detail::TestSuite& getCurrentTestSuite() { static doctest::detail::TestSuite data; return data; } } // namespace doctest_detail_test_suite_ns namespace doctest { namespace detail { TestCase::TestCase(funcType test, const char* file, unsigned line, const TestSuite& test_suite, const char* type, int template_id) : m_test(test) , m_name(0) , m_type(type) , m_test_suite(test_suite.m_test_suite) , m_description(test_suite.m_description) , m_skip(test_suite.m_skip) , m_may_fail(test_suite.m_may_fail) , m_should_fail(test_suite.m_should_fail) , m_expected_failures(test_suite.m_expected_failures) , m_timeout(test_suite.m_timeout) , m_file(file) , m_line(line) , m_template_id(template_id) {} TestCase& TestCase::operator*(const char* in) { m_name = in; // make a new name with an appended type for templated test case if(m_template_id != -1) { m_full_name = String(m_name) + m_type; // redirect the name to point to the newly constructed full name m_name = m_full_name.c_str(); } return *this; } TestCase& TestCase::operator=(const TestCase& other) { m_test = other.m_test; m_full_name = other.m_full_name; m_name = other.m_name; m_type = other.m_type; m_test_suite = other.m_test_suite; m_description = other.m_description; m_skip = other.m_skip; m_may_fail = other.m_may_fail; m_should_fail = other.m_should_fail; m_expected_failures = other.m_expected_failures; m_timeout = other.m_timeout; m_file = other.m_file; m_line = other.m_line; m_template_id = other.m_template_id; if(m_template_id != -1) m_name = m_full_name.c_str(); return *this; } bool TestCase::operator<(const TestCase& other) const { if(m_line != other.m_line) return m_line < other.m_line; const int file_cmp = std::strcmp(m_file, other.m_file); if(file_cmp != 0) return file_cmp < 0; return m_template_id < other.m_template_id; } const char* assertString(assertType::Enum val) { DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH( 4062) // enumerator 'x' in switch of enum 'y' is not handled switch(val) { //!OCLINT missing default in switch statements // clang-format off case assertType::DT_WARN : return "WARN"; case assertType::DT_CHECK : return "CHECK"; case assertType::DT_REQUIRE : return "REQUIRE"; case assertType::DT_WARN_FALSE : return "WARN_FALSE"; case assertType::DT_CHECK_FALSE : return "CHECK_FALSE"; case assertType::DT_REQUIRE_FALSE : return "REQUIRE_FALSE"; case assertType::DT_WARN_THROWS : return "WARN_THROWS"; case assertType::DT_CHECK_THROWS : return "CHECK_THROWS"; case assertType::DT_REQUIRE_THROWS : return "REQUIRE_THROWS"; case assertType::DT_WARN_THROWS_AS : return "WARN_THROWS_AS"; case assertType::DT_CHECK_THROWS_AS : return "CHECK_THROWS_AS"; case assertType::DT_REQUIRE_THROWS_AS : return "REQUIRE_THROWS_AS"; case assertType::DT_WARN_NOTHROW : return "WARN_NOTHROW"; case assertType::DT_CHECK_NOTHROW : return "CHECK_NOTHROW"; case assertType::DT_REQUIRE_NOTHROW : return "REQUIRE_NOTHROW"; case assertType::DT_WARN_EQ : return "WARN_EQ"; case assertType::DT_CHECK_EQ : return "CHECK_EQ"; case assertType::DT_REQUIRE_EQ : return "REQUIRE_EQ"; case assertType::DT_WARN_NE : return "WARN_NE"; case assertType::DT_CHECK_NE : return "CHECK_NE"; case assertType::DT_REQUIRE_NE : return "REQUIRE_NE"; case assertType::DT_WARN_GT : return "WARN_GT"; case assertType::DT_CHECK_GT : return "CHECK_GT"; case assertType::DT_REQUIRE_GT : return "REQUIRE_GT"; case assertType::DT_WARN_LT : return "WARN_LT"; case assertType::DT_CHECK_LT : return "CHECK_LT"; case assertType::DT_REQUIRE_LT : return "REQUIRE_LT"; case assertType::DT_WARN_GE : return "WARN_GE"; case assertType::DT_CHECK_GE : return "CHECK_GE"; case assertType::DT_REQUIRE_GE : return "REQUIRE_GE"; case assertType::DT_WARN_LE : return "WARN_LE"; case assertType::DT_CHECK_LE : return "CHECK_LE"; case assertType::DT_REQUIRE_LE : return "REQUIRE_LE"; case assertType::DT_WARN_UNARY : return "WARN_UNARY"; case assertType::DT_CHECK_UNARY : return "CHECK_UNARY"; case assertType::DT_REQUIRE_UNARY : return "REQUIRE_UNARY"; case assertType::DT_WARN_UNARY_FALSE : return "WARN_UNARY_FALSE"; case assertType::DT_CHECK_UNARY_FALSE : return "CHECK_UNARY_FALSE"; case assertType::DT_REQUIRE_UNARY_FALSE : return "REQUIRE_UNARY_FALSE"; case assertType::DT_FAST_WARN_EQ : return "FAST_WARN_EQ"; case assertType::DT_FAST_CHECK_EQ : return "FAST_CHECK_EQ"; case assertType::DT_FAST_REQUIRE_EQ : return "FAST_REQUIRE_EQ"; case assertType::DT_FAST_WARN_NE : return "FAST_WARN_NE"; case assertType::DT_FAST_CHECK_NE : return "FAST_CHECK_NE"; case assertType::DT_FAST_REQUIRE_NE : return "FAST_REQUIRE_NE"; case assertType::DT_FAST_WARN_GT : return "FAST_WARN_GT"; case assertType::DT_FAST_CHECK_GT : return "FAST_CHECK_GT"; case assertType::DT_FAST_REQUIRE_GT : return "FAST_REQUIRE_GT"; case assertType::DT_FAST_WARN_LT : return "FAST_WARN_LT"; case assertType::DT_FAST_CHECK_LT : return "FAST_CHECK_LT"; case assertType::DT_FAST_REQUIRE_LT : return "FAST_REQUIRE_LT"; case assertType::DT_FAST_WARN_GE : return "FAST_WARN_GE"; case assertType::DT_FAST_CHECK_GE : return "FAST_CHECK_GE"; case assertType::DT_FAST_REQUIRE_GE : return "FAST_REQUIRE_GE"; case assertType::DT_FAST_WARN_LE : return "FAST_WARN_LE"; case assertType::DT_FAST_CHECK_LE : return "FAST_CHECK_LE"; case assertType::DT_FAST_REQUIRE_LE : return "FAST_REQUIRE_LE"; case assertType::DT_FAST_WARN_UNARY : return "FAST_WARN_UNARY"; case assertType::DT_FAST_CHECK_UNARY : return "FAST_CHECK_UNARY"; case assertType::DT_FAST_REQUIRE_UNARY : return "FAST_REQUIRE_UNARY"; case assertType::DT_FAST_WARN_UNARY_FALSE : return "FAST_WARN_UNARY_FALSE"; case assertType::DT_FAST_CHECK_UNARY_FALSE : return "FAST_CHECK_UNARY_FALSE"; case assertType::DT_FAST_REQUIRE_UNARY_FALSE: return "FAST_REQUIRE_UNARY_FALSE"; // clang-format on } DOCTEST_MSVC_SUPPRESS_WARNING_POP return ""; } bool checkIfShouldThrow(assertType::Enum at) { if(at & assertType::is_require) //!OCLINT bitwise operator in conditional return true; if((at & assertType::is_check) //!OCLINT bitwise operator in conditional && contextState->abort_after > 0 && contextState->numFailedAssertions >= contextState->abort_after) return true; return false; } void throwException() { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS throw TestFailureException(); #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } void fastAssertThrowIfFlagSet(int flags) { if(flags & assertAction::shouldthrow) //!OCLINT bitwise operator in conditional throwException(); } // matching of a string against a wildcard mask (case sensitivity configurable) taken from // https://www.codeproject.com/Articles/1088/Wildcard-string-compare-globbing int wildcmp(const char* str, const char* wild, bool caseSensitive) { const char* cp = 0; const char* mp = 0; while((*str) && (*wild != '*')) { if((caseSensitive ? (*wild != *str) : (tolower(*wild) != tolower(*str))) && (*wild != '?')) { return 0; } wild++; str++; } while(*str) { if(*wild == '*') { if(!*++wild) { return 1; } mp = wild; cp = str + 1; } else if((caseSensitive ? (*wild == *str) : (tolower(*wild) == tolower(*str))) || (*wild == '?')) { wild++; str++; } else { wild = mp; //!OCLINT parameter reassignment str = cp++; //!OCLINT parameter reassignment } } while(*wild == '*') { wild++; } return !*wild; } //// C string hash function (djb2) - taken from http://www.cse.yorku.ca/~oz/hash.html //unsigned hashStr(unsigned const char* str) { // unsigned long hash = 5381; // char c; // while((c = *str++)) // hash = ((hash << 5) + hash) + c; // hash * 33 + c // return hash; //} // checks if the name matches any of the filters (and can be configured what to do when empty) bool matchesAny(const char* name, const std::vector& filters, int matchEmpty, bool caseSensitive) { if(filters.empty() && matchEmpty) return true; for(unsigned i = 0; i < filters.size(); ++i) if(wildcmp(name, filters[i].c_str(), caseSensitive)) return true; return false; } #ifdef DOCTEST_PLATFORM_WINDOWS typedef unsigned long long UInt64; UInt64 getCurrentTicks() { static UInt64 hz = 0, hzo = 0; if(!hz) { QueryPerformanceFrequency(reinterpret_cast(&hz)); QueryPerformanceCounter(reinterpret_cast(&hzo)); } UInt64 t; QueryPerformanceCounter(reinterpret_cast(&t)); return ((t - hzo) * 1000000) / hz; } #else // DOCTEST_PLATFORM_WINDOWS typedef uint64_t UInt64; UInt64 getCurrentTicks() { timeval t; gettimeofday(&t, 0); return static_cast(t.tv_sec) * 1000000 + static_cast(t.tv_usec); } #endif // DOCTEST_PLATFORM_WINDOWS class Timer { public: Timer() : m_ticks(0) {} void start() { m_ticks = getCurrentTicks(); } unsigned int getElapsedMicroseconds() const { return static_cast(getCurrentTicks() - m_ticks); } unsigned int getElapsedMilliseconds() const { return static_cast(getElapsedMicroseconds() / 1000); } double getElapsedSeconds() const { return getElapsedMicroseconds() / 1000000.0; } private: UInt64 m_ticks; }; TestAccessibleContextState* getTestsContextState() { return contextState; } // TODO: remove this from here void logTestEnd(); bool SubcaseSignature::operator<(const SubcaseSignature& other) const { if(m_line != other.m_line) return m_line < other.m_line; if(std::strcmp(m_file, other.m_file) != 0) return std::strcmp(m_file, other.m_file) < 0; return std::strcmp(m_name, other.m_name) < 0; } Subcase::Subcase(const char* name, const char* file, int line) : m_signature(name, file, line) , m_entered(false) { ContextState* s = contextState; // if we have already completed it if(s->subcasesPassed.count(m_signature) != 0) return; // check subcase filters if(s->subcasesCurrentLevel < s->subcase_filter_levels) { if(!matchesAny(m_signature.m_name, s->filters[6], 1, s->case_sensitive)) return; if(matchesAny(m_signature.m_name, s->filters[7], 0, s->case_sensitive)) return; } // if a Subcase on the same level has already been entered if(s->subcasesEnteredLevels.count(s->subcasesCurrentLevel) != 0) { s->subcasesHasSkipped = true; return; } s->subcasesStack.push_back(*this); if(s->hasLoggedCurrentTestStart) logTestEnd(); s->hasLoggedCurrentTestStart = false; s->subcasesEnteredLevels.insert(s->subcasesCurrentLevel++); m_entered = true; } Subcase::Subcase(const Subcase& other) : m_signature(other.m_signature.m_name, other.m_signature.m_file, other.m_signature.m_line) , m_entered(other.m_entered) {} Subcase::~Subcase() { if(m_entered) { ContextState* s = contextState; s->subcasesCurrentLevel--; // only mark the subcase as passed if no subcases have been skipped if(s->subcasesHasSkipped == false) s->subcasesPassed.insert(m_signature); if(!s->subcasesStack.empty()) s->subcasesStack.pop_back(); if(s->hasLoggedCurrentTestStart) logTestEnd(); s->hasLoggedCurrentTestStart = false; } } Result::~Result() {} Result& Result::operator=(const Result& other) { m_passed = other.m_passed; m_decomposition = other.m_decomposition; return *this; } // for sorting tests by file/line int fileOrderComparator(const void* a, const void* b) { const TestCase* lhs = *static_cast(a); const TestCase* rhs = *static_cast(b); #if DOCTEST_MSVC // this is needed because MSVC gives different case for drive letters // for __FILE__ when evaluated in a header and a source file const int res = stricmp(lhs->m_file, rhs->m_file); #else // MSVC const int res = std::strcmp(lhs->m_file, rhs->m_file); #endif // MSVC if(res != 0) return res; return static_cast(lhs->m_line - rhs->m_line); } // for sorting tests by suite/file/line int suiteOrderComparator(const void* a, const void* b) { const TestCase* lhs = *static_cast(a); const TestCase* rhs = *static_cast(b); const int res = std::strcmp(lhs->m_test_suite, rhs->m_test_suite); if(res != 0) return res; return fileOrderComparator(a, b); } // for sorting tests by name/suite/file/line int nameOrderComparator(const void* a, const void* b) { const TestCase* lhs = *static_cast(a); const TestCase* rhs = *static_cast(b); const int res_name = std::strcmp(lhs->m_name, rhs->m_name); if(res_name != 0) return res_name; return suiteOrderComparator(a, b); } // sets the current test suite int setTestSuite(const TestSuite& ts) { doctest_detail_test_suite_ns::getCurrentTestSuite() = ts; return 0; } // all the registered tests std::set& getRegisteredTests() { static std::set data; return data; } // used by the macros for registering tests int regTest(const TestCase& tc) { getRegisteredTests().insert(tc); return 0; } namespace Color { enum Code { None = 0, White, Red, Green, Blue, Cyan, Yellow, Grey, Bright = 0x10, BrightRed = Bright | Red, BrightGreen = Bright | Green, LightGrey = Bright | Grey, BrightWhite = Bright | White }; #ifdef DOCTEST_CONFIG_COLORS_WINDOWS HANDLE g_stdoutHandle; WORD g_originalForegroundAttributes; WORD g_originalBackgroundAttributes; bool g_attrsInitted = false; #endif // DOCTEST_CONFIG_COLORS_WINDOWS void init() { #ifdef DOCTEST_CONFIG_COLORS_WINDOWS if(!g_attrsInitted) { g_stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); g_attrsInitted = true; CONSOLE_SCREEN_BUFFER_INFO csbiInfo; GetConsoleScreenBufferInfo(g_stdoutHandle, &csbiInfo); g_originalForegroundAttributes = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY); g_originalBackgroundAttributes = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); } #endif // DOCTEST_CONFIG_COLORS_WINDOWS } std::ostream& operator<<(std::ostream& s, Color::Code #ifndef DOCTEST_CONFIG_COLORS_NONE code #endif // DOCTEST_CONFIG_COLORS_NONE ) { const ContextState* p = contextState; if(p->no_colors) return s; #ifdef DOCTEST_CONFIG_COLORS_ANSI if(isatty(STDOUT_FILENO) == false && p->force_colors == false) return s; const char* col = ""; // clang-format off switch(code) { //!OCLINT missing break in switch statement / unnecessary default statement in covered switch statement case Color::Red: col = "[0;31m"; break; case Color::Green: col = "[0;32m"; break; case Color::Blue: col = "[0;34m"; break; case Color::Cyan: col = "[0;36m"; break; case Color::Yellow: col = "[0;33m"; break; case Color::Grey: col = "[1;30m"; break; case Color::LightGrey: col = "[0;37m"; break; case Color::BrightRed: col = "[1;31m"; break; case Color::BrightGreen: col = "[1;32m"; break; case Color::BrightWhite: col = "[1;37m"; break; case Color::Bright: // invalid case Color::None: case Color::White: default: col = "[0m"; } // clang-format on s << "\033" << col; #endif // DOCTEST_CONFIG_COLORS_ANSI #ifdef DOCTEST_CONFIG_COLORS_WINDOWS if(isatty(fileno(stdout)) == false && p->force_colors == false) return s; #define DOCTEST_SET_ATTR(x) \ SetConsoleTextAttribute(g_stdoutHandle, x | g_originalBackgroundAttributes) // clang-format off switch (code) { case Color::White: DOCTEST_SET_ATTR(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; case Color::Red: DOCTEST_SET_ATTR(FOREGROUND_RED); break; case Color::Green: DOCTEST_SET_ATTR(FOREGROUND_GREEN); break; case Color::Blue: DOCTEST_SET_ATTR(FOREGROUND_BLUE); break; case Color::Cyan: DOCTEST_SET_ATTR(FOREGROUND_BLUE | FOREGROUND_GREEN); break; case Color::Yellow: DOCTEST_SET_ATTR(FOREGROUND_RED | FOREGROUND_GREEN); break; case Color::Grey: DOCTEST_SET_ATTR(0); break; case Color::LightGrey: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY); break; case Color::BrightRed: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_RED); break; case Color::BrightGreen: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN); break; case Color::BrightWhite: DOCTEST_SET_ATTR(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); break; case Color::None: case Color::Bright: // invalid default: DOCTEST_SET_ATTR(g_originalForegroundAttributes); } // clang-format on #undef DOCTEST_SET_ATTR #endif // DOCTEST_CONFIG_COLORS_WINDOWS return s; } } // namespace Color std::vector& getExceptionTranslators() { static std::vector data; return data; } void registerExceptionTranslatorImpl(const IExceptionTranslator* translateFunction) { if(std::find(getExceptionTranslators().begin(), getExceptionTranslators().end(), translateFunction) == getExceptionTranslators().end()) getExceptionTranslators().push_back(translateFunction); } String translateActiveException() { #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS String res; std::vector& translators = getExceptionTranslators(); for(size_t i = 0; i < translators.size(); ++i) if(translators[i]->translate(res)) return res; // clang-format off try { throw; } catch(std::exception& ex) { return ex.what(); } catch(std::string& msg) { return msg.c_str(); } catch(const char* msg) { return msg; } catch(...) { return "unknown exception"; } // clang-format on #else // DOCTEST_CONFIG_NO_EXCEPTIONS return ""; #endif // DOCTEST_CONFIG_NO_EXCEPTIONS } void writeStringToStream(std::ostream* s, const String& str) { *s << str; } #ifdef DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING void toStream(std::ostream* s, char* in) { *s << in; } void toStream(std::ostream* s, const char* in) { *s << in; } #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING void toStream(std::ostream* s, bool in) { *s << std::boolalpha << in << std::noboolalpha; } void toStream(std::ostream* s, float in) { *s << in; } void toStream(std::ostream* s, double in) { *s << in; } void toStream(std::ostream* s, double long in) { *s << in; } void toStream(std::ostream* s, char in) { *s << in; } void toStream(std::ostream* s, char signed in) { *s << in; } void toStream(std::ostream* s, char unsigned in) { *s << in; } void toStream(std::ostream* s, int short in) { *s << in; } void toStream(std::ostream* s, int short unsigned in) { *s << in; } void toStream(std::ostream* s, int in) { *s << in; } void toStream(std::ostream* s, int unsigned in) { *s << in; } void toStream(std::ostream* s, int long in) { *s << in; } void toStream(std::ostream* s, int long unsigned in) { *s << in; } #ifdef DOCTEST_CONFIG_WITH_LONG_LONG void toStream(std::ostream* s, int long long in) { *s << in; } void toStream(std::ostream* s, int long long unsigned in) { *s << in; } #endif // DOCTEST_CONFIG_WITH_LONG_LONG void addToContexts(IContextScope* ptr) { contextState->contexts.push_back(ptr); } void popFromContexts() { contextState->contexts.pop_back(); } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4996) // std::uncaught_exception is deprecated in C++17 DOCTEST_GCC_SUPPRESS_WARNING_WITH_PUSH("-Wdeprecated-declarations") void useContextIfExceptionOccurred(IContextScope* ptr) { if(std::uncaught_exception()) { std::ostringstream s; ptr->build(&s); contextState->exceptionalContexts.push_back(s.str()); } } DOCTEST_GCC_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP void printSummary(std::ostream& s); #if !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && !defined(DOCTEST_CONFIG_WINDOWS_SEH) void reportFatal(const std::string&) {} struct FatalConditionHandler { void reset() {} }; #else // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH void reportFatal(const std::string&); #ifdef DOCTEST_PLATFORM_WINDOWS struct SignalDefs { DWORD id; const char* name; }; // There is no 1-1 mapping between signals and windows exceptions. // Windows can easily distinguish between SO and SigSegV, // but SigInt, SigTerm, etc are handled differently. SignalDefs signalDefs[] = { {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"}, {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"}, {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"}, {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"}, }; struct FatalConditionHandler { static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { for(size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { if(ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { reportFatal(signalDefs[i].name); } } // If its not an exception we care about, pass it along. // This stops us from eating debugger breaks etc. return EXCEPTION_CONTINUE_SEARCH; } FatalConditionHandler() { isSet = true; // 32k seems enough for doctest to handle stack overflow, // but the value was found experimentally, so there is no strong guarantee guaranteeSize = 32 * 1024; exceptionHandlerHandle = 0; // Register as first handler in current chain exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); // Pass in guarantee size to be filled SetThreadStackGuarantee(&guaranteeSize); } static void reset() { if(isSet) { // Unregister handler and restore the old guarantee RemoveVectoredExceptionHandler(exceptionHandlerHandle); SetThreadStackGuarantee(&guaranteeSize); exceptionHandlerHandle = 0; isSet = false; } } ~FatalConditionHandler() { reset(); } private: static bool isSet; static ULONG guaranteeSize; static PVOID exceptionHandlerHandle; }; bool FatalConditionHandler::isSet = false; ULONG FatalConditionHandler::guaranteeSize = 0; PVOID FatalConditionHandler::exceptionHandlerHandle = 0; #else // DOCTEST_PLATFORM_WINDOWS struct SignalDefs { int id; const char* name; }; SignalDefs signalDefs[] = {{SIGINT, "SIGINT - Terminal interrupt signal"}, {SIGILL, "SIGILL - Illegal instruction signal"}, {SIGFPE, "SIGFPE - Floating point error signal"}, {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, {SIGTERM, "SIGTERM - Termination request signal"}, {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; struct FatalConditionHandler { static bool isSet; static struct sigaction oldSigActions[DOCTEST_COUNTOF(signalDefs)]; static stack_t oldSigStack; static char altStackMem[4 * SIGSTKSZ]; static void handleSignal(int sig) { std::string name = ""; for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { SignalDefs& def = signalDefs[i]; if(sig == def.id) { name = def.name; break; } } reset(); reportFatal(name); raise(sig); } FatalConditionHandler() { isSet = true; stack_t sigStack; sigStack.ss_sp = altStackMem; sigStack.ss_size = sizeof(altStackMem); sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = {0}; sa.sa_handler = handleSignal; // NOLINT sa.sa_flags = SA_ONSTACK; for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); } } ~FatalConditionHandler() { reset(); } static void reset() { if(isSet) { // Set signals back to previous values -- hopefully nobody overwrote them in the meantime for(std::size_t i = 0; i < DOCTEST_COUNTOF(signalDefs); ++i) { sigaction(signalDefs[i].id, &oldSigActions[i], 0); } // Return the old stack sigaltstack(&oldSigStack, 0); isSet = false; } } }; bool FatalConditionHandler::isSet = false; struct sigaction FatalConditionHandler::oldSigActions[DOCTEST_COUNTOF(signalDefs)] = {}; stack_t FatalConditionHandler::oldSigStack = {}; char FatalConditionHandler::altStackMem[] = {}; #endif // DOCTEST_PLATFORM_WINDOWS #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH void separator_to_stream(std::ostream& s) { s << Color::Yellow << "===============================================================================\n"; } // depending on the current options this will remove the path of filenames const char* fileForOutput(const char* file) { if(contextState->no_path_in_filenames) { const char* back = std::strrchr(file, '\\'); const char* forward = std::strrchr(file, '/'); if(back || forward) { if(back > forward) forward = back; return forward + 1; } } return file; } void file_line_to_stream(std::ostream& s, const char* file, int line, const char* tail = "") { s << Color::LightGrey << fileForOutput(file) << (contextState->gnu_file_line ? ":" : "(") << (contextState->no_line_numbers ? 0 : line) // 0 or the real num depending on the option << (contextState->gnu_file_line ? ":" : "):") << tail; } const char* getSuccessOrFailString(bool success, assertType::Enum at, const char* success_str) { if(success) return success_str; if(at & assertType::is_warn) //!OCLINT bitwise operator in conditional return "WARNING: "; if(at & assertType::is_check) //!OCLINT bitwise operator in conditional return "ERROR: "; if(at & assertType::is_require) //!OCLINT bitwise operator in conditional return "FATAL ERROR: "; return ""; } Color::Code getSuccessOrFailColor(bool success, assertType::Enum at) { return success ? Color::BrightGreen : (at & assertType::is_warn) ? Color::Yellow : Color::Red; } void successOrFailColoredStringToStream(std::ostream& s, bool success, assertType::Enum at, const char* success_str = "SUCCESS: ") { s << getSuccessOrFailColor(success, at) << getSuccessOrFailString(success, at, success_str); } #ifdef DOCTEST_PLATFORM_MAC #include #include #include // The following function is taken directly from the following technical note: // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html // Returns true if the current process is being debugged (either // running under the debugger or has a debugger attached post facto). bool isDebuggerActive() { int mib[4]; kinfo_proc info; size_t size; // Initialize the flags so that, if sysctl fails for some bizarre // reason, we get a predictable result. info.kp_proc.p_flag = 0; // Initialize mib, which tells sysctl the info we want, in this case // we're looking for information about a specific process ID. mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = getpid(); // Call sysctl. size = sizeof(info); if(sysctl(mib, DOCTEST_COUNTOF(mib), &info, &size, 0, 0) != 0) { fprintf(stderr, "\n** Call to sysctl failed - unable to determine if debugger is " "active **\n\n"); return false; } // We're being debugged if the P_TRACED flag is set. return ((info.kp_proc.p_flag & P_TRACED) != 0); } #elif DOCTEST_MSVC || defined(__MINGW32__) bool isDebuggerActive() { return ::IsDebuggerPresent() != 0; } #else bool isDebuggerActive() { return false; } #endif // Platform #ifdef DOCTEST_PLATFORM_WINDOWS void myOutputDebugString(const String& text) { ::OutputDebugStringA(text.c_str()); } #else // TODO: integration with XCode and other IDEs void myOutputDebugString(const String&) {} #endif // Platform void printToDebugConsole(const String& text) { if(isDebuggerActive()) myOutputDebugString(text); } void addFailedAssert(assertType::Enum at) { if((at & assertType::is_warn) == 0) { //!OCLINT bitwise operator in conditional contextState->numFailedAssertions++; contextState->numFailedAssertionsForCurrentTestcase++; contextState->hasCurrentTestFailed = true; } } std::ostream& operator<<(std::ostream& s, const std::vector& contexts) { if(!contexts.empty()) s << Color::None << " logged: "; for(size_t i = 0; i < contexts.size(); ++i) { s << (i == 0 ? "" : " "); contexts[i]->build(&s); s << "\n"; } s << "\n"; return s; } void logTestStart(std::ostream& s, const TestCase& tc) { separator_to_stream(s); file_line_to_stream(s, tc.m_file, tc.m_line, "\n"); if(tc.m_description) s << Color::Yellow << "DESCRIPTION: " << Color::None << tc.m_description << "\n"; if(tc.m_test_suite && tc.m_test_suite[0] != '\0') s << Color::Yellow << "TEST SUITE: " << Color::None << tc.m_test_suite << "\n"; if(strncmp(tc.m_name, " Scenario:", 11) != 0) s << Color::None << "TEST CASE: "; s << Color::None << tc.m_name << "\n"; std::vector& subcasesStack = contextState->subcasesStack; for(unsigned i = 0; i < subcasesStack.size(); ++i) if(subcasesStack[i].m_signature.m_name[0] != '\0') s << " " << subcasesStack[i].m_signature.m_name << "\n"; s << "\n"; } void logTestEnd() {} void logTestException_impl(std::ostream& s, const TestCase& tc, const String& str, bool crash) { file_line_to_stream(s, tc.m_file, tc.m_line, " "); successOrFailColoredStringToStream(s, false, crash ? assertType::is_require : assertType::is_check); s << Color::Red << (crash ? "test case CRASHED: " : "test case THREW exception: ") << Color::Cyan << str << "\n"; if(!contextState->exceptionalContexts.empty()) { s << Color::None << " logged: "; for(size_t i = contextState->exceptionalContexts.size(); i > 0; --i) s << (i == contextState->exceptionalContexts.size() ? "" : " ") << contextState->exceptionalContexts[i - 1] << "\n"; } s << "\n"; } void logTestException(const TestCase& tc, const String& what, bool crash) { logTestException_impl(std::cout, tc, what, crash); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logTestException_impl(oss, tc, what, crash); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } #if defined(DOCTEST_CONFIG_POSIX_SIGNALS) || defined(DOCTEST_CONFIG_WINDOWS_SEH) void reportFatal(const std::string& message) { DOCTEST_LOG_START(std::cout); contextState->numAssertions += contextState->numAssertionsForCurrentTestcase; logTestException(*contextState->currentTest, message.c_str(), true); logTestEnd(); contextState->numFailed++; printSummary(std::cout); } #endif // DOCTEST_CONFIG_POSIX_SIGNALS || DOCTEST_CONFIG_WINDOWS_SEH void logAssert_impl(std::ostream& s, bool passed, const String& dec, bool threw, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { file_line_to_stream(s, file, line, " "); successOrFailColoredStringToStream(s, passed, at); s << Color::Cyan << assertString(at) << "( " << expr << " ) " << Color::None << (threw ? "THREW exception: " : (passed ? "is correct!\n" : "is NOT correct!\n")); if(threw) s << ex << "\n"; else s << " values: " << assertString(at) << "( " << dec << " )\n"; s << contextState->contexts; } void logAssert(bool passed, const String& dec, bool threw, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { logAssert_impl(std::cout, passed, dec, threw, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logAssert_impl(oss, passed, dec, threw, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } void logAssertThrows_impl(std::ostream& s, bool threw, const char* expr, assertType::Enum at, const char* file, int line) { file_line_to_stream(s, file, line, " "); successOrFailColoredStringToStream(s, threw, at); s << Color::Cyan << assertString(at) << "( " << expr << " ) " << Color::None << (threw ? "threw as expected!" : "did NOT throw at all!") << "\n"; s << contextState->contexts; } void logAssertThrows(bool threw, const char* expr, assertType::Enum at, const char* file, int line) { logAssertThrows_impl(std::cout, threw, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logAssertThrows_impl(oss, threw, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } void logAssertThrowsAs_impl(std::ostream& s, bool threw, bool threw_as, const char* as, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { file_line_to_stream(s, file, line, " "); successOrFailColoredStringToStream(s, threw_as, at); s << Color::Cyan << assertString(at) << "( " << expr << ", " << as << " ) " << Color::None << (threw ? (threw_as ? "threw as expected!" : "threw a DIFFERENT exception: ") : "did NOT throw at all!") << Color::Cyan << ex << "\n"; s << contextState->contexts; } void logAssertThrowsAs(bool threw, bool threw_as, const char* as, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { logAssertThrowsAs_impl(std::cout, threw, threw_as, as, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logAssertThrowsAs_impl(oss, threw, threw_as, as, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } void logAssertNothrow_impl(std::ostream& s, bool threw, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { file_line_to_stream(s, file, line, " "); successOrFailColoredStringToStream(s, !threw, at); s << Color::Cyan << assertString(at) << "( " << expr << " ) " << Color::None << (threw ? "THREW exception: " : "didn't throw!") << Color::Cyan << ex << "\n"; s << contextState->contexts; } void logAssertNothrow(bool threw, const String& ex, const char* expr, assertType::Enum at, const char* file, int line) { logAssertNothrow_impl(std::cout, threw, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; logAssertNothrow_impl(oss, threw, ex, expr, at, file, line); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; } ResultBuilder::ResultBuilder(assertType::Enum at, const char* file, int line, const char* expr, const char* exception_type) : m_assert_type(at) , m_file(file) , m_line(line) , m_expr(expr) , m_exception_type(exception_type) , m_threw(false) , m_threw_as(false) , m_failed(false) { #if DOCTEST_MSVC if(m_expr[0] == ' ') // this happens when variadic macros are disabled under MSVC ++m_expr; #endif // MSVC } ResultBuilder::~ResultBuilder() {} void ResultBuilder::unexpectedExceptionOccurred() { m_threw = true; m_exception = translateActiveException(); } bool ResultBuilder::log() { if((m_assert_type & assertType::is_warn) == 0) //!OCLINT bitwise operator in conditional contextState->numAssertionsForCurrentTestcase++; if(m_assert_type & assertType::is_throws) { //!OCLINT bitwise operator in conditional m_failed = !m_threw; } else if(m_assert_type & //!OCLINT bitwise operator in conditional assertType::is_throws_as) { m_failed = !m_threw_as; } else if(m_assert_type & //!OCLINT bitwise operator in conditional assertType::is_nothrow) { m_failed = m_threw; } else { m_failed = m_result; } if(m_failed || contextState->success) { DOCTEST_LOG_START(std::cout); if(m_assert_type & assertType::is_throws) { //!OCLINT bitwise operator in conditional logAssertThrows(m_threw, m_expr, m_assert_type, m_file, m_line); } else if(m_assert_type & //!OCLINT bitwise operator in conditional assertType::is_throws_as) { logAssertThrowsAs(m_threw, m_threw_as, m_exception_type, m_exception, m_expr, m_assert_type, m_file, m_line); } else if(m_assert_type & //!OCLINT bitwise operator in conditional assertType::is_nothrow) { logAssertNothrow(m_threw, m_exception, m_expr, m_assert_type, m_file, m_line); } else { logAssert(m_result.m_passed, m_result.m_decomposition, m_threw, m_exception, m_expr, m_assert_type, m_file, m_line); } } if(m_failed) addFailedAssert(m_assert_type); return m_failed && isDebuggerActive() && !contextState->no_breaks; // break into debugger } void ResultBuilder::react() const { if(m_failed && checkIfShouldThrow(m_assert_type)) throwException(); } MessageBuilder::MessageBuilder(const char* file, int line, assertType::Enum severity) : m_stream(createStream()) , m_file(file) , m_line(line) , m_severity(severity) {} void MessageBuilder::log(std::ostream& s) { file_line_to_stream(s, m_file, m_line, " "); s << getSuccessOrFailColor(false, m_severity) << getSuccessOrFailString(m_severity & assertType::is_warn, m_severity, "MESSAGE: "); s << Color::None << getStreamResult(m_stream) << "\n"; s << contextState->contexts; } bool MessageBuilder::log() { DOCTEST_LOG_START(std::cout); log(std::cout); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_BEGIN; log(oss); DOCTEST_PRINT_TO_OUTPUT_WINDOW_IN_IDE_END; const bool isWarn = m_severity & assertType::is_warn; // warn is just a message in this context so we dont treat it as an assert if(!isWarn) { contextState->numAssertionsForCurrentTestcase++; addFailedAssert(m_severity); } return isDebuggerActive() && !contextState->no_breaks && !isWarn; // break into debugger } void MessageBuilder::react() { if(m_severity & assertType::is_require) //!OCLINT bitwise operator in conditional throwException(); } MessageBuilder::~MessageBuilder() { freeStream(m_stream); } // the implementation of parseFlag() bool parseFlagImpl(int argc, const char* const* argv, const char* pattern) { for(int i = argc - 1; i >= 0; --i) { const char* temp = std::strstr(argv[i], pattern); if(temp && strlen(temp) == strlen(pattern)) { // eliminate strings in which the chars before the option are not '-' bool noBadCharsFound = true; //!OCLINT prefer early exits and continue while(temp != argv[i]) { if(*--temp != '-') { noBadCharsFound = false; break; } } if(noBadCharsFound && argv[i][0] == '-') return true; } } return false; } // locates a flag on the command line bool parseFlag(int argc, const char* const* argv, const char* pattern) { #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS if(!parseFlagImpl(argc, argv, pattern)) return parseFlagImpl(argc, argv, pattern + 3); // 3 for "dt-" return true; #else // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS return parseFlagImpl(argc, argv, pattern); #endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS } // the implementation of parseOption() bool parseOptionImpl(int argc, const char* const* argv, const char* pattern, String& res) { for(int i = argc - 1; i >= 0; --i) { const char* temp = std::strstr(argv[i], pattern); if(temp) { //!OCLINT prefer early exits and continue // eliminate matches in which the chars before the option are not '-' bool noBadCharsFound = true; const char* curr = argv[i]; while(curr != temp) { if(*curr++ != '-') { noBadCharsFound = false; break; } } if(noBadCharsFound && argv[i][0] == '-') { temp += strlen(pattern); const unsigned len = strlen(temp); if(len) { res = temp; return true; } } } } return false; } // parses an option and returns the string after the '=' character bool parseOption(int argc, const char* const* argv, const char* pattern, String& res, const String& defaultVal = String()) { res = defaultVal; #ifndef DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS if(!parseOptionImpl(argc, argv, pattern, res)) return parseOptionImpl(argc, argv, pattern + 3, res); // 3 for "dt-" return true; #else // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS return parseOptionImpl(argc, argv, pattern, res); #endif // DOCTEST_CONFIG_NO_UNPREFIXED_OPTIONS } // parses a comma separated list of words after a pattern in one of the arguments in argv bool parseCommaSepArgs(int argc, const char* const* argv, const char* pattern, std::vector& res) { String filtersString; if(parseOption(argc, argv, pattern, filtersString)) { // tokenize with "," as a separator // cppcheck-suppress strtokCalled char* pch = std::strtok(filtersString.c_str(), ","); // modifies the string while(pch != 0) { if(strlen(pch)) res.push_back(pch); // uses the strtok() internal state to go to the next token // cppcheck-suppress strtokCalled pch = std::strtok(0, ","); } return true; } return false; } enum optionType { option_bool, option_int }; // parses an int/bool option from the command line bool parseIntOption(int argc, const char* const* argv, const char* pattern, optionType type, int& res) { String parsedValue; if(!parseOption(argc, argv, pattern, parsedValue)) return false; if(type == 0) { // boolean const char positive[][5] = {"1", "true", "on", "yes"}; // 5 - strlen("true") + 1 const char negative[][6] = {"0", "false", "off", "no"}; // 6 - strlen("false") + 1 // if the value matches any of the positive/negative possibilities for(unsigned i = 0; i < 4; i++) { if(parsedValue.compare(positive[i], true) == 0) { res = 1; //!OCLINT parameter reassignment return true; } if(parsedValue.compare(negative[i], true) == 0) { res = 0; //!OCLINT parameter reassignment return true; } } } else { // integer int theInt = std::atoi(parsedValue.c_str()); // NOLINT if(theInt != 0) { res = theInt; //!OCLINT parameter reassignment return true; } } return false; } void printVersion(std::ostream& s) { if(contextState->no_version == false) s << Color::Cyan << "[doctest] " << Color::None << "doctest version is \"" << DOCTEST_VERSION_STR << "\"\n"; } void printHelp(std::ostream& s) { printVersion(s); // clang-format off s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "boolean values: \"1/on/yes/true\" or \"0/off/no/false\"\n"; s << Color::Cyan << "[doctest] " << Color::None; s << "filter values: \"str1,str2,str3\" (comma separated strings)\n"; s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "filters use wildcards for matching strings\n"; s << Color::Cyan << "[doctest] " << Color::None; s << "something passes a filter if any of the strings in a filter matches\n"; s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "ALL FLAGS, OPTIONS AND FILTERS ALSO AVAILABLE WITH A \"dt-\" PREFIX!!!\n"; s << Color::Cyan << "[doctest]\n" << Color::None; s << Color::Cyan << "[doctest] " << Color::None; s << "Query flags - the program quits after them. Available:\n\n"; s << " -?, --help, -h prints this message\n"; s << " -v, --version prints the version\n"; s << " -c, --count prints the number of matching tests\n"; s << " -ltc, --list-test-cases lists all matching tests by name\n"; s << " -lts, --list-test-suites lists all matching test suites\n\n"; // ================================================================================== << 79 s << Color::Cyan << "[doctest] " << Color::None; s << "The available / options/filters are:\n\n"; s << " -tc, --test-case= filters tests by their name\n"; s << " -tce, --test-case-exclude= filters OUT tests by their name\n"; s << " -sf, --source-file= filters tests by their file\n"; s << " -sfe, --source-file-exclude= filters OUT tests by their file\n"; s << " -ts, --test-suite= filters tests by their test suite\n"; s << " -tse, --test-suite-exclude= filters OUT tests by their test suite\n"; s << " -sc, --subcase= filters subcases by their name\n"; s << " -sce, --subcase-exclude= filters OUT subcases by their name\n"; s << " -ob, --order-by= how the tests should be ordered\n"; s << " - by [file/suite/name/rand]\n"; s << " -rs, --rand-seed= seed for random ordering\n"; s << " -f, --first= the first test passing the filters to\n"; s << " execute - for range-based execution\n"; s << " -l, --last= the last test passing the filters to\n"; s << " execute - for range-based execution\n"; s << " -aa, --abort-after= stop after failed assertions\n"; s << " -scfl,--subcase-filter-levels= apply filters for the first levels\n"; s << Color::Cyan << "\n[doctest] " << Color::None; s << "Bool options - can be used like flags and true is assumed. Available:\n\n"; s << " -s, --success= include successful assertions in output\n"; s << " -cs, --case-sensitive= filters being treated as case sensitive\n"; s << " -e, --exit= exits after the tests finish\n"; s << " -d, --duration= prints the time duration of each test\n"; s << " -nt, --no-throw= skips exceptions-related assert checks\n"; s << " -ne, --no-exitcode= returns (or exits) always with success\n"; s << " -nr, --no-run= skips all runtime doctest operations\n"; s << " -nv, --no-version= omit the framework version in the output\n"; s << " -nc, --no-colors= disables colors in output\n"; s << " -fc, --force-colors= use colors even when not in a tty\n"; s << " -nb, --no-breaks= disables breakpoints in debuggers\n"; s << " -ns, --no-skip= don't skip test cases marked as skip\n"; s << " -gfl, --gnu-file-line= :n: vs (n): for line numbers in output\n"; s << " -npf, --no-path-filenames= only filenames and no paths in output\n"; s << " -nln, --no-line-numbers= 0 instead of real line numbers in output\n"; // ================================================================================== << 79 // clang-format on s << Color::Cyan << "\n[doctest] " << Color::None; s << "for more information visit the project documentation\n\n"; } void printSummary(std::ostream& s) { const ContextState* p = contextState; separator_to_stream(s); if(p->count || p->list_test_cases) { s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << p->numTestsPassingFilters << "\n"; } else if(p->list_test_suites) { s << Color::Cyan << "[doctest] " << Color::None << "unskipped test cases passing the current filters: " << p->numTestsPassingFilters << "\n"; s << Color::Cyan << "[doctest] " << Color::None << "test suites with unskipped test cases passing the current filters: " << p->numTestSuitesPassingFilters << "\n"; } else { const bool anythingFailed = p->numFailed > 0 || p->numFailedAssertions > 0; s << Color::Cyan << "[doctest] " << Color::None << "test cases: " << std::setw(6) << p->numTestsPassingFilters << " | " << ((p->numTestsPassingFilters == 0 || anythingFailed) ? Color::None : Color::Green) << std::setw(6) << p->numTestsPassingFilters - p->numFailed << " passed" << Color::None << " | " << (p->numFailed > 0 ? Color::Red : Color::None) << std::setw(6) << p->numFailed << " failed" << Color::None << " | "; if(p->no_skipped_summary == false) { const int numSkipped = static_cast(getRegisteredTests().size()) - p->numTestsPassingFilters; s << (numSkipped == 0 ? Color::None : Color::Yellow) << std::setw(6) << numSkipped << " skipped" << Color::None; } s << "\n"; s << Color::Cyan << "[doctest] " << Color::None << "assertions: " << std::setw(6) << p->numAssertions << " | " << ((p->numAssertions == 0 || anythingFailed) ? Color::None : Color::Green) << std::setw(6) << (p->numAssertions - p->numFailedAssertions) << " passed" << Color::None << " | " << (p->numFailedAssertions > 0 ? Color::Red : Color::None) << std::setw(6) << p->numFailedAssertions << " failed" << Color::None << " |\n"; s << Color::Cyan << "[doctest] " << Color::None << "Status: " << (p->numFailed > 0 ? Color::Red : Color::Green) << ((p->numFailed > 0) ? "FAILURE!\n" : "SUCCESS!\n"); } // remove any coloring s << Color::None; } } // namespace detail bool isRunningInTest() { return detail::contextState != 0; } Context::Context(int argc, const char* const* argv) : p(new detail::ContextState) { parseArgs(argc, argv, true); } Context::~Context() { delete p; } void Context::applyCommandLine(int argc, const char* const* argv) { parseArgs(argc, argv); } // parses args void Context::parseArgs(int argc, const char* const* argv, bool withDefaults) { using namespace detail; // clang-format off parseCommaSepArgs(argc, argv, "dt-source-file=", p->filters[0]); parseCommaSepArgs(argc, argv, "dt-sf=", p->filters[0]); parseCommaSepArgs(argc, argv, "dt-source-file-exclude=",p->filters[1]); parseCommaSepArgs(argc, argv, "dt-sfe=", p->filters[1]); parseCommaSepArgs(argc, argv, "dt-test-suite=", p->filters[2]); parseCommaSepArgs(argc, argv, "dt-ts=", p->filters[2]); parseCommaSepArgs(argc, argv, "dt-test-suite-exclude=", p->filters[3]); parseCommaSepArgs(argc, argv, "dt-tse=", p->filters[3]); parseCommaSepArgs(argc, argv, "dt-test-case=", p->filters[4]); parseCommaSepArgs(argc, argv, "dt-tc=", p->filters[4]); parseCommaSepArgs(argc, argv, "dt-test-case-exclude=", p->filters[5]); parseCommaSepArgs(argc, argv, "dt-tce=", p->filters[5]); parseCommaSepArgs(argc, argv, "dt-subcase=", p->filters[6]); parseCommaSepArgs(argc, argv, "dt-sc=", p->filters[6]); parseCommaSepArgs(argc, argv, "dt-subcase-exclude=", p->filters[7]); parseCommaSepArgs(argc, argv, "dt-sce=", p->filters[7]); // clang-format on int intRes = 0; String strRes; #define DOCTEST_PARSE_AS_BOOL_OR_FLAG(name, sname, var, default) \ if(parseIntOption(argc, argv, name "=", option_bool, intRes) || \ parseIntOption(argc, argv, sname "=", option_bool, intRes)) \ p->var = !!intRes; \ else if(parseFlag(argc, argv, name) || parseFlag(argc, argv, sname)) \ p->var = true; \ else if(withDefaults) \ p->var = default #define DOCTEST_PARSE_INT_OPTION(name, sname, var, default) \ if(parseIntOption(argc, argv, name "=", option_int, intRes) || \ parseIntOption(argc, argv, sname "=", option_int, intRes)) \ p->var = intRes; \ else if(withDefaults) \ p->var = default #define DOCTEST_PARSE_STR_OPTION(name, sname, var, default) \ if(parseOption(argc, argv, name "=", strRes, default) || \ parseOption(argc, argv, sname "=", strRes, default) || withDefaults) \ p->var = strRes // clang-format off DOCTEST_PARSE_STR_OPTION("dt-order-by", "dt-ob", order_by, "file"); DOCTEST_PARSE_INT_OPTION("dt-rand-seed", "dt-rs", rand_seed, 0); DOCTEST_PARSE_INT_OPTION("dt-first", "dt-f", first, 1); DOCTEST_PARSE_INT_OPTION("dt-last", "dt-l", last, 0); DOCTEST_PARSE_INT_OPTION("dt-abort-after", "dt-aa", abort_after, 0); DOCTEST_PARSE_INT_OPTION("dt-subcase-filter-levels", "dt-scfl", subcase_filter_levels, 2000000000); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-success", "dt-s", success, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-case-sensitive", "dt-cs", case_sensitive, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-exit", "dt-e", exit, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-duration", "dt-d", duration, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-throw", "dt-nt", no_throw, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-exitcode", "dt-ne", no_exitcode, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-run", "dt-nr", no_run, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-version", "dt-nv", no_version, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-colors", "dt-nc", no_colors, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-force-colors", "dt-fc", force_colors, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-breaks", "dt-nb", no_breaks, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-skip", "dt-ns", no_skip, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-gnu-file-line", "dt-gfl", gnu_file_line, bool(DOCTEST_GCC) || bool(DOCTEST_CLANG)); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-path-filenames", "dt-npf", no_path_in_filenames, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-line-numbers", "dt-nln", no_line_numbers, false); DOCTEST_PARSE_AS_BOOL_OR_FLAG("dt-no-skipped-summary", "dt-nss", no_skipped_summary, false); // clang-format on #undef DOCTEST_PARSE_STR_OPTION #undef DOCTEST_PARSE_INT_OPTION #undef DOCTEST_PARSE_AS_BOOL_OR_FLAG if(withDefaults) { p->help = false; p->version = false; p->count = false; p->list_test_cases = false; p->list_test_suites = false; } if(parseFlag(argc, argv, "dt-help") || parseFlag(argc, argv, "dt-h") || parseFlag(argc, argv, "dt-?")) { p->help = true; p->exit = true; } if(parseFlag(argc, argv, "dt-version") || parseFlag(argc, argv, "dt-v")) { p->version = true; p->exit = true; } if(parseFlag(argc, argv, "dt-count") || parseFlag(argc, argv, "dt-c")) { p->count = true; p->exit = true; } if(parseFlag(argc, argv, "dt-list-test-cases") || parseFlag(argc, argv, "dt-ltc")) { p->list_test_cases = true; p->exit = true; } if(parseFlag(argc, argv, "dt-list-test-suites") || parseFlag(argc, argv, "dt-lts")) { p->list_test_suites = true; p->exit = true; } } // allows the user to add procedurally to the filters from the command line void Context::addFilter(const char* filter, const char* value) { setOption(filter, value); } // allows the user to clear all filters from the command line void Context::clearFilters() { for(unsigned i = 0; i < p->filters.size(); ++i) p->filters[i].clear(); } // allows the user to override procedurally the int/bool options from the command line void Context::setOption(const char* option, int value) { setOption(option, toString(value).c_str()); } // allows the user to override procedurally the string options from the command line void Context::setOption(const char* option, const char* value) { String argv = String("-") + option + "=" + value; const char* lvalue = argv.c_str(); parseArgs(1, &lvalue); } // users should query this in their main() and exit the program if true bool Context::shouldExit() { return p->exit; } // the main function that does all the filtering and test running int Context::run() { using namespace detail; Color::init(); contextState = p; p->resetRunData(); // handle version, help and no_run if(p->no_run || p->version || p->help) { if(p->version) printVersion(std::cout); if(p->help) printHelp(std::cout); contextState = 0; return EXIT_SUCCESS; } printVersion(std::cout); std::cout << Color::Cyan << "[doctest] " << Color::None << "run with \"--help\" for options\n"; unsigned i = 0; // counter used for loops - here for VC6 std::set& registeredTests = getRegisteredTests(); std::vector testArray; for(std::set::iterator it = registeredTests.begin(); it != registeredTests.end(); ++it) testArray.push_back(&(*it)); // sort the collected records if(!testArray.empty()) { if(p->order_by.compare("file", true) == 0) { std::qsort(&testArray[0], testArray.size(), sizeof(TestCase*), fileOrderComparator); } else if(p->order_by.compare("suite", true) == 0) { std::qsort(&testArray[0], testArray.size(), sizeof(TestCase*), suiteOrderComparator); } else if(p->order_by.compare("name", true) == 0) { std::qsort(&testArray[0], testArray.size(), sizeof(TestCase*), nameOrderComparator); } else if(p->order_by.compare("rand", true) == 0) { std::srand(p->rand_seed); // random_shuffle implementation const TestCase** first = &testArray[0]; for(i = testArray.size() - 1; i > 0; --i) { int idxToSwap = std::rand() % (i + 1); // NOLINT const TestCase* temp = first[i]; first[i] = first[idxToSwap]; first[idxToSwap] = temp; } } } if(p->list_test_cases) { std::cout << Color::Cyan << "[doctest] " << Color::None << "listing all test case names\n"; separator_to_stream(std::cout); } std::set testSuitesPassingFilters; if(p->list_test_suites) { std::cout << Color::Cyan << "[doctest] " << Color::None << "listing all test suites\n"; separator_to_stream(std::cout); } // invoke the registered functions if they match the filter criteria (or just count them) for(i = 0; i < testArray.size(); i++) { const TestCase& data = *testArray[i]; if(data.m_skip && !p->no_skip) continue; if(!matchesAny(data.m_file, p->filters[0], 1, p->case_sensitive)) continue; if(matchesAny(data.m_file, p->filters[1], 0, p->case_sensitive)) continue; if(!matchesAny(data.m_test_suite, p->filters[2], 1, p->case_sensitive)) continue; if(matchesAny(data.m_test_suite, p->filters[3], 0, p->case_sensitive)) continue; if(!matchesAny(data.m_name, p->filters[4], 1, p->case_sensitive)) continue; if(matchesAny(data.m_name, p->filters[5], 0, p->case_sensitive)) continue; p->numTestsPassingFilters++; // do not execute the test if we are to only count the number of filter passing tests if(p->count) continue; // print the name of the test and don't execute it if(p->list_test_cases) { std::cout << Color::None << data.m_name << "\n"; continue; } // print the name of the test suite if not done already and don't execute it if(p->list_test_suites) { if((testSuitesPassingFilters.count(data.m_test_suite) == 0) && data.m_test_suite[0] != '\0') { std::cout << Color::None << data.m_test_suite << "\n"; testSuitesPassingFilters.insert(data.m_test_suite); p->numTestSuitesPassingFilters++; } continue; } // skip the test if it is not in the execution range if((p->last < p->numTestsPassingFilters && p->first <= p->last) || (p->first > p->numTestsPassingFilters)) continue; // execute the test if it passes all the filtering { p->currentTest = &data; bool failed = false; p->hasLoggedCurrentTestStart = false; p->numFailedAssertionsForCurrentTestcase = 0; p->subcasesPassed.clear(); double duration = 0; Timer timer; timer.start(); do { // if the start has been logged from a previous iteration of this loop if(p->hasLoggedCurrentTestStart) logTestEnd(); p->hasLoggedCurrentTestStart = false; // if logging successful tests - force the start log if(p->success) DOCTEST_LOG_START(std::cout); // reset the assertion state p->numAssertionsForCurrentTestcase = 0; p->hasCurrentTestFailed = false; // reset some of the fields for subcases (except for the set of fully passed ones) p->subcasesHasSkipped = false; p->subcasesCurrentLevel = 0; p->subcasesEnteredLevels.clear(); // reset stuff for logging with INFO() p->exceptionalContexts.clear(); // execute the test #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS try { #endif // DOCTEST_CONFIG_NO_EXCEPTIONS FatalConditionHandler fatalConditionHandler; // Handle signals data.m_test(); fatalConditionHandler.reset(); if(contextState->hasCurrentTestFailed) failed = true; #ifndef DOCTEST_CONFIG_NO_EXCEPTIONS } catch(const TestFailureException&) { failed = true; } catch(...) { DOCTEST_LOG_START(std::cout); logTestException(*contextState->currentTest, translateActiveException(), false); failed = true; } #endif // DOCTEST_CONFIG_NO_EXCEPTIONS p->numAssertions += p->numAssertionsForCurrentTestcase; // exit this loop if enough assertions have failed if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after) { p->subcasesHasSkipped = false; std::cout << Color::Red << "Aborting - too many failed asserts!\n"; } } while(p->subcasesHasSkipped == true); duration = timer.getElapsedSeconds(); if(Approx(p->currentTest->m_timeout).epsilon(DBL_EPSILON) != 0 && Approx(duration).epsilon(DBL_EPSILON) > p->currentTest->m_timeout) { failed = true; DOCTEST_LOG_START(std::cout); std::cout << Color::Red << "Test case exceeded time limit of " << std::setprecision(6) << std::fixed << p->currentTest->m_timeout << "!\n"; } if(p->duration) std::cout << Color::None << std::setprecision(6) << std::fixed << duration << " s: " << p->currentTest->m_name << "\n"; if(data.m_should_fail) { DOCTEST_LOG_START(std::cout); if(failed) std::cout << Color::Yellow << "Failed as expected so marking it as not failed\n"; else std::cout << Color::Red << "Should have failed but didn't! Marking it as failed!\n"; failed = !failed; } else if(failed && data.m_may_fail) { DOCTEST_LOG_START(std::cout); failed = false; std::cout << Color::Yellow << "Allowed to fail so marking it as not failed\n"; } else if(data.m_expected_failures > 0) { DOCTEST_LOG_START(std::cout); if(p->numFailedAssertionsForCurrentTestcase == data.m_expected_failures) { failed = false; std::cout << Color::Yellow << "Failed exactly " << data.m_expected_failures << " times as expected so marking it as not failed!\n"; } else { failed = true; std::cout << Color::Red << "Didn't fail exactly " << data.m_expected_failures << " times so marking it as failed!\n"; } } std::cout << Color::None; if(p->hasLoggedCurrentTestStart) logTestEnd(); if(failed) // if any subcase has failed - the whole test case has failed p->numFailed++; // stop executing tests if enough assertions have failed if(p->abort_after > 0 && p->numFailedAssertions >= p->abort_after) break; } } printSummary(std::cout); contextState = 0; if(p->numFailed && !p->no_exitcode) return EXIT_FAILURE; return EXIT_SUCCESS; } } // namespace doctest #endif // DOCTEST_CONFIG_DISABLE #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } #endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_MSVC_SUPPRESS_WARNING_POP DOCTEST_GCC_SUPPRESS_WARNING_POP #endif // DOCTEST_LIBRARY_IMPLEMENTATION #endif // DOCTEST_CONFIG_IMPLEMENT