├── deps ├── pprintpp │ ├── example │ │ ├── .gitignore │ │ ├── Makefile │ │ └── main.cpp │ ├── test │ │ ├── .gitignore │ │ ├── Makefile │ │ └── main.cpp │ ├── .travis.yml │ ├── LICENSE │ ├── Documentation.md │ ├── include │ │ ├── charlist.hpp │ │ ├── stl_symbols.hpp │ │ ├── typelist.hpp │ │ └── pprintpp.hpp │ └── README.md └── fmt │ ├── include │ └── fmt │ │ ├── posix.h │ │ ├── locale.h │ │ ├── ostream.h │ │ ├── args.h │ │ ├── xchar.h │ │ ├── os.h │ │ ├── printf.h │ │ └── compile.h │ ├── .clang-format │ ├── .gitignore │ ├── LICENSE.rst │ ├── src │ ├── fmt.cc │ ├── format.cc │ └── os.cc │ ├── .travis.yml │ └── README.rst ├── vcsetup.bat ├── scripts ├── bootstrap-linux-pprintpp.sh └── bootstrap-android-standalone.sh ├── .clang-format ├── meson.build ├── Makefile ├── azure-pipelines.yml ├── Makefile.pprintpp ├── sandbox ├── Makefile └── main.cc ├── Makefile.fmtlib ├── LICENSE ├── CMakeLists.txt ├── test └── main.cc ├── README.md └── src └── nanolog.cc /deps/pprintpp/example/.gitignore: -------------------------------------------------------------------------------- 1 | example 2 | -------------------------------------------------------------------------------- /deps/pprintpp/test/.gitignore: -------------------------------------------------------------------------------- 1 | pprintpp_test 2 | -------------------------------------------------------------------------------- /deps/fmt/include/fmt/posix.h: -------------------------------------------------------------------------------- 1 | #include "os.h" 2 | #warning "fmt/posix.h is deprecated; use fmt/os.h instead" 3 | -------------------------------------------------------------------------------- /vcsetup.bat: -------------------------------------------------------------------------------- 1 | rmdir /s /q build 2 | mkdir build 3 | 4 | "C:\Program Files\Meson\meson.exe" build --backend vs2017 5 | -------------------------------------------------------------------------------- /deps/fmt/include/fmt/locale.h: -------------------------------------------------------------------------------- 1 | #include "xchar.h" 2 | #warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead 3 | -------------------------------------------------------------------------------- /scripts/bootstrap-linux-pprintpp.sh: -------------------------------------------------------------------------------- 1 | rm -rf build 2 | mkdir build 3 | 4 | cd build && cmake -DNANOLOG_USE_PPRINTPP=On \ 5 | .. 6 | 7 | 8 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | IndentWidth: 2 4 | TabWidth: 2 5 | UseTab: Never 6 | BreakBeforeBraces: Attach 7 | Standard: Cpp11 8 | -------------------------------------------------------------------------------- /deps/pprintpp/example/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CXXFLAGS = -I../include -std=c++11 -O2 3 | 4 | default: example 5 | 6 | example: main.cpp 7 | $(CXX) $(CXXFLAGS) -o $@ $< 8 | 9 | clean: 10 | rm -rf example 11 | 12 | -------------------------------------------------------------------------------- /deps/pprintpp/test/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CXXFLAGS = -I../include -std=c++11 -O2 -Wfatal-errors -Wno-format-security 3 | 4 | default: pprintpp_test 5 | 6 | pprintpp_test: main.cpp 7 | $(CXX) $(CXXFLAGS) -o $@ $< 8 | 9 | clean: 10 | rm -rf pprintpp_test 11 | -------------------------------------------------------------------------------- /deps/fmt/.clang-format: -------------------------------------------------------------------------------- 1 | # Run manually to reformat a file: 2 | # clang-format -i --style=file 3 | Language: Cpp 4 | BasedOnStyle: Google 5 | IndentPPDirectives: AfterHash 6 | IndentCaseLabels: false 7 | AlwaysBreakTemplateDeclarations: false 8 | DerivePointerAlignment: false 9 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('nanolog', 'cpp', default_options : ['cpp_std=c++11']) 2 | 3 | thread_dep = dependency('threads') 4 | 5 | incdir = include_directories(['deps/pprintpp/include', 'include']) 6 | 7 | executable('run_test', ['src/nanolog.cc', 'test/main.cc'], include_directories : incdir, dependencies : thread_dep) 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Nanolog with internal backend. 2 | # 3 | CXXFLAGS=-g -O2 -Wall -Werror -std=c++11 -Wno-c++98-compat -Wno-c++98-compat-pedantic 4 | 5 | all: nanolog.o 6 | g++ $(CXXFLAGS) -o run_test -Iinclude test/main.cc nanolog.o -pthread 7 | 8 | nanolog.o: src/nanolog.cc 9 | g++ $(CXXFLAGS) -c -Iinclude src/nanolog.cc 10 | 11 | .PHONY: clean 12 | 13 | clean: 14 | rm nanolog.o run_test 15 | -------------------------------------------------------------------------------- /deps/pprintpp/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: cpp 4 | 5 | compiler: 6 | - gcc 7 | - clang 8 | 9 | install: 10 | - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi 11 | 12 | addons: 13 | apt: 14 | sources: 15 | - ubuntu-toolchain-r-test 16 | packages: 17 | - gcc-4.8 18 | - g++-4.8 19 | - clang 20 | 21 | script: 22 | - cd test 23 | - make 24 | - ./pprintpp_test 25 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | 4 | pool: 5 | vmImage: 'ubuntu-latest' 6 | 7 | steps: 8 | - script: | 9 | sudo apt-get install -y python3 python3-pip python3-setuptools python3-wheel ninja-build 10 | pip3 install --user meson 11 | displayName: 'setup' 12 | - script: | 13 | pwd 14 | PATH=$PATH:~/.local/bin 15 | meson builddir 16 | cd builddir 17 | ninja 18 | displayName: 'meson' 19 | 20 | -------------------------------------------------------------------------------- /Makefile.pprintpp: -------------------------------------------------------------------------------- 1 | # pprintpp backend 2 | # 3 | CXXFLAGS=-DNANOLOG_USE_PPRINTPP -g -O2 -Wall -Werror -std=c++11 -Wno-c++98-compat -Wno-c++98-compat-pedantic 4 | 5 | all: nanolog.o 6 | g++ $(CXXFLAGS) -o run_test -Ideps/pprintpp/include -Iinclude test/main.cc nanolog.o -pthread 7 | 8 | nanolog.o: src/nanolog.cc 9 | g++ $(CXXFLAGS) -c -Ideps/pprintpp/include -Iinclude src/nanolog.cc 10 | 11 | .PHONY: clean 12 | 13 | clean: 14 | rm nanolog.o run_test 15 | -------------------------------------------------------------------------------- /sandbox/Makefile: -------------------------------------------------------------------------------- 1 | CXX=clang++ 2 | CXXFLAGS=-g -O2 -std=c++11 -I../deps/fmt/include 3 | 4 | NANOLOG_CXXFLAGS=$(CXXFLAGS) 5 | 6 | all: os.o format.o 7 | $(CXX) $(NANOLOG_CXXFLAGS) -o run_test main.cc $? -pthread 8 | 9 | os.o: ../deps/fmt/src/os.cc 10 | $(CXX) $(CXXFLAGS) -c -Iinclude ../deps/fmt/src/os.cc 11 | 12 | format.o: ../deps/fmt/src/format.cc 13 | $(CXX) $(CXXFLAGS) -c -Iinclude ../deps/fmt/src/format.cc 14 | 15 | .PHONY: clean 16 | 17 | clean: 18 | rm -f os.o format.o run_test 19 | -------------------------------------------------------------------------------- /deps/fmt/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | 3 | *.iml 4 | .idea/ 5 | .externalNativeBuild/ 6 | .gradle/ 7 | gradle/ 8 | gradlew* 9 | local.properties 10 | build/ 11 | 12 | bin/ 13 | /_CPack_Packages 14 | /CMakeScripts 15 | /doc/doxyxml 16 | /doc/html 17 | virtualenv 18 | /Testing 19 | /install_manifest.txt 20 | *~ 21 | *.a 22 | *.so* 23 | *.xcodeproj 24 | *.zip 25 | cmake_install.cmake 26 | CPack*.cmake 27 | fmt-*.cmake 28 | CTestTestfile.cmake 29 | CMakeCache.txt 30 | CMakeFiles 31 | FMT.build 32 | Makefile 33 | run-msbuild.bat 34 | fmt.pc 35 | -------------------------------------------------------------------------------- /Makefile.fmtlib: -------------------------------------------------------------------------------- 1 | CXX=clang++ 2 | CXXFLAGS=-g -O2 -std=c++11 -Ideps/fmt/include 3 | 4 | NANOLOG_CXXFLAGS=$(CXXFLAGS) -Weverything -Werror -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -DNANOLOG_USE_FMTLIB 5 | 6 | all: nanolog.o os.o format.o 7 | $(CXX) $(NANOLOG_CXXFLAGS) -o run_test -Iinclude test/main.cc $? -pthread 8 | 9 | os.o: deps/fmt/src/os.cc 10 | $(CXX) $(CXXFLAGS) -c -Iinclude deps/fmt/src/os.cc 11 | 12 | format.o: deps/fmt/src/format.cc 13 | $(CXX) $(CXXFLAGS) -c -Iinclude deps/fmt/src/format.cc 14 | 15 | nanolog.o: src/nanolog.cc 16 | $(CXX) $(NANOLOG_CXXFLAGS) -c -Iinclude src/nanolog.cc 17 | 18 | .PHONY: clean 19 | 20 | clean: 21 | rm -f nanolog.o os.o format.o run_test 22 | -------------------------------------------------------------------------------- /scripts/bootstrap-android-standalone.sh: -------------------------------------------------------------------------------- 1 | ## Please edit android sdk/ndk/cmake path 2 | ANDROID_SDK_ROOT=$HOME/Android/Sdk/ 3 | #ANDROID_NDK_ROOT=$HOME/Android/Sdk/ndk-bundle 4 | ANDROID_NDK_ROOT=$HOME/Android/Sdk/ndk/21.0.6113669/ 5 | CMAKE_BIN=$ANDROID_SDK_ROOT/cmake/3.10.2.4988404/bin/cmake 6 | # CMake 3.7 or later required 7 | #CMAKE_BIN=cmake 8 | 9 | rm -rf build-android-standalone 10 | 11 | # For ninja build 12 | # -DCMAKE_MAKE_PROGRAM=$ANDROID_SDK_ROOT/cmake/3.6.4111459/bin/ninja \ 13 | 14 | mkdir build-android-standalone 15 | cd build-android-standalone && $CMAKE_BIN \ 16 | -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake \ 17 | -DANDROID_ABI=arm64-v8a \ 18 | -DANDROID_NATIVE_API_LEVEL=28 \ 19 | -DANDROID_STL=c++_shared \ 20 | -DNANOLOG_ANDROID_USE_STDIO=On \ 21 | .. 22 | 23 | 24 | -------------------------------------------------------------------------------- /deps/pprintpp/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jacek Galowicz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Light Transport Entertainment Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /deps/fmt/LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - present, Victor Zverovich 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | --- Optional exception to the license --- 23 | 24 | As an exception, if, as a result of your compiling your source code, portions 25 | of this Software are embedded into a machine-executable object form of such 26 | source code, you may redistribute such embedded portions in such object form 27 | without including the above copyright and permission notices. 28 | -------------------------------------------------------------------------------- /deps/pprintpp/example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main() 6 | { 7 | pprintf("{} hello {s}! {}\n", int(1), "world", 2.0) ; 8 | 9 | pprintf("This is \\{ how you mask } {s}.\n", "curly braces"); 10 | 11 | pprintf("Of course it's possible to add more formatting flags:\n" 12 | "{x}, {10x}, {#10x}, and {#010x} use the normal printf capabilities.\n", 13 | 0x123, 0x123, 0x123, 0x123); 14 | 15 | pprintf("{20}\n", 1.23456); 16 | 17 | pprintf("{35s} | Format str -> Result\n" 18 | "{35s} | ---------------------\n" 19 | "{35s} | \\{s} -> \"{s}\"\n" 20 | "{35s} | \\{10s} -> \"{10s}\"\n" 21 | "{35s} | \\{} -> \"{}\"\n" 22 | "{35s} | \\{x} -> \"{x}\"\n" 23 | "{35s} | \\{10} -> \"{10}\"\n" 24 | "{35s} | \\{10x} -> \"{10x}\"\n" 25 | "{35s} | \\{#10x} -> \"{#10x}\"\n" 26 | "{35s} | \\{#010x} -> \"{#010x}\"\n" 27 | "{35s} | \\{} -> \"{}\"\n" 28 | "{35s} | \\{10} -> \"{10}\"\n" 29 | "{35s} | \\{5.2} -> \"{5.2}\"\n", 30 | "Meaning", "---------------------", 31 | "String \"abc\"", "abc", 32 | "String \"abc\" + min width", "abc", 33 | "value 0x123, default", 0x123, 34 | "value 0x123, hex", 0x123, 35 | "minimum width", 0x123, 36 | "hex + min width", 0x123, 37 | "hex + min width + hex prefix", 0x123, 38 | "hex + min w. + hex prefix + 0-pad", 0x123, 39 | "FP", 12.345, 40 | "FP + min width", 12.34567890123456789, 41 | "FP + width + max precision", 12.34567890123456789 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /deps/pprintpp/Documentation.md: -------------------------------------------------------------------------------- 1 | # `pprintpp` Documentation 2 | 3 | ## How to use 4 | 5 | It's easy. Just add the library to your include path, and include ``. 6 | `pprintpp.hpp` defined the macro `pprintf`, which just depends on the original `printf`. 7 | 8 | ``` c++ 9 | #include 10 | #include 11 | 12 | int main() 13 | { 14 | pprintf("Hello {s}!, {} {} {}\n", "world", 123, 123ull, 1.23); 15 | } 16 | ``` 17 | 18 | ## "Advanced" Usage 19 | 20 | `pprintpp` tries to be as `printf` compatible as possible, but there are some pecularities: 21 | 22 | ### Adding Additional Format Directives 23 | 24 | `printf` historically supports indendation, hex printing of unsigneds, etc. via additional 25 | *optional* format specifiers between `%` and the actual type format character. 26 | 27 | `pprintpp` accepts these between the `{}` brackets. Examples: 28 | 29 | ``` c++ 30 | pprintf("Int with 10 characters indentation: {10}\n", 123); 31 | pprintf("Unsigned long long or whatever in hex notation: {#8x}\n", static_cast(0x123)); 32 | ``` 33 | 34 | ### Printing Strings 35 | 36 | It would easily be possible, to transform `pprintf("{}", "some string");` to `printf("%s", "some string");`. 37 | For safety reasons, i decided to *not* automatically print everything as a string, which looks like a character array. 38 | 39 | In order to print such a string, please put an "s" between the braces: `{s}`: `pprintf("{s}", "some string");`. 40 | 41 | ### Printing Actual `{` Braces 42 | 43 | Braces can be normally printed as before `pprintpp`, but the opening ones have to be masked in order to 44 | tell `pprintpp` to not interprete these as place holders: 45 | 46 | ``` c++ 47 | pprintf("These are braces: \\{}.\n"); 48 | pprintf("JSON document: \\{ "foo" : "bar", "value" : {} }\n", 123); 49 | ``` 50 | -------------------------------------------------------------------------------- /deps/pprintpp/test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define TEST(correct_str, ...) \ 7 | assert(!strcmp(AUTOFORMAT(__VA_ARGS__), correct_str)) 8 | 9 | int main() 10 | { 11 | TEST("", ""); 12 | 13 | TEST("%%", "%%"); 14 | TEST("%d %f", "{} %f", 123, 1.23f); 15 | TEST("%f %d", "%f {}", 1.23f, 123); 16 | 17 | TEST(" { ", " \\{ "); 18 | TEST("{}", "\\{}"); 19 | TEST(" { %d } ", " \\{ {} } ", 123); 20 | 21 | TEST("%p", "{}", nullptr); 22 | TEST("%p", "{}", reinterpret_cast(0)); 23 | 24 | // For safety reasons: 25 | // Only print strings as strings, if the user also writes {s} 26 | TEST("%p", "{}", "str"); 27 | TEST("%s", "{s}", "str"); 28 | 29 | TEST("%c", "{}", static_cast(123)); 30 | 31 | TEST("%d", "{}", static_cast(123)); 32 | TEST("%d", "{}", 123); 33 | TEST("%ld", "{}", 123l); 34 | TEST("%lld", "{}", 123ll); 35 | 36 | TEST("%u", "{}", 123u); 37 | TEST("%lu", "{}", 123ul); 38 | TEST("%llu", "{}", 123ull); 39 | 40 | TEST("%x", "{x}", 123u); 41 | TEST("%lx", "{x}", 123ul); 42 | TEST("%llx", "{x}", 123ull); 43 | 44 | TEST("%d", "{}", true); 45 | 46 | TEST("%f", "{}", 1.0f); 47 | TEST("%lf", "{}", 1.0); 48 | 49 | TEST("%10d", "{10}", 123); 50 | TEST("%10x", "{10x}", 123u); 51 | TEST("%#10x", "{#10x}", 123u); 52 | 53 | // Give the user hex if asked for explicitly. 54 | TEST("%x", "{x}", 123); 55 | TEST("%lx", "{x}", 123l); 56 | TEST("%llx", "{x}", 123ll); 57 | 58 | puts("Green, green, green! All tests passed.\n"); 59 | 60 | pprintf("{s} {} {1} {} {} {} {} {} {} {} {} {} {} {} {} {#x}\n", 61 | "1",2u,3.0,4.0f,5ull,'6',7ul,8,9,10,11,12,13,14,15,16u); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | set(TEST_TARGET "test_nanolog") 4 | 5 | project(${TEST_TARGET}) 6 | 7 | # default: Use internal backend(fast to compile, limited log formatting) 8 | option(NANOLOG_USE_FMTLIB "Use fmtlib(feature rich)" OFF) 9 | option(NANOLOG_USE_PPRINTPP "Use pprintpp backend(faster to compile than fmtlib, but has some limitation in log formattring)" OFF) 10 | option(NANOLOG_ANDROID_USE_STDIO "Print logs to stdout(For Android)" OFF) 11 | 12 | set(CMAKE_CXX_STANDARD 11) 13 | 14 | find_package(Threads REQUIRED) 15 | 16 | set(NANOLOG_TEST_SOURCES 17 | test/main.cc 18 | src/nanolog.cc 19 | ) 20 | 21 | if (NANOLOG_USE_FMTLIB) 22 | 23 | list(APPEND NANOLOG_TEST_SOURCES ${PROJECT_SOURCE_DIR}/deps/fmt/src/format.cc) 24 | 25 | if (NOT ANDROID) 26 | list(APPEND NANOLOG_TEST_SOURCES 27 | ${PROJECT_SOURCE_DIR}/deps/fmt/src/os.cc 28 | ) 29 | endif (NOT ANDROID) 30 | 31 | endif (NANOLOG_USE_FMTLIB) 32 | 33 | add_executable(${TEST_TARGET} 34 | ${NANOLOG_TEST_SOURCES} 35 | ) 36 | 37 | target_link_libraries(${TEST_TARGET} PUBLIC Threads::Threads) 38 | 39 | if (NANOLOG_USE_FMTLIB) 40 | target_compile_definitions(${TEST_TARGET} PRIVATE NANOLOG_USE_FMTLIB) 41 | target_include_directories(${TEST_TARGET} PRIVATE ${PROJECT_SOURCE_DIR}/include 42 | ${PROJECT_SOURCE_DIR}/deps/fmt/include) 43 | elseif (NANOLOG_USE_PPRINTPP) 44 | target_compile_definitions(${TEST_TARGET} PRIVATE NANOLOG_USE_PPRINTPP) 45 | target_include_directories(${TEST_TARGET} PRIVATE ${PROJECT_SOURCE_DIR}/include 46 | ${PROJECT_SOURCE_DIR}/deps/pprintpp/include) 47 | else () 48 | target_include_directories(${TEST_TARGET} PRIVATE ${PROJECT_SOURCE_DIR}/include 49 | ) 50 | endif (NANOLOG_USE_FMTLIB) 51 | 52 | if (ANDROID) 53 | if (NANOLOG_ANDROID_USE_STDIO) 54 | target_compile_definitions(${TEST_TARGET} PRIVATE NANOLOG_ANDROID_USE_STDIO) 55 | else (NANOLOG_ANDROID_USE_STDIO) 56 | target_link_libraries(${TEST_TARGET} PUBLIC log) 57 | endif (NANOLOG_ANDROID_USE_STDIO) 58 | endif (ANDROID) 59 | -------------------------------------------------------------------------------- /deps/fmt/src/fmt.cc: -------------------------------------------------------------------------------- 1 | module; 2 | #ifndef __cpp_modules 3 | # error Module not supported. 4 | #endif 5 | 6 | // put all implementation-provided headers into the global module fragment 7 | // to prevent attachment to this module 8 | #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) 9 | # define _CRT_SECURE_NO_WARNINGS 10 | #endif 11 | #if !defined(WIN32_LEAN_AND_MEAN) && defined(_WIN32) 12 | # define WIN32_LEAN_AND_MEAN 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #if _MSC_VER 47 | # include 48 | #endif 49 | #if defined __APPLE__ || defined(__FreeBSD__) 50 | # include 51 | #endif 52 | #if __has_include() 53 | # include 54 | #endif 55 | #if (__has_include() || defined(__APPLE__) || \ 56 | defined(__linux__)) && \ 57 | (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) 58 | # include 59 | # include 60 | # include 61 | # ifndef _WIN32 62 | # include 63 | # else 64 | # include 65 | # endif 66 | #endif 67 | #ifdef _WIN32 68 | # include 69 | #endif 70 | 71 | export module fmt; 72 | 73 | #define FMT_MODULE_EXPORT export 74 | #define FMT_MODULE_EXPORT_BEGIN export { 75 | #define FMT_MODULE_EXPORT_END } 76 | #define FMT_BEGIN_DETAIL_NAMESPACE \ 77 | } \ 78 | namespace detail { 79 | #define FMT_END_DETAIL_NAMESPACE \ 80 | } \ 81 | export { 82 | // all library-provided declarations and definitions 83 | // must be in the module purview to be exported 84 | #include "fmt/args.h" 85 | #include "fmt/chrono.h" 86 | #include "fmt/color.h" 87 | #include "fmt/compile.h" 88 | #include "fmt/format.h" 89 | #include "fmt/os.h" 90 | #include "fmt/printf.h" 91 | #include "fmt/xchar.h" 92 | 93 | // gcc doesn't yet implement private module fragments 94 | #if !FMT_GCC_VERSION 95 | module : private; 96 | #endif 97 | 98 | #include "format.cc" 99 | #include "os.cc" 100 | -------------------------------------------------------------------------------- /deps/pprintpp/include/charlist.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Jacek Galowicz 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | #pragma once 25 | 26 | 27 | #include "typelist.hpp" 28 | 29 | namespace charlist { 30 | 31 | using namespace typelist; 32 | 33 | template struct char_t { static const constexpr char value {val}; }; 34 | 35 | template 36 | struct char_tl; 37 | 38 | template 39 | struct char_tl { 40 | using type = typelist::tl, typename char_tl::type>; 41 | }; 42 | template 43 | struct char_tl { 44 | using type = typelist::tl, typelist::null_t>; 45 | }; 46 | 47 | template 48 | using char_tl_t = typename char_tl::type; 49 | 50 | template 51 | struct string_list; 52 | 53 | template 54 | struct string_list { 55 | using next_piece = typename string_list< 56 | Str, 57 | Pos + 1, 58 | Str::str()[Pos + 1] 59 | >::type; 60 | using type = typelist::tl, next_piece>; 61 | }; 62 | 63 | template 64 | struct string_list { 65 | using type = typelist::null_t; 66 | }; 67 | 68 | template 69 | using string_list_t = typename string_list::type; 70 | 71 | template 72 | struct tl_to_varlist; 73 | 74 | template 75 | struct tl_to_varlist, restlist>, chars...> 76 | : public tl_to_varlist 77 | { }; 78 | 79 | template <> 80 | struct tl_to_varlist { 81 | static const char * const str() { return ""; } 82 | }; 83 | template 84 | struct tl_to_varlist { 85 | using list = char_tl; 86 | 87 | static const char * const str() { 88 | static constexpr const char string[] = {chars..., '\0'}; 89 | return string; 90 | } 91 | }; 92 | 93 | } 94 | -------------------------------------------------------------------------------- /deps/pprintpp/include/stl_symbols.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 Jacek Galowicz 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | #pragma once 25 | 26 | #ifdef PPRINTPP_AVOID_STL 27 | #include 28 | #else 29 | #include 30 | #include 31 | #endif 32 | 33 | 34 | #ifndef PPRINTPP_AVOID_STL 35 | #endif 36 | #include "typelist.hpp" 37 | 38 | namespace pprintpp { 39 | 40 | 41 | #ifndef PPRINTPP_AVOID_STL 42 | 43 | using nullptr_t = std::nullptr_t; 44 | 45 | template 46 | using remove_cv_t = typename std::remove_cv::type; 47 | 48 | template 49 | using is_same = std::is_same; 50 | 51 | template 52 | using conditional = std::conditional; 53 | 54 | template 55 | using remove_ptr = std::remove_pointer; 56 | 57 | template 58 | using is_int_type = std::is_integral; 59 | 60 | #else 61 | 62 | using nullptr_t = decltype(nullptr); 63 | 64 | template 65 | struct remove_c { using type = T; }; 66 | template 67 | struct remove_c { using type = T; }; 68 | 69 | template 70 | struct remove_v { using type = T; }; 71 | template 72 | struct remove_v { using type = T; }; 73 | 74 | template 75 | using remove_cv_t = typename remove_v< 76 | typename remove_c::type 77 | >::type; 78 | 79 | template 80 | struct is_same { static constexpr bool value {false}; }; 81 | 82 | template 83 | struct is_same { static constexpr bool value {true}; }; 84 | 85 | template 86 | struct conditional { using type = A; }; 87 | 88 | template 89 | struct conditional { using type = B; }; 90 | 91 | template 92 | struct remove_ptr { using type = T; }; 93 | 94 | template 95 | struct remove_ptr { using type = T; }; 96 | 97 | template 98 | struct is_int_type { 99 | using ints = typelist::make_t; 100 | static constexpr bool value {typelist::contains::value}; 101 | }; 102 | 103 | #endif 104 | 105 | } 106 | -------------------------------------------------------------------------------- /deps/pprintpp/include/typelist.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Jacek Galowicz 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | #pragma once 25 | 26 | namespace typelist { 27 | 28 | struct null_t { 29 | using head = null_t; 30 | using tail = null_t; 31 | }; 32 | 33 | template 34 | struct tl 35 | { 36 | using head = T; 37 | using tail = U; 38 | }; 39 | 40 | template 41 | using head_t = typename TList::head; 42 | 43 | template 44 | using tail_t = typename TList::tail; 45 | 46 | 47 | template struct make; 48 | 49 | template 50 | struct make { using type = tl::type>; }; 51 | template <> 52 | struct make<> { using type = null_t; }; 53 | 54 | template 55 | using make_t = typename make::type; 56 | 57 | 58 | template 59 | struct append; 60 | template <> 61 | struct append { using type = null_t; }; 62 | template 63 | struct append { using type = make_t; }; 64 | template 65 | struct append> { using type = tl; }; 66 | template 67 | struct append, T> 68 | { using type = tl::type>; }; 69 | 70 | template 71 | using append_t = typename append::type; 72 | 73 | template 74 | struct contains; 75 | 76 | template 77 | struct contains { static constexpr bool value {false}; }; 78 | template 79 | struct contains, T> { static constexpr bool value {true}; }; 80 | template 81 | struct contains, T> : contains {}; 82 | 83 | 84 | template 85 | struct remove; 86 | 87 | template 88 | struct remove { using type = null_t; }; 89 | template 90 | struct remove, T> { using type = typename remove::type; }; 91 | template 92 | struct remove, T> { using type = tl::type>; }; 93 | 94 | template 95 | using remove_t = typename remove::type; 96 | 97 | 98 | template 99 | struct substitute; 100 | 101 | template 102 | struct substitute { using type = null_t; }; 103 | template 104 | struct substitute, T, TS> { using type = tl::type>; }; 105 | template 106 | struct substitute, T, TS> { using type = tl::type>; }; 107 | 108 | template 109 | using substitute_t = typename substitute::type; 110 | 111 | } // namespace tl 112 | -------------------------------------------------------------------------------- /deps/fmt/.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | dist: trusty 3 | sudo: false 4 | 5 | os: linux 6 | 7 | git: 8 | depth: 1 9 | 10 | env: 11 | global: 12 | - secure: |- 13 | a1eovNn4uol9won7ghr67eD3/59oeESN+G9bWE+ecI1V6yRseG9whniGhIpC/YfMW/Qz5I 14 | 5sxSmFjaw9bxCISNwUIrL1O5x2AmRYTnFcXk4dFsUvlZg+WeF/aKyBYCNRM8C2ndbBmtAO 15 | o1F2EwFbiso0EmtzhAPs19ujiVxkLn4= 16 | 17 | matrix: 18 | include: 19 | # Documentation 20 | - env: BUILD=Doc 21 | sudo: required 22 | # g++ 6 on Linux with C++14 23 | - env: COMPILER=g++-6 BUILD=Debug STANDARD=14 24 | compiler: gcc 25 | addons: 26 | apt: 27 | update: true 28 | sources: 29 | - ubuntu-toolchain-r-test 30 | packages: 31 | - g++-6 32 | - env: COMPILER=g++-6 BUILD=Release STANDARD=14 33 | compiler: gcc 34 | addons: 35 | apt: 36 | update: true 37 | sources: 38 | - ubuntu-toolchain-r-test 39 | packages: 40 | - g++-6 41 | # g++ 8 on Linux with C++17 42 | - env: COMPILER=g++-8 BUILD=Debug STANDARD=17 43 | compiler: gcc 44 | addons: 45 | apt: 46 | update: true 47 | sources: 48 | - ubuntu-toolchain-r-test 49 | packages: 50 | - g++-8 51 | - env: COMPILER=g++-8 BUILD=Release STANDARD=17 52 | compiler: gcc 53 | addons: 54 | apt: 55 | update: true 56 | sources: 57 | - ubuntu-toolchain-r-test 58 | packages: 59 | - g++-8 60 | 61 | # Apple clang on OS X with C++14 62 | - env: BUILD=Debug STANDARD=14 63 | compiler: clang 64 | os: osx 65 | - env: BUILD=Release STANDARD=14 66 | compiler: clang 67 | os: osx 68 | # clang 6.0 on Linux with C++14 (builds the fuzzers as well) 69 | - env: COMPILER=clang++-6.0 BUILD=Debug STANDARD=14 ENABLE_FUZZING=1 70 | compiler: clang 71 | addons: 72 | apt: 73 | update: true 74 | packages: 75 | - clang-6.0 76 | sources: 77 | - ubuntu-toolchain-r-test 78 | - llvm-toolchain-trusty 79 | - llvm-toolchain-trusty-6.0 80 | # clang 4.0 on Linux with C++14 81 | - env: COMPILER=clang++-4.0 BUILD=Debug STANDARD=11 82 | compiler: clang 83 | addons: 84 | apt: 85 | update: true 86 | packages: 87 | - clang-4.0 88 | sources: 89 | - ubuntu-toolchain-r-test 90 | - llvm-toolchain-trusty 91 | - llvm-toolchain-trusty-4.0 92 | # g++ 4.8 on Linux with C++11 93 | - env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11 94 | compiler: gcc 95 | - name: Android NDK (Gradle) 96 | language: android 97 | addons: 98 | apt: 99 | update: true 100 | sources: 101 | - ubuntu-toolchain-r-test 102 | packages: 103 | - ninja-build 104 | - curl 105 | - tree 106 | android: 107 | components: 108 | - tools 109 | - platform-tools 110 | - android-25 # 7.0 111 | - android-27 # 8.1 112 | - android-28 # 9.0 113 | - build-tools-28.0.3 114 | before_install: 115 | # Install Gradle from https://sdkman.io/ 116 | - curl -s "https://get.sdkman.io" | bash > /dev/null 117 | - source "$HOME/.sdkman/bin/sdkman-init.sh" 118 | - sdk version 119 | - sdk install gradle 120 | - sdk use gradle 121 | - gradle --version 122 | install: 123 | # Accept SDK Licenses + Install NDK 124 | - yes | sdkmanager --update > /dev/null 2>&1 125 | - sdkmanager ndk-bundle > /dev/null 2>&1 126 | before_script: 127 | - pushd ./support 128 | script: 129 | - gradle clean 130 | - gradle assemble 131 | after_success: 132 | - popd; 133 | - tree ./libs 134 | 135 | before_script: 136 | - if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi 137 | - if [[ "${BUILD}" != "Doc" ]]; then ${CXX} --version; fi 138 | 139 | script: 140 | - support/travis-build.py 141 | -------------------------------------------------------------------------------- /test/main.cc: -------------------------------------------------------------------------------- 1 | #include "nanolog.hh" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // Workaround for macro expansionin MSVC 8 | // https://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly 9 | #define EXPAND(x) x 10 | #define MY_ASSERT(cond, ...) \ 11 | do { \ 12 | if (!(cond)) { \ 13 | EXPAND(NANOLOG_FATAL(__VA_ARGS__)); \ 14 | abort(); \ 15 | } \ 16 | } while(0) 17 | 18 | struct vec3 19 | { 20 | float x, y, z; 21 | }; 22 | 23 | std::ostream &operator<<(std::ostream& ofs, const vec3 &v) { 24 | ofs << "(" << v.x << ", " << v.y << ", " << v.z << ")"; 25 | 26 | return ofs; 27 | } 28 | 29 | static void logger1() 30 | { 31 | int n = 100; 32 | 33 | for (int i = 0; i < n; i++) { 34 | NANOLOG_TRACE_N(10, "This log will be printed up to 10 times. i = {}", i); 35 | //NANOLOG_INFO("thread1 i = {}", i); 36 | #if !defined(NANOLOG_USE_PPRINTPP) 37 | // print log each 1000 msecs(1 second) 38 | NANOLOG_ERROR_MSEC(1000, "thread1 i = {}", i); 39 | #endif 40 | std::this_thread::sleep_for(std::chrono::milliseconds(33)); 41 | } 42 | } 43 | 44 | static void logger2() 45 | { 46 | int n = 120; 47 | 48 | for (int i = 0; i < n; i++) { 49 | #if !defined(NANOLOG_USE_PPRINTPP) 50 | // print log each 2000 msecs(2 second) 51 | NANOLOG_INFO_MSEC(2000, "thread2 i = {}", i); 52 | #else 53 | NANOLOG_INFO("thread2 i = {}", i); 54 | #endif 55 | std::this_thread::sleep_for(std::chrono::milliseconds(27)); 56 | } 57 | } 58 | 59 | int main(int argc, char **argv) 60 | { 61 | (void)argc; 62 | (void)argv; 63 | 64 | nanolog::set_level(nanolog::kTRACE); 65 | nanolog::set_apptag("myapp"); 66 | 67 | MY_ASSERT(argc < 16, "argc must be less than 16, but got {}", "muda"); 68 | 69 | NANOLOG_INFO("{}", "start"); 70 | std::string hello; 71 | NANOLOG_INFO("{}", hello.c_str()); 72 | 73 | float x = 3.13f; 74 | NANOLOG_INFO("{}", x); 75 | 76 | #if defined(NANOLOG_USE_FMTLIB) 77 | NANOLOG_TRACE("argc {}, argv {}", argc, argv[0]); 78 | NANOLOG_DEBUG("argc {}, argv {}", argc, argv[0]); 79 | NANOLOG_INFO("argc {}, argv {}", argc, argv[0]); 80 | NANOLOG_WARN("argc {}, argv {}", argc, argv[0]); 81 | NANOLOG_ERROR("argc {}, argv {}", argc, argv[0]); 82 | //NANOLOG_FATAL("argc {}, argv {}", argc, argv[0]); // This may result in program abort() 83 | 84 | nanolog::set_color(false); 85 | NANOLOG_WARN("argc {}, argv {}", argc, argv[0]); 86 | NANOLOG_ERROR("argc {}, argv {}", argc, argv[0]); 87 | #elif defined(NANOLOG_USE_PPRINTPP) 88 | NANOLOG_TRACE("argc {}, argv {s}", argc, argv[0]); 89 | NANOLOG_DEBUG("argc {}, argv {s}", argc, argv[0]); 90 | NANOLOG_INFO("argc {}, argv {s}", argc, argv[0]); 91 | NANOLOG_WARN("argc {}, argv {s}", argc, argv[0]); 92 | NANOLOG_ERROR("argc {}, argv {s}", argc, argv[0]); 93 | //NANOLOG_FATAL("argc {}, argv {}", argc, argv[0]); // This may result in program abort() 94 | 95 | nanolog::set_color(false); 96 | NANOLOG_WARN("argc {}, argv {s}", argc, argv[0]); 97 | NANOLOG_ERROR("argc {}, argv {s}", argc, argv[0]); 98 | #else 99 | NANOLOG_TRACE("argc {}, argv {}", argc, argv[0]); 100 | NANOLOG_DEBUG("argc {}, argv {}", argc, argv[0]); 101 | NANOLOG_INFO("argc {}, argv {}", argc, argv[0]); 102 | NANOLOG_WARN("argc {}, argv {}", argc, argv[0]); 103 | NANOLOG_ERROR("argc {}, argv {}", argc, argv[0]); 104 | //NANOLOG_FATAL("argc {}, argv {}", argc, argv[0]); // This may result in program abort() 105 | 106 | nanolog::set_color(false); 107 | NANOLOG_WARN("argc {}, argv {}", argc, argv[0]); 108 | NANOLOG_ERROR("argc {}, argv {}", argc, argv[0]); 109 | 110 | vec3 myvec; 111 | myvec.x = 1.2f; 112 | myvec.y = 1.5f; 113 | myvec.z = 2.4f; 114 | // Use `operator<<` to format vec3 value. 115 | NANOLOG_INFO("vec {}", myvec); 116 | 117 | // internal can use `std::string` argument 118 | NANOLOG_INFO("hello: {}", hello); 119 | 120 | // zero-arg log is supported. 121 | NANOLOG_INFO("world"); 122 | #endif 123 | 124 | { 125 | std::thread th1(logger1); 126 | std::thread th2(logger2); 127 | 128 | th1.join(); 129 | th2.join(); 130 | } 131 | 132 | nanolog::set_printtime(false); 133 | { 134 | std::thread th1(logger1); 135 | std::thread th2(logger2); 136 | 137 | th1.join(); 138 | th2.join(); 139 | } 140 | 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /deps/fmt/include/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include 12 | 13 | #include "format.h" 14 | 15 | FMT_BEGIN_NAMESPACE 16 | 17 | template class basic_printf_context; 18 | 19 | namespace detail { 20 | 21 | // Checks if T has a user-defined operator<<. 22 | template 23 | class is_streamable { 24 | private: 25 | template 26 | static auto test(int) 27 | -> bool_constant&>() 28 | << std::declval()) != 0>; 29 | 30 | template static auto test(...) -> std::false_type; 31 | 32 | using result = decltype(test(0)); 33 | 34 | public: 35 | is_streamable() = default; 36 | 37 | static const bool value = result::value; 38 | }; 39 | 40 | // Formatting of built-in types and arrays is intentionally disabled because 41 | // it's handled by standard (non-ostream) formatters. 42 | template 43 | struct is_streamable< 44 | T, Char, 45 | enable_if_t< 46 | std::is_arithmetic::value || std::is_array::value || 47 | std::is_pointer::value || std::is_same::value || 48 | std::is_same>::value || 49 | std::is_same>::value || 50 | (std::is_convertible::value && !std::is_enum::value)>> 51 | : std::false_type {}; 52 | 53 | // Write the content of buf to os. 54 | // It is a separate function rather than a part of vprint to simplify testing. 55 | template 56 | void write_buffer(std::basic_ostream& os, buffer& buf) { 57 | const Char* buf_data = buf.data(); 58 | using unsigned_streamsize = std::make_unsigned::type; 59 | unsigned_streamsize size = buf.size(); 60 | unsigned_streamsize max_size = to_unsigned(max_value()); 61 | do { 62 | unsigned_streamsize n = size <= max_size ? size : max_size; 63 | os.write(buf_data, static_cast(n)); 64 | buf_data += n; 65 | size -= n; 66 | } while (size != 0); 67 | } 68 | 69 | template 70 | void format_value(buffer& buf, const T& value, 71 | locale_ref loc = locale_ref()) { 72 | auto&& format_buf = formatbuf>(buf); 73 | auto&& output = std::basic_ostream(&format_buf); 74 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) 75 | if (loc) output.imbue(loc.get()); 76 | #endif 77 | output << value; 78 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 79 | buf.try_resize(buf.size()); 80 | } 81 | 82 | // Formats an object of type T that has an overloaded ostream operator<<. 83 | template 84 | struct fallback_formatter::value>> 85 | : private formatter, Char> { 86 | using formatter, Char>::parse; 87 | 88 | template 89 | auto format(const T& value, basic_format_context& ctx) 90 | -> OutputIt { 91 | auto buffer = basic_memory_buffer(); 92 | format_value(buffer, value, ctx.locale()); 93 | return formatter, Char>::format( 94 | {buffer.data(), buffer.size()}, ctx); 95 | } 96 | 97 | // DEPRECATED! 98 | template 99 | auto format(const T& value, basic_printf_context& ctx) 100 | -> OutputIt { 101 | auto buffer = basic_memory_buffer(); 102 | format_value(buffer, value, ctx.locale()); 103 | return std::copy(buffer.begin(), buffer.end(), ctx.out()); 104 | } 105 | }; 106 | } // namespace detail 107 | 108 | FMT_MODULE_EXPORT 109 | template 110 | void vprint(std::basic_ostream& os, basic_string_view format_str, 111 | basic_format_args>> args) { 112 | auto buffer = basic_memory_buffer(); 113 | detail::vformat_to(buffer, format_str, args); 114 | detail::write_buffer(os, buffer); 115 | } 116 | 117 | /** 118 | \rst 119 | Prints formatted data to the stream *os*. 120 | 121 | **Example**:: 122 | 123 | fmt::print(cerr, "Don't {}!", "panic"); 124 | \endrst 125 | */ 126 | FMT_MODULE_EXPORT 127 | template ::value, char_t>> 129 | void print(std::basic_ostream& os, const S& format_str, Args&&... args) { 130 | vprint(os, to_string_view(format_str), 131 | fmt::make_args_checked(format_str, args...)); 132 | } 133 | FMT_END_NAMESPACE 134 | 135 | #endif // FMT_OSTREAM_H_ 136 | -------------------------------------------------------------------------------- /deps/fmt/src/format.cc: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #include "fmt/format-inl.h" 9 | 10 | FMT_BEGIN_NAMESPACE 11 | namespace detail { 12 | 13 | // DEPRECATED! 14 | template struct basic_data { 15 | FMT_API static constexpr const char digits[100][2] = { 16 | {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, 17 | {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, 18 | {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, 19 | {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, 20 | {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, 21 | {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, 22 | {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, 23 | {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, 24 | {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, 25 | {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, 26 | {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, 27 | {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, 28 | {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, 29 | {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, 30 | {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, 31 | {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, 32 | {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; 33 | FMT_API static constexpr const char hex_digits[] = "0123456789abcdef"; 34 | FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '}; 35 | FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, 36 | 0}; 37 | FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, 38 | 0}; 39 | FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 40 | 0x1000000u | ' '}; 41 | }; 42 | 43 | #ifdef FMT_SHARED 44 | // Required for -flto, -fivisibility=hidden and -shared to work 45 | extern template struct basic_data; 46 | #endif 47 | 48 | #if __cplusplus < 201703L 49 | // DEPRECATED! These are here only for ABI compatiblity. 50 | template constexpr const char basic_data::digits[][2]; 51 | template constexpr const char basic_data::hex_digits[]; 52 | template constexpr const char basic_data::signs[]; 53 | template constexpr const char basic_data::left_padding_shifts[]; 54 | template 55 | constexpr const char basic_data::right_padding_shifts[]; 56 | template constexpr const unsigned basic_data::prefixes[]; 57 | #endif 58 | 59 | template 60 | int format_float(char* buf, std::size_t size, const char* format, int precision, 61 | T value) { 62 | #ifdef FMT_FUZZ 63 | if (precision > 100000) 64 | throw std::runtime_error( 65 | "fuzz mode - avoid large allocation inside snprintf"); 66 | #endif 67 | // Suppress the warning about nonliteral format string. 68 | int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; 69 | return precision < 0 ? snprintf_ptr(buf, size, format, value) 70 | : snprintf_ptr(buf, size, format, precision, value); 71 | } 72 | 73 | template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(float x) 74 | FMT_NOEXCEPT; 75 | template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(double x) 76 | FMT_NOEXCEPT; 77 | } // namespace detail 78 | 79 | // Workaround a bug in MSVC2013 that prevents instantiation of format_float. 80 | int (*instantiate_format_float)(double, int, detail::float_specs, 81 | detail::buffer&) = detail::format_float; 82 | 83 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 84 | template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); 85 | template FMT_API std::locale detail::locale_ref::get() const; 86 | #endif 87 | 88 | // Explicit instantiations for char. 89 | 90 | template FMT_API auto detail::thousands_sep_impl(locale_ref) 91 | -> thousands_sep_result; 92 | template FMT_API char detail::decimal_point_impl(locale_ref); 93 | 94 | template FMT_API void detail::buffer::append(const char*, const char*); 95 | 96 | // DEPRECATED! 97 | // There is no correspondent extern template in format.h because of 98 | // incompatibility between clang and gcc (#2377). 99 | template FMT_API void detail::vformat_to( 100 | detail::buffer&, string_view, 101 | basic_format_args, detail::locale_ref); 102 | 103 | template FMT_API int detail::snprintf_float(double, int, detail::float_specs, 104 | detail::buffer&); 105 | template FMT_API int detail::snprintf_float(long double, int, 106 | detail::float_specs, 107 | detail::buffer&); 108 | template FMT_API int detail::format_float(double, int, detail::float_specs, 109 | detail::buffer&); 110 | template FMT_API int detail::format_float(long double, int, detail::float_specs, 111 | detail::buffer&); 112 | 113 | // Explicit instantiations for wchar_t. 114 | 115 | template FMT_API auto detail::thousands_sep_impl(locale_ref) 116 | -> thousands_sep_result; 117 | template FMT_API wchar_t detail::decimal_point_impl(locale_ref); 118 | 119 | template FMT_API void detail::buffer::append(const wchar_t*, 120 | const wchar_t*); 121 | 122 | template struct detail::basic_data; 123 | 124 | FMT_END_NAMESPACE 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nanolog, Nanoscale logging library in C++11 2 | 3 | nanolog is a simple, portable and nanoscale logging library in C++11. 4 | nanolog uses `pprintpp`(default) or `fmtlib` for supporting Python-like format string(`{}`). 5 | 6 | ## Features 7 | 8 | * Faster compilation time. nanolog itself uses very small amount of template code. 9 | * Default uses `internal`(nodep) backend(no dependency except for STL) 10 | * `pprintpp` and `fmtlib` backend provided. 11 | * Using `pprintpp` backend is rougly 3x ~ 10x faster compile than `fmtlib` backend(when `-O2` optimization enabled) 12 | * But `pprintpp` has many limitations(see the `Limitation` section in the below) 13 | * fmtlib backend is also provided. 14 | * Thread-safe. nanolog logging is a thread-safe. 15 | 16 | nanolog is good if you want faster C++11 compile time but don't need absolute performance of logging output. 17 | e.g. graphics, raytracing, machine learning application. An application where text logging is important for data loading/saving, but non-text debugging is primarily used in its primary task. 18 | 19 | ## Supported platform 20 | 21 | * [x] Linux 64bit 22 | * [x] macOS 23 | * [x] Windows 10 64bit 24 | * [x] Android arm64va8 25 | * [x] Raspberry Pi(AARCH64) 26 | * [ ] iOS(Should work) 27 | * [ ] RISC-V(should work) 28 | 29 | ## Supported compilers 30 | 31 | * C++11 compiler 32 | * Clang 3.9+ 33 | * GCC 5.4+ 34 | * Visual Studio 2017+ 35 | * Other C++11 compatible compilers 36 | 37 | ## Build 38 | 39 | Just copy `nanolog/include`, `nanolog/src` and optionally `deps/pprintpp` or `deps/fmtlib` to your project folder and add .cc and .hh for your project. 40 | 41 | There are three backends 42 | 43 | * internal(default) 44 | * Minimal, fast-to-compile, run time log fotmatting. No denendecy. 45 | * No compile-time type check for format string and arguments(for faster compilation) 46 | * `NANOLOG_USE_PPRINTPP` pprintpp: 47 | * Small, compile time type check and log formatting. 48 | * `NANOLOG_USE_FMTLIB` fmt 49 | * Feature ritch, compile time type check and log formatting. 50 | 51 | ### Internal backend(nodep) 52 | 53 | Internal backend only uses STL, no dependent libraries required to add your project. 54 | 55 | Internal backend only supports `{}` as a format specifier. 56 | Internally, `{}` is replaced by a string generated by `operator<<` for each argument. 57 | 58 | You can define your own print routine of custom class by overriding `operator<<`. 59 | 60 | #### Features 61 | 62 | Internal backend has some featues not present in pprintpp and fmtlib backend. These features are convienient for graphics apps and ML apps. 63 | 64 | * `LOG_***_N`: Limit printing the log up to `N` times. 65 | * `LOG_***_MSEC`: Suppress log output within the specified milliseconds after the time of last log message was printed. This helps to avoid the flood of outputting logs for rare events. 66 | 67 | Both `LOG_***_N` and `LOG_***_MSEC` take a global lock thus these logging should be only placed to the place where the event happens in less frequent manner(e.g. print NaN warning when the pixel value is NaN) 68 | 69 | 70 | ### pprintpp backend 71 | 72 | Add `nanolog/src/nanolog.cc` to your project. 73 | (pprintpp is a header only library, so no extra .cc required) 74 | 75 | There are some limitations in pprintpp backend. 76 | 77 | #### Limitation 78 | 79 | Single argument would give compilation error(due to C++'s variadic macro specification) when you raise the compiler warning level. 80 | 81 | ``` 82 | NANOLOG_INFO("hello"); // => error 83 | 84 | NANOLOG_INFO("{}", "hello"); // => ok 85 | ``` 86 | 87 | Cannot specify `std::string` directly. 88 | 89 | ``` 90 | std::string str = "hello"; 91 | NANOLOG_INFO("{}", str); // => error 92 | 93 | NANOLOG_INFO("{}", str.c_str()); // => ok 94 | ``` 95 | 96 | ### fmtlib backend 97 | 98 | Add `nanolog/src/nanolog.cc`, `deps/fmt/src/format.cc` and `deps/fmt/src/posix.cc` to your project. 99 | 100 | ### Build test on Visual Studio 2017 101 | 102 | Install meson. 103 | (Assume meson will be installed to `C:\Program Files\Meson\meson.exe`) 104 | 105 | Edit path in `vcsetup.bat` if required. 106 | 107 | Open `Developer Command Prompt for Visual Studio 2017` from Windows menu. 108 | 109 | ``` 110 | > cd $nanolog 111 | > vcsetup.bat 112 | ``` 113 | 114 | VS solution file will be generated at `build` directory. 115 | 116 | ## Note on logging API 117 | 118 | New line(`\n`) character is prepended to each log message. 119 | So you don't need to include new line character to format string. 120 | 121 | ## Example 122 | 123 | ``` 124 | #include "nanolog.hh" 125 | 126 | // logging method 127 | NANOLOG_TRACE("The answer is {}", 42); 128 | NANOLOG_DEBUG("The answer is {}", 42); 129 | NANOLOG_INFO("The answer is {}", 42); 130 | NANOLOG_WARN("The answer is {}", 42); 131 | NANOLOG_ERROR("The answer is {}", 42); 132 | NANOLOG_FATAL("The answer is {}", 42); 133 | 134 | // set log level 135 | nanolog::set_level(nanolog::kDEBUG); 136 | 137 | // set app tag(useful for Android) 138 | nanolog::set_apptag("myapp"); 139 | 140 | // print time(disabled by default for Android logcat) 141 | nanolog::set_printtime(false); 142 | 143 | // set colored output(default = true. NOTE: to see colored output on Android logcat, you may need to see it on Android Studio) 144 | nanolog::set_color(false); 145 | NANOLOG_ERROR("The answer is {}", 42); 146 | ``` 147 | 148 | ## Compile options 149 | 150 | ### `NANOLOG_NO_EXCEPTION_AT_FATAL` 151 | 152 | Do not throw an exception after fatal message output. 153 | 154 | ### fmtlib backend 155 | 156 | Use `NANOLOG_USE_FMTLIB` compile flags to use fmtlib backend. 157 | 158 | #### `NANOLOG_NO_FMT_INCLUDE` 159 | 160 | Do not include `fmt` header files in `nanolog.h`. 161 | This macro is useful when you want to include your own fmt files. 162 | (fmtlib is required to build nanolog anyway) 163 | 164 | Example usage is: 165 | 166 | ``` 167 | #include "your_own/fmt/core.h" 168 | 169 | #define NANOLOG_USE_FMTLIB 170 | #define NANOLOG_NO_FMT_INCLUDE 171 | #include "nanolog.hh" 172 | ``` 173 | 174 | 175 | ### `NANOLOG_ANDROID_USE_STDIO` 176 | 177 | Print log to stdout for Android platform. 178 | In default, log messages are sent to Android log system(`adb logcat` to see it). 179 | When `NANOLOG_ANDROID_USE_STDIO` is defined, log messeages are sent to `stdout`. 180 | This flags is useful when you build standalone native Android program(`./a.out` style app. for example unit tester) 181 | 182 | ## TODO 183 | 184 | * [ ] Multiple logging instances. 185 | * [ ] Custom stream output(e.g. to file) 186 | * [ ] Write example for Android, iOS 187 | * [ ] Emoji and better wide character support. 188 | 189 | ## License 190 | 191 | nanolog is licensed under MIT license. 192 | 193 | ## Third party 194 | 195 | * pprintpp: MIT or simplified BSD(2-clause BSD?) license. https://github.com/wolever/pprintpp 196 | * fmtlib : 2-clause BSD license. https://github.com/fmtlib/fmt 197 | -------------------------------------------------------------------------------- /deps/pprintpp/include/pprintpp.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2016 Jacek Galowicz 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | #pragma once 25 | 26 | #include "stl_symbols.hpp" 27 | #include "charlist.hpp" 28 | 29 | namespace pprintpp { 30 | 31 | template struct always_false { static constexpr bool value {false}; }; 32 | 33 | using namespace typelist; 34 | using namespace charlist; 35 | 36 | template struct type2fmt; 37 | 38 | template <> struct type2fmt { using type = char_tl_t<'c'>; }; 39 | template <> struct type2fmt { using type = char_tl_t<'d'>; }; 40 | template <> struct type2fmt { using type = char_tl_t<'d'>; }; 41 | template <> struct type2fmt { using type = char_tl_t<'l', 'd'>; }; 42 | template <> struct type2fmt { using type = char_tl_t<'l', 'l', 'd'>; }; 43 | template <> struct type2fmt { using type = char_tl_t<'u'>; }; 44 | template <> struct type2fmt { using type = char_tl_t<'u'>; }; 45 | template <> struct type2fmt { using type = char_tl_t<'u'>; }; 46 | template <> struct type2fmt { using type = char_tl_t<'l', 'u'>; }; 47 | template <> struct type2fmt { using type = char_tl_t<'l', 'l', 'u'>; }; 48 | 49 | template <> struct type2fmt { using type = char_tl_t<'d'>; }; 50 | 51 | template <> struct type2fmt { using type = char_tl_t<'f'>; }; 52 | template <> struct type2fmt { using type = char_tl_t<'l', 'f'>; }; 53 | 54 | template <> struct type2fmt { using type = char_tl_t<'p'>; }; 55 | template struct type2fmt { using type = char_tl_t<'p'>; }; 56 | 57 | template 58 | struct format_str { 59 | using raw_T = remove_cv_t; 60 | static constexpr bool s_fmt {contains>::value}; 61 | static constexpr bool is_str {is_same::type>>::value}; 63 | 64 | static constexpr bool is_int {is_int_type::value}; 65 | static constexpr bool has_x {contains>::value}; 66 | 67 | using raw_fmt = typename type2fmt::type; 68 | 69 | using uint_x_fmt = typename conditional, char_t<'x'>>, 72 | char_t<'u'>, char_t<'x'>>, 73 | raw_fmt 74 | >::type; 75 | 76 | using fmt_type = typename conditional, char_t<'s'>>, 78 | uint_x_fmt 79 | >::type; 80 | 81 | using filtered_fl = remove_t>, char_t<'s'>>; 82 | 83 | using type = append_t; 84 | }; 85 | 86 | template 87 | struct find_brace; 88 | 89 | template 90 | struct find_brace, InList>, OutList, 1> { 91 | using before = OutList; 92 | using after = InList; 93 | }; 94 | 95 | template 96 | struct find_brace, InList>, OutList, N> 97 | : public find_brace>, N> 98 | { 99 | static_assert(C != '{', "Found nested braces: {...{...}...}!" 100 | " Maybe you want to mask the outer one?"); 101 | }; 102 | 103 | template 104 | struct find_brace 105 | { 106 | static_assert(N + 1 == N, "Missing } after {."); 107 | }; 108 | 109 | template 110 | struct autoformat; 111 | 112 | template <> 113 | struct autoformat { using type = null_t; }; 114 | 115 | template 116 | struct autoformat { 117 | using type = null_t; 118 | static_assert(always_false::value, "There are more vars than format tokens!"); 119 | }; 120 | 121 | template 122 | struct autoformat, tl, SL>>, TL> 123 | { 124 | using type = tl, tl, typename autoformat::type>>; 125 | }; 126 | 127 | template 128 | struct autoformat, SL>, tl> 129 | { 130 | using type = tl, typename autoformat::type>; 131 | }; 132 | 133 | template 134 | struct autoformat, tl, SL>>, TL> 135 | { 136 | using type = tl, typename autoformat::type>; 137 | }; 138 | 139 | template 140 | struct autoformat, SL>, TL> 141 | { 142 | using other_brace = find_brace; 143 | using format_block = typename other_brace::before; 144 | using rest_str = typename other_brace::after; 145 | 146 | static_assert(!is_same::value, "There are more {} than arguments to print"); 147 | using T = typename TL::head; 148 | using fmt_str = typename format_str::type; 149 | 150 | using type = tl, 151 | append_t::type>>; 152 | }; 153 | 154 | template 155 | struct autoformat, TL> { 156 | using type = tl::type>; 157 | }; 158 | 159 | 160 | template 161 | using autoformat_t = 162 | tl_to_varlist< 163 | typename autoformat, PtList>::type 164 | >; 165 | 166 | template 167 | make_t tie_types(Ts...); 168 | 169 | #define AUTOFORMAT(s, ...) \ 170 | ({ \ 171 | struct strprov { static constexpr const char * const str() { return s; } }; \ 172 | using paramtypes = decltype(pprintpp::tie_types(__VA_ARGS__)); \ 173 | using af = pprintpp::autoformat_t; \ 174 | af::str(); \ 175 | }) 176 | 177 | #define pprintf(s, ...) printf(AUTOFORMAT(s, ## __VA_ARGS__), ## __VA_ARGS__); 178 | 179 | } 180 | -------------------------------------------------------------------------------- /sandbox/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | //#include 12 | 13 | void func(...) { 14 | printf("aaa\n"); 15 | } 16 | 17 | #define COMMON_CODE(n) \ 18 | bool reached{false}; \ 19 | static int64_t counter = 0; \ 20 | static std::mutex _mtx; \ 21 | _mtx.lock(); \ 22 | counter++; \ 23 | if (counter > (n)) { reached = true; } \ 24 | _mtx.unlock(); 25 | 26 | #define BORA(s, ...) { \ 27 | func(s, __VA_ARGS__); \ 28 | } 29 | 30 | std::mutex mtx; 31 | 32 | // key = 33 | std::map, int64_t> g_countmap; 34 | 35 | static std::vector split_string(const std::string &input, const std::string &delimiter) { 36 | std::istringstream ss(input); 37 | std::string token; 38 | std::string::const_iterator it; 39 | 40 | std::vector result; 41 | 42 | while(std::getline(ss, token, *(it = delimiter.begin()))) { 43 | while(*(++it)) ss.get(); 44 | //std::cout << token << " " << '\n'; 45 | 46 | result.push_back(token); 47 | } 48 | 49 | return result; 50 | } 51 | 52 | class Msg 53 | { 54 | public: 55 | Msg(const char *fname, const char *funname, int line, int64_t max_count_ = -1) : _fname(fname), _funname(funname), _line(line), max_count(max_count_) { 56 | //std::cout << "line = " << _line << "\n"; 57 | } 58 | 59 | void log_arg() noexcept { 60 | } 61 | 62 | template 63 | void log_arg(T head, Args ... rest) noexcept { 64 | std::stringstream ss; 65 | ss << head; 66 | arg_strs.push_back(ss.str()); 67 | 68 | log_arg(rest...); 69 | } 70 | 71 | void fmt(const char *msg) { 72 | 73 | _msg = msg; 74 | } 75 | 76 | void flush() { 77 | 78 | // global lock 79 | std::lock_guard lock(mtx); 80 | 81 | std::string str(_msg); 82 | 83 | // find the number of occurences of `{}` 84 | // https://stackoverflow.com/questions/4034750/find-all-a-substrings-occurrences-and-locations 85 | std::vector positions; 86 | size_t pos = str.find("{}", 0); 87 | while(pos != std::string::npos) 88 | { 89 | positions.push_back(pos); 90 | pos = str.find("{}" ,pos+1); 91 | } 92 | 93 | //std::cout << "positions.size: " << positions.size() << "\n"; 94 | 95 | std::vector toks = split_string(_msg, "{}"); 96 | //std::cout << "ntoks = " << toks.size() << "\n"; 97 | //for (auto &tok : toks) { 98 | // std::cout << "tok: " << tok << "\n"; 99 | //} 100 | 101 | //for (auto &arg : arg_strs) { 102 | // std::cout << "arg: " << arg << "\n"; 103 | //} 104 | 105 | if (positions.size() != arg_strs.size()) { 106 | std::cerr << "argument mismatch!" << "\n"; 107 | // Argument mismatch 108 | return; 109 | } 110 | 111 | // replace string from the end to the beginning so that we don't need to adjust 112 | // the location in `positions`. 113 | size_t n = positions.size(); 114 | for (size_t i = 0; i < n; i++) { 115 | str.replace(positions[n - i - 1], /* strlen("{}") */2, arg_strs[n - i - 1]); 116 | } 117 | 118 | std::cout << str << "\n"; 119 | 120 | } 121 | 122 | std::string _fname; 123 | std::string _funname; 124 | int _line; 125 | std::string _msg; 126 | 127 | Msg(Msg &&other) { 128 | std::cout << "rval ctor\n"; 129 | } 130 | 131 | private: 132 | Msg() = delete; 133 | Msg(const Msg &other) = delete; 134 | Msg &operator=(const Msg &other) = delete; 135 | 136 | std::vector arg_strs; 137 | 138 | int64_t count{0}; 139 | int64_t max_count{-1}; 140 | }; 141 | 142 | #define MYFUN(s, ...) do { \ 143 | struct strprv { \ 144 | static constexpr const char *str() { return s; }; \ 145 | }; \ 146 | Msg msg(__FILE__, __func__, __LINE__); msg.fmt(strprv::str()); \ 147 | msg.log_arg(__VA_ARGS__); \ 148 | msg.flush(); \ 149 | } while(0) 150 | 151 | // Only print the log `n` times. 152 | #define MYFUN_N(n, s, ...) do { \ 153 | COMMON_CODE(n); \ 154 | struct strprv { \ 155 | static constexpr const char *str() { return s; }; \ 156 | }; \ 157 | if (!reached) { \ 158 | Msg msg(__FILE__, __func__, __LINE__); msg.fmt(strprv::str()); \ 159 | msg.log_arg(__VA_ARGS__); \ 160 | msg.flush(); \ 161 | } \ 162 | } while(0) 163 | 164 | // 165 | // Do not prit log within `msec` milliseconds after the last time of this log was printed. 166 | // 167 | 168 | #define MYFUN_MS(msecs, s, ...) do { \ 169 | bool suppressed{false}; \ 170 | static int64_t nsuppressed{0}; \ 171 | static std::chrono::time_point last_time(std::chrono::system_clock::from_time_t({})); \ 172 | static std::mutex _mtx; \ 173 | _mtx.lock(); \ 174 | auto curr = std::chrono::system_clock::now(); \ 175 | auto elapsed = std::chrono::duration_cast(curr - last_time).count(); \ 176 | if (elapsed < msecs) { suppressed = true ; nsuppressed++; } else { last_time = curr; } \ 177 | struct strprv { \ 178 | static constexpr const char *str() { return s; }; \ 179 | }; \ 180 | if (!suppressed) { \ 181 | std::string suppstr = "(Suppressed " + std::to_string(nsuppressed) + " messages)"; \ 182 | Msg suppmsg(__FILE__, __func__, __LINE__); suppmsg.fmt(suppstr.c_str()); \ 183 | suppmsg.flush(); \ 184 | nsuppressed = 0; \ 185 | Msg msg(__FILE__, __func__, __LINE__); msg.fmt(strprv::str()); \ 186 | msg.log_arg(__VA_ARGS__); \ 187 | msg.flush(); \ 188 | } \ 189 | /* TODO(LTE): Narrow critical section */ \ 190 | _mtx.unlock(); \ 191 | } while(0) 192 | 193 | //inline int log_all() noexcept { 194 | // std::cout << "hello\n"; 195 | // return 0; 196 | //} 197 | // 198 | //template 199 | //int log(T target) noexcept { 200 | // std::cout << "log: " << target << "\n"; 201 | // 202 | // return 0; 203 | //} 204 | // 205 | //template 206 | //inline void log_all(T target, Args ... rest) noexcept { 207 | // log(target); 208 | // log_all(rest...); 209 | //} 210 | // 211 | 212 | void print_n(int id) { 213 | MYFUN_N(3, "print_n(3): {} th id", id); 214 | } 215 | 216 | void print_msec(int wait) { 217 | std::this_thread::sleep_for(std::chrono::milliseconds(wait)); 218 | MYFUN_MS(1000, "print_n(3): wait {}", wait); 219 | } 220 | 221 | int main(int argc, char **argv) 222 | { 223 | //log_all(1, 0.2, "foo", true); 224 | //std::string s = fmt::format("{}", argc); 225 | 226 | //std::cout << s << "\n"; 227 | { 228 | struct strprv { 229 | static constexpr const char *str() { return "muda"; }; 230 | }; 231 | } 232 | 233 | MYFUN("{}aa"); 234 | MYFUN("aa{}", 1); 235 | MYFUN("{} aa", 1); 236 | MYFUN("aa{}, {} bora", 3, "gogo"); 237 | 238 | for (int i = 0; i < 10; i++) { 239 | //MYFUN_N(3, "this log is printed only three times"); 240 | } 241 | 242 | { 243 | std::thread threads[10]; 244 | // spawn 10 threads: 245 | for (int i=0; i<10; ++i) { 246 | threads[i] = std::thread(print_n,i+1); 247 | } 248 | for (auto& th : threads) th.join(); 249 | } 250 | 251 | // spawn 1000 threads: 252 | { 253 | std::thread threads[1000]; 254 | for (int i=0; i<1000; ++i) { 255 | threads[i] = std::thread(print_msec, (i+1) * 10); 256 | } 257 | for (auto& th : threads) th.join(); 258 | } 259 | 260 | return 0; 261 | } 262 | -------------------------------------------------------------------------------- /deps/fmt/include/fmt/args.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - dynamic format arguments 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_ARGS_H_ 9 | #define FMT_ARGS_H_ 10 | 11 | #include // std::reference_wrapper 12 | #include // std::unique_ptr 13 | #include 14 | 15 | #include "core.h" 16 | 17 | FMT_BEGIN_NAMESPACE 18 | 19 | namespace detail { 20 | 21 | template struct is_reference_wrapper : std::false_type {}; 22 | template 23 | struct is_reference_wrapper> : std::true_type {}; 24 | 25 | template const T& unwrap(const T& v) { return v; } 26 | template const T& unwrap(const std::reference_wrapper& v) { 27 | return static_cast(v); 28 | } 29 | 30 | class dynamic_arg_list { 31 | // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for 32 | // templates it doesn't complain about inability to deduce single translation 33 | // unit for placing vtable. So storage_node_base is made a fake template. 34 | template struct node { 35 | virtual ~node() = default; 36 | std::unique_ptr> next; 37 | }; 38 | 39 | template struct typed_node : node<> { 40 | T value; 41 | 42 | template 43 | FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} 44 | 45 | template 46 | FMT_CONSTEXPR typed_node(const basic_string_view& arg) 47 | : value(arg.data(), arg.size()) {} 48 | }; 49 | 50 | std::unique_ptr> head_; 51 | 52 | public: 53 | template const T& push(const Arg& arg) { 54 | auto new_node = std::unique_ptr>(new typed_node(arg)); 55 | auto& value = new_node->value; 56 | new_node->next = std::move(head_); 57 | head_ = std::move(new_node); 58 | return value; 59 | } 60 | }; 61 | } // namespace detail 62 | 63 | /** 64 | \rst 65 | A dynamic version of `fmt::format_arg_store`. 66 | It's equipped with a storage to potentially temporary objects which lifetimes 67 | could be shorter than the format arguments object. 68 | 69 | It can be implicitly converted into `~fmt::basic_format_args` for passing 70 | into type-erased formatting functions such as `~fmt::vformat`. 71 | \endrst 72 | */ 73 | template 74 | class dynamic_format_arg_store 75 | #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 76 | // Workaround a GCC template argument substitution bug. 77 | : public basic_format_args 78 | #endif 79 | { 80 | private: 81 | using char_type = typename Context::char_type; 82 | 83 | template struct need_copy { 84 | static constexpr detail::type mapped_type = 85 | detail::mapped_type_constant::value; 86 | 87 | enum { 88 | value = !(detail::is_reference_wrapper::value || 89 | std::is_same>::value || 90 | std::is_same>::value || 91 | (mapped_type != detail::type::cstring_type && 92 | mapped_type != detail::type::string_type && 93 | mapped_type != detail::type::custom_type)) 94 | }; 95 | }; 96 | 97 | template 98 | using stored_type = conditional_t::value && 99 | !has_formatter::value && 100 | !detail::is_reference_wrapper::value, 101 | std::basic_string, T>; 102 | 103 | // Storage of basic_format_arg must be contiguous. 104 | std::vector> data_; 105 | std::vector> named_info_; 106 | 107 | // Storage of arguments not fitting into basic_format_arg must grow 108 | // without relocation because items in data_ refer to it. 109 | detail::dynamic_arg_list dynamic_args_; 110 | 111 | friend class basic_format_args; 112 | 113 | unsigned long long get_types() const { 114 | return detail::is_unpacked_bit | data_.size() | 115 | (named_info_.empty() 116 | ? 0ULL 117 | : static_cast(detail::has_named_args_bit)); 118 | } 119 | 120 | const basic_format_arg* data() const { 121 | return named_info_.empty() ? data_.data() : data_.data() + 1; 122 | } 123 | 124 | template void emplace_arg(const T& arg) { 125 | data_.emplace_back(detail::make_arg(arg)); 126 | } 127 | 128 | template 129 | void emplace_arg(const detail::named_arg& arg) { 130 | if (named_info_.empty()) { 131 | constexpr const detail::named_arg_info* zero_ptr{nullptr}; 132 | data_.insert(data_.begin(), {zero_ptr, 0}); 133 | } 134 | data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); 135 | auto pop_one = [](std::vector>* data) { 136 | data->pop_back(); 137 | }; 138 | std::unique_ptr>, decltype(pop_one)> 139 | guard{&data_, pop_one}; 140 | named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); 141 | data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; 142 | guard.release(); 143 | } 144 | 145 | public: 146 | constexpr dynamic_format_arg_store() = default; 147 | 148 | constexpr dynamic_format_arg_store( 149 | const dynamic_format_arg_store& store) 150 | : 151 | #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 152 | basic_format_args(), 153 | #endif 154 | data_(store.data_), 155 | named_info_(store.named_info_) { 156 | } 157 | 158 | /** 159 | \rst 160 | Adds an argument into the dynamic store for later passing to a formatting 161 | function. 162 | 163 | Note that custom types and string types (but not string views) are copied 164 | into the store dynamically allocating memory if necessary. 165 | 166 | **Example**:: 167 | 168 | fmt::dynamic_format_arg_store store; 169 | store.push_back(42); 170 | store.push_back("abc"); 171 | store.push_back(1.5f); 172 | std::string result = fmt::vformat("{} and {} and {}", store); 173 | \endrst 174 | */ 175 | template void push_back(const T& arg) { 176 | if (detail::const_check(need_copy::value)) 177 | emplace_arg(dynamic_args_.push>(arg)); 178 | else 179 | emplace_arg(detail::unwrap(arg)); 180 | } 181 | 182 | /** 183 | \rst 184 | Adds a reference to the argument into the dynamic store for later passing to 185 | a formatting function. 186 | 187 | **Example**:: 188 | 189 | fmt::dynamic_format_arg_store store; 190 | char band[] = "Rolling Stones"; 191 | store.push_back(std::cref(band)); 192 | band[9] = 'c'; // Changing str affects the output. 193 | std::string result = fmt::vformat("{}", store); 194 | // result == "Rolling Scones" 195 | \endrst 196 | */ 197 | template void push_back(std::reference_wrapper arg) { 198 | static_assert( 199 | need_copy::value, 200 | "objects of built-in types and string views are always copied"); 201 | emplace_arg(arg.get()); 202 | } 203 | 204 | /** 205 | Adds named argument into the dynamic store for later passing to a formatting 206 | function. ``std::reference_wrapper`` is supported to avoid copying of the 207 | argument. The name is always copied into the store. 208 | */ 209 | template 210 | void push_back(const detail::named_arg& arg) { 211 | const char_type* arg_name = 212 | dynamic_args_.push>(arg.name).c_str(); 213 | if (detail::const_check(need_copy::value)) { 214 | emplace_arg( 215 | fmt::arg(arg_name, dynamic_args_.push>(arg.value))); 216 | } else { 217 | emplace_arg(fmt::arg(arg_name, arg.value)); 218 | } 219 | } 220 | 221 | /** Erase all elements from the store */ 222 | void clear() { 223 | data_.clear(); 224 | named_info_.clear(); 225 | dynamic_args_ = detail::dynamic_arg_list(); 226 | } 227 | 228 | /** 229 | \rst 230 | Reserves space to store at least *new_cap* arguments including 231 | *new_cap_named* named arguments. 232 | \endrst 233 | */ 234 | void reserve(size_t new_cap, size_t new_cap_named) { 235 | FMT_ASSERT(new_cap >= new_cap_named, 236 | "Set of arguments includes set of named arguments"); 237 | data_.reserve(new_cap); 238 | named_info_.reserve(new_cap_named); 239 | } 240 | }; 241 | 242 | FMT_END_NAMESPACE 243 | 244 | #endif // FMT_ARGS_H_ 245 | -------------------------------------------------------------------------------- /deps/fmt/include/fmt/xchar.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional wchar_t and exotic character support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_XCHAR_H_ 9 | #define FMT_XCHAR_H_ 10 | 11 | #include 12 | #include 13 | 14 | #include "format.h" 15 | 16 | FMT_BEGIN_NAMESPACE 17 | namespace detail { 18 | template 19 | using is_exotic_char = bool_constant::value>; 20 | } 21 | 22 | FMT_MODULE_EXPORT_BEGIN 23 | 24 | using wstring_view = basic_string_view; 25 | using wformat_parse_context = basic_format_parse_context; 26 | using wformat_context = buffer_context; 27 | using wformat_args = basic_format_args; 28 | using wmemory_buffer = basic_memory_buffer; 29 | 30 | #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 31 | // Workaround broken conversion on older gcc. 32 | template using wformat_string = wstring_view; 33 | #else 34 | template 35 | using wformat_string = basic_format_string...>; 36 | #endif 37 | 38 | template <> struct is_char : std::true_type {}; 39 | template <> struct is_char : std::true_type {}; 40 | template <> struct is_char : std::true_type {}; 41 | template <> struct is_char : std::true_type {}; 42 | 43 | template 44 | constexpr format_arg_store make_wformat_args( 45 | const Args&... args) { 46 | return {args...}; 47 | } 48 | 49 | inline namespace literals { 50 | constexpr auto operator"" _format(const wchar_t* s, size_t n) 51 | -> detail::udl_formatter { 52 | return {{s, n}}; 53 | } 54 | 55 | #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 56 | constexpr detail::udl_arg operator"" _a(const wchar_t* s, size_t) { 57 | return {s}; 58 | } 59 | #endif 60 | } // namespace literals 61 | 62 | template 63 | auto join(It begin, Sentinel end, wstring_view sep) 64 | -> join_view { 65 | return {begin, end, sep}; 66 | } 67 | 68 | template 69 | auto join(Range&& range, wstring_view sep) 70 | -> join_view, detail::sentinel_t, 71 | wchar_t> { 72 | return join(std::begin(range), std::end(range), sep); 73 | } 74 | 75 | template 76 | auto join(std::initializer_list list, wstring_view sep) 77 | -> join_view { 78 | return join(std::begin(list), std::end(list), sep); 79 | } 80 | 81 | template ::value)> 82 | auto vformat(basic_string_view format_str, 83 | basic_format_args>> args) 84 | -> std::basic_string { 85 | basic_memory_buffer buffer; 86 | detail::vformat_to(buffer, format_str, args); 87 | return to_string(buffer); 88 | } 89 | 90 | // Pass char_t as a default template parameter instead of using 91 | // std::basic_string> to reduce the symbol size. 92 | template , 93 | FMT_ENABLE_IF(!std::is_same::value)> 94 | auto format(const S& format_str, Args&&... args) -> std::basic_string { 95 | const auto& vargs = fmt::make_args_checked(format_str, args...); 96 | return vformat(to_string_view(format_str), vargs); 97 | } 98 | 99 | template , 100 | FMT_ENABLE_IF(detail::is_locale::value&& 101 | detail::is_exotic_char::value)> 102 | inline auto vformat( 103 | const Locale& loc, const S& format_str, 104 | basic_format_args>> args) 105 | -> std::basic_string { 106 | return detail::vformat(loc, to_string_view(format_str), args); 107 | } 108 | 109 | template , 111 | FMT_ENABLE_IF(detail::is_locale::value&& 112 | detail::is_exotic_char::value)> 113 | inline auto format(const Locale& loc, const S& format_str, Args&&... args) 114 | -> std::basic_string { 115 | return detail::vformat(loc, to_string_view(format_str), 116 | fmt::make_args_checked(format_str, args...)); 117 | } 118 | 119 | template , 120 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 121 | detail::is_exotic_char::value)> 122 | auto vformat_to(OutputIt out, const S& format_str, 123 | basic_format_args>> args) 124 | -> OutputIt { 125 | auto&& buf = detail::get_buffer(out); 126 | detail::vformat_to(buf, to_string_view(format_str), args); 127 | return detail::get_iterator(buf); 128 | } 129 | 130 | template , 132 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 133 | detail::is_exotic_char::value)> 134 | inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { 135 | const auto& vargs = fmt::make_args_checked(fmt, args...); 136 | return vformat_to(out, to_string_view(fmt), vargs); 137 | } 138 | 139 | template ::value)> 141 | FMT_DEPRECATED auto format_to(basic_memory_buffer& buf, 142 | const S& format_str, Args&&... args) -> 143 | typename buffer_context::iterator { 144 | const auto& vargs = fmt::make_args_checked(format_str, args...); 145 | detail::vformat_to(buf, to_string_view(format_str), vargs, {}); 146 | return detail::buffer_appender(buf); 147 | } 148 | 149 | template , 151 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 152 | detail::is_locale::value&& 153 | detail::is_exotic_char::value)> 154 | inline auto vformat_to( 155 | OutputIt out, const Locale& loc, const S& format_str, 156 | basic_format_args>> args) -> OutputIt { 157 | auto&& buf = detail::get_buffer(out); 158 | vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); 159 | return detail::get_iterator(buf); 160 | } 161 | 162 | template < 163 | typename OutputIt, typename Locale, typename S, typename... Args, 164 | typename Char = char_t, 165 | bool enable = detail::is_output_iterator::value&& 166 | detail::is_locale::value&& detail::is_exotic_char::value> 167 | inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, 168 | Args&&... args) -> 169 | typename std::enable_if::type { 170 | const auto& vargs = fmt::make_args_checked(format_str, args...); 171 | return vformat_to(out, loc, to_string_view(format_str), vargs); 172 | } 173 | 174 | template ::value&& 176 | detail::is_exotic_char::value)> 177 | inline auto vformat_to_n( 178 | OutputIt out, size_t n, basic_string_view format_str, 179 | basic_format_args>> args) 180 | -> format_to_n_result { 181 | detail::iterator_buffer buf(out, 182 | n); 183 | detail::vformat_to(buf, format_str, args); 184 | return {buf.out(), buf.count()}; 185 | } 186 | 187 | template , 189 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 190 | detail::is_exotic_char::value)> 191 | inline auto format_to_n(OutputIt out, size_t n, const S& fmt, 192 | const Args&... args) -> format_to_n_result { 193 | const auto& vargs = fmt::make_args_checked(fmt, args...); 194 | return vformat_to_n(out, n, to_string_view(fmt), vargs); 195 | } 196 | 197 | template , 198 | FMT_ENABLE_IF(detail::is_exotic_char::value)> 199 | inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { 200 | detail::counting_buffer buf; 201 | const auto& vargs = fmt::make_args_checked(fmt, args...); 202 | detail::vformat_to(buf, to_string_view(fmt), vargs); 203 | return buf.count(); 204 | } 205 | 206 | inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { 207 | wmemory_buffer buffer; 208 | detail::vformat_to(buffer, fmt, args); 209 | buffer.push_back(L'\0'); 210 | if (std::fputws(buffer.data(), f) == -1) 211 | FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); 212 | } 213 | 214 | inline void vprint(wstring_view fmt, wformat_args args) { 215 | vprint(stdout, fmt, args); 216 | } 217 | 218 | template 219 | void print(std::FILE* f, wformat_string fmt, T&&... args) { 220 | return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); 221 | } 222 | 223 | template void print(wformat_string fmt, T&&... args) { 224 | return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); 225 | } 226 | 227 | /** 228 | Converts *value* to ``std::wstring`` using the default format for type *T*. 229 | */ 230 | template inline auto to_wstring(const T& value) -> std::wstring { 231 | return format(FMT_STRING(L"{}"), value); 232 | } 233 | FMT_MODULE_EXPORT_END 234 | FMT_END_NAMESPACE 235 | 236 | #endif // FMT_XCHAR_H_ 237 | -------------------------------------------------------------------------------- /deps/pprintpp/README.md: -------------------------------------------------------------------------------- 1 | # Python style printf for C++ 2 | 3 | [![Build Status](https://travis-ci.org/tfc/pprintpp.svg?branch=master)](https://travis-ci.org/tfc/pprintpp) (GCC and Clang) 4 | 5 | ## What is `pprintpp`? 6 | 7 | The acronym stands for "Python style print for C plus plus". 8 | 9 | `pprintpp` is a **header-only** C++ library which aims to make `printf` use safe and easy. 10 | It is a *pure compile time library* and will add **no overhead** to the runtime of your programs. 11 | 12 | This library is for everyone who uses C++ but sticks to printf-like functions (like `printf`, `fprintf`, `sprintf`, `snprintf`, etc...). 13 | `pprintpp` adds a typesafe adapter on top of those functions by preprocessing strings to the format printf and its friends are expecting. 14 | Apart from the preformatted string, *no other symbols are added to the resulting binary*. 15 | This means that this library produces **no runtime code** at all, which distinguishes it from libraries like [fmtlib](https://github.com/fmtlib/fmt) (There has been some controversy in the comparison with `fmt` - look into the FAQ in this document on that matter please). 16 | 17 | ## Dependencies 18 | 19 | The library does only depend on **C++11** (or higher) and the **STL** (`` and ``). 20 | 21 | The STL dependency can easily get rid of by reimplementing some type traits. This way `pprintpp` can be used in hardcore baremetal environments (where it already has been in use actually). 22 | 23 | ## Example 24 | 25 | When using `printf`, the programmer has to choose the right types in the format string. 26 | ``` C++ 27 | printf("An int %d, a float %f, a string %s\n", 123, 7.89, "abc"); 28 | ``` 29 | If this format string is wrong, the programm will misformat something in the best case. 30 | In the worst case, the program might even crash. 31 | 32 | The python style print library allows for the following: 33 | ``` C++ 34 | pprintf("An int {}, a float {}, a string {s}\n", 123, 7.89, "abc"); 35 | ``` 36 | The types are chosen *automatically* **at compile time**. 37 | This is both safe *and* convenient. 38 | 39 | > Note the `{s}` in the format string: It is a safety detail of the library to require an additional "s" in the format string to print `char*` types really as strings. Otherwise, every `char*` would be printed as string, although it might be some non-null-terminated buffer. 40 | 41 | 42 | The assembly generated from the simple program... 43 | ``` c++ 44 | int main() 45 | { 46 | pprintf("{} hello {s}! {}\n", 1, "world", 2); 47 | } 48 | ``` 49 | 50 | ...shows that this library comes with *no* runtime overhead: 51 | ``` 52 | bash $ objdump -d example 53 | ... 54 | 0000000000400450
: 55 | 400450: 48 83 ec 08 sub $0x8,%rsp 56 | 400454: 41 b8 02 00 00 00 mov $0x2,%r8d 57 | 40045a: b9 04 06 40 00 mov $0x400604,%ecx # <-- "world" 58 | 40045f: ba 01 00 00 00 mov $0x1,%edx 59 | 400464: be 10 06 40 00 mov $0x400610,%esi # <-- "%d hello %s! %d" 60 | 400469: bf 01 00 00 00 mov $0x1,%edi 61 | 40046e: 31 c0 xor %eax,%eax 62 | 400470: e8 bb ff ff ff callq 400430 <__printf_chk@plt> 63 | 400475: 31 c0 xor %eax,%eax 64 | 400477: 48 83 c4 08 add $0x8,%rsp 65 | 40047b: c3 retq 66 | ... 67 | ``` 68 | 69 | Dumping the read-only data section of the binary shows the `printf` format string. 70 | It looks as if the programmer had directly written the printf line without ever having used `pprintpp`: 71 | ``` 72 | bash $ objdump -s -j .rodata example 73 | ... 74 | Contents of section .rodata: 75 | 400600 01000200 776f726c 64000000 00000000 ....world....... 76 | 400610 25642068 656c6c6f 20257321 2025640a %d hello %s! %d. 77 | 400620 00 . 78 | ``` 79 | 80 | ## More Examples 81 | 82 | In `printf`, in order to e.g. add a minimum width to a number as in `printf("%10d", 123);`, one needs to combine format specifiers (`10`) with conversion letters (`d`). 83 | 84 | In `pprintpp`, this is easily possible, too: You would just write `{10}` to get the same effect. The library picks the format specifiers from between the curly braces and merges them with the correct conversion letter that it deduces from the type of the actual expression. 85 | This way you get `%10d` for integers and `%10f` for floating point variables while using`{10}` for both. 86 | Just look up the format specifiers in the documentation of your `printf` implementation and use them. 87 | 88 | This output is from the file [`example/main.cpp`](example/main.cpp): 89 | 90 | ``` bash 91 | Meaning | Format str -> Result 92 | --------------------- | --------------------- 93 | String "abc" | {s} -> "abc" 94 | String "abc" + min width | {10s} -> " abc" 95 | value 0x123, default | {} -> "291" 96 | value 0x123, hex | {x} -> "123" 97 | minimum width | {10} -> " 291" 98 | hex + min width | {10x} -> " 123" 99 | hex + min width + hex prefix | {#10x} -> " 0x123" 100 | hex + min w. + hex prefix + 0-pad | {#010x} -> "0x00000123" 101 | FP | {} -> "12.345000" 102 | FP + min width | {10} -> " 12.345679" 103 | FP + width + max precision | {5.2} -> "12.35" 104 | ``` 105 | 106 | ## Printf Compatibility 107 | 108 | `pprintpp` will transform a tuple `(format_str, [type list])` to `printf_compatible_format_string`. 109 | 110 | That means that it can be used with any `printf`-like function. You just need to define a macro, like for example, these ones for `printf` and `snprintf`: 111 | 112 | ``` c++ 113 | #define pprintf(fmtstr, ...) printf(AUTOFORMAT(fmtstr, ## __VA_ARGS), ## __VA_ARGS__) 114 | #define psnprintf(outbuf, len, fmtstr, ...) \ 115 | snprintf(outbuf, len, AUTOFORMAT(fmtstr, ## __VA_ARGS__), ## __VA_ARGS__) 116 | ``` 117 | 118 | > Unfortunately, it is not possible to express this detail without macros in C++. However, the rest of the library was designed without macros at all. 119 | 120 | Embedded projects that introduce their own logging/tracing functions which accept `printf`-style format string, will also profit from this library. 121 | 122 | ## FAQ 123 | 124 | ### Why `printf`, when there is stream style printing in C++? 125 | 126 | Yes, stream style printing is type safe, and from a features perspective clearly superior to `printf`. 127 | However, in some projects, C++ is used without streams. 128 | This library was designed to help out developers of such projects with some type safety and comfort. 129 | 130 | An older version of the library even contained code which rewrote the parts which come from the STL, so it doesn't even depend on the STL. 131 | These can be reimplemented, if someone wishes to use this on some hardcore baremetal project where no STL is available. 132 | It's just that no one asked for that reimplementation, yet. 133 | 134 | ### I am pretty happy with `fmtlib`. Why `pprintpp`? 135 | 136 | Those two libs have similar purposes but completely different approaches. `pprintpp` is just a *compile-time frontend* that adds type safety and comfort to *existing* `printf`-style functions. `fmtlib` is a complete string formatting and printing library that produces actual code and acts as a complete substitute for `printf` and the output/printing part of C++ streams. 137 | 138 | ### Is `pprintpp` faster than `fmtlib`? 139 | 140 | Yes and no: `fmtlib` has its own (highly optimized and elegant) code for formatting strings which has demonstrably high performance. When you compare the performance of `pprintpp` and `fmtlib`, you actually compare the performance of `printf` and `fmtlib`, because `pprintpp` adds no code to your binary. 141 | 142 | Depending on the `printf` implementation you use, you are faster or slower than `fmtlib`. 143 | 144 | ### Is `pprintpp` smaller than `fmtlib`/some other library? 145 | 146 | Certainly. No matter how much you use `pprintpp`, it will never result in actual runtime code. It only fixes your format strings at compile time. 147 | 148 | If binary size is your concern and you want to reduce the number of libraries you link against, `pprintpp` is totally for you. 149 | 150 | This library has first been used in a C++ bare metal operating system project that was also optimized for size. For logging purposes it used its own tiny `printf` implementation combined with `pprintpp` (it was not linked with the standard `libc` or runtime parts of the STL). This way the whole printing-related part of the binary was kept within a few hundred bytes. 151 | 152 | ### Will I profit over `fmtlib`? 153 | 154 | `fmtlib` is a full-blown, highly optimized *formatting library*. `pprintpp` is only a *preprocessor* which enables for automatically composing `printf`-compatible format strings. With other words: `pprintpp` is a `printf` *frontend*. 155 | 156 | You will profit from `pprintpp` over `fmtlib` if: 157 | 158 | - You don't need more formatting features than `printf` and friends provide, and/or cannot use something else than `printf` for whatever reason. 159 | - You have your own tiny and/or fast `printf` implementation and want to make it type safe and comfortable to use 160 | - You want to add type safety and comfort to your printing **without adding additional runtime code**. 161 | 162 | If you are writing a user space application that can handle some bytes of printing library, then you are most probably better with `fmtlib` or the standard C++ streams. 163 | 164 | ### I don't see how this helps printing my own types/classes? 165 | 166 | You are right. It doesn't. Printing for example a custom vector type with `pprintpp` will always look like this: 167 | ``` c++ 168 | pprintf("My vector: ({}, {}, {})\n", vec[0], vec[1], vec[2]); 169 | ``` 170 | 171 | Due to the nature of `pprintpp` just being a *format string preprocessor* which will call a `printf`-like function in the end, it will not be possible to print custom types. 172 | 173 | If it is not possible to express something with printf directly (`printf(" %FOOBARXZY ", my_custom_type_instance);`), then `pprintpp` will not help out here. 174 | 175 | However, if you use some kind of `my_own_printf` implementation, which actually accepts a format specifier like `%v` for `struct my_vector`, and is able to pretty print that type at runtime, then it is easy to extend `pprintpp` with knowledge of this type, yes. 176 | *Then* it is possible to do `pprintf("my own vector type: {}\n", my_own_vector);`. 177 | 178 | Baseline: If you are asking for actual *formatting features*, you will need to add these features to the formatting function, not to `pprintpp`. 179 | 180 | ### Isn't that *no runtime overhead* feature just a result of compiler optimization? 181 | 182 | No. 183 | The whole format string is preprocessed at compile time, this is guaranteed. 184 | -------------------------------------------------------------------------------- /deps/fmt/src/os.cc: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | // Disable bogus MSVC warnings. 9 | #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) 10 | # define _CRT_SECURE_NO_WARNINGS 11 | #endif 12 | 13 | #include "fmt/os.h" 14 | 15 | #include 16 | 17 | #if FMT_USE_FCNTL 18 | # include 19 | # include 20 | 21 | # ifndef _WIN32 22 | # include 23 | # else 24 | # ifndef WIN32_LEAN_AND_MEAN 25 | # define WIN32_LEAN_AND_MEAN 26 | # endif 27 | # include 28 | 29 | # ifndef S_IRUSR 30 | # define S_IRUSR _S_IREAD 31 | # endif 32 | # ifndef S_IWUSR 33 | # define S_IWUSR _S_IWRITE 34 | # endif 35 | # ifndef S_IRGRP 36 | # define S_IRGRP 0 37 | # endif 38 | # ifndef S_IROTH 39 | # define S_IROTH 0 40 | # endif 41 | # endif // _WIN32 42 | #endif // FMT_USE_FCNTL 43 | 44 | #ifdef _WIN32 45 | # include 46 | #endif 47 | 48 | #ifdef fileno 49 | # undef fileno 50 | #endif 51 | 52 | namespace { 53 | #ifdef _WIN32 54 | // Return type of read and write functions. 55 | using rwresult = int; 56 | 57 | // On Windows the count argument to read and write is unsigned, so convert 58 | // it from size_t preventing integer overflow. 59 | inline unsigned convert_rwcount(std::size_t count) { 60 | return count <= UINT_MAX ? static_cast(count) : UINT_MAX; 61 | } 62 | #elif FMT_USE_FCNTL 63 | // Return type of read and write functions. 64 | using rwresult = ssize_t; 65 | 66 | inline std::size_t convert_rwcount(std::size_t count) { return count; } 67 | #endif 68 | } // namespace 69 | 70 | FMT_BEGIN_NAMESPACE 71 | 72 | #ifdef _WIN32 73 | detail::utf16_to_utf8::utf16_to_utf8(basic_string_view s) { 74 | if (int error_code = convert(s)) { 75 | FMT_THROW(windows_error(error_code, 76 | "cannot convert string from UTF-16 to UTF-8")); 77 | } 78 | } 79 | 80 | int detail::utf16_to_utf8::convert(basic_string_view s) { 81 | if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; 82 | int s_size = static_cast(s.size()); 83 | if (s_size == 0) { 84 | // WideCharToMultiByte does not support zero length, handle separately. 85 | buffer_.resize(1); 86 | buffer_[0] = 0; 87 | return 0; 88 | } 89 | 90 | int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0, 91 | nullptr, nullptr); 92 | if (length == 0) return GetLastError(); 93 | buffer_.resize(length + 1); 94 | length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0], 95 | length, nullptr, nullptr); 96 | if (length == 0) return GetLastError(); 97 | buffer_[length] = 0; 98 | return 0; 99 | } 100 | 101 | namespace detail { 102 | 103 | class system_message { 104 | system_message(const system_message&) = delete; 105 | void operator=(const system_message&) = delete; 106 | 107 | unsigned long result_; 108 | wchar_t* message_; 109 | 110 | static bool is_whitespace(wchar_t c) FMT_NOEXCEPT { 111 | return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; 112 | } 113 | 114 | public: 115 | explicit system_message(unsigned long error_code) 116 | : result_(0), message_(nullptr) { 117 | result_ = FormatMessageW( 118 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | 119 | FORMAT_MESSAGE_IGNORE_INSERTS, 120 | nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 121 | reinterpret_cast(&message_), 0, nullptr); 122 | if (result_ != 0) { 123 | while (result_ != 0 && is_whitespace(message_[result_ - 1])) { 124 | --result_; 125 | } 126 | } 127 | } 128 | ~system_message() { LocalFree(message_); } 129 | explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } 130 | operator basic_string_view() const FMT_NOEXCEPT { 131 | return basic_string_view(message_, result_); 132 | } 133 | }; 134 | 135 | class utf8_system_category final : public std::error_category { 136 | public: 137 | const char* name() const FMT_NOEXCEPT override { return "system"; } 138 | std::string message(int error_code) const override { 139 | system_message msg(error_code); 140 | if (msg) { 141 | utf16_to_utf8 utf8_message; 142 | if (utf8_message.convert(msg) == ERROR_SUCCESS) { 143 | return utf8_message.str(); 144 | } 145 | } 146 | return "unknown error"; 147 | } 148 | }; 149 | 150 | } // namespace detail 151 | 152 | FMT_API const std::error_category& system_category() FMT_NOEXCEPT { 153 | static const detail::utf8_system_category category; 154 | return category; 155 | } 156 | 157 | std::system_error vwindows_error(int err_code, string_view format_str, 158 | format_args args) { 159 | auto ec = std::error_code(err_code, system_category()); 160 | return std::system_error(ec, vformat(format_str, args)); 161 | } 162 | 163 | void detail::format_windows_error(detail::buffer& out, int error_code, 164 | const char* message) FMT_NOEXCEPT { 165 | FMT_TRY { 166 | system_message msg(error_code); 167 | if (msg) { 168 | utf16_to_utf8 utf8_message; 169 | if (utf8_message.convert(msg) == ERROR_SUCCESS) { 170 | format_to(buffer_appender(out), "{}: {}", message, utf8_message); 171 | return; 172 | } 173 | } 174 | } 175 | FMT_CATCH(...) {} 176 | format_error_code(out, error_code, message); 177 | } 178 | 179 | void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT { 180 | report_error(detail::format_windows_error, error_code, message); 181 | } 182 | #endif // _WIN32 183 | 184 | buffered_file::~buffered_file() FMT_NOEXCEPT { 185 | if (file_ && FMT_SYSTEM(fclose(file_)) != 0) 186 | report_system_error(errno, "cannot close file"); 187 | } 188 | 189 | buffered_file::buffered_file(cstring_view filename, cstring_view mode) { 190 | FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 191 | nullptr); 192 | if (!file_) 193 | FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); 194 | } 195 | 196 | void buffered_file::close() { 197 | if (!file_) return; 198 | int result = FMT_SYSTEM(fclose(file_)); 199 | file_ = nullptr; 200 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 201 | } 202 | 203 | // A macro used to prevent expansion of fileno on broken versions of MinGW. 204 | #define FMT_ARGS 205 | 206 | int buffered_file::fileno() const { 207 | int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); 208 | if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); 209 | return fd; 210 | } 211 | 212 | #if FMT_USE_FCNTL 213 | file::file(cstring_view path, int oflag) { 214 | # ifdef _WIN32 215 | using mode_t = int; 216 | # endif 217 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 218 | # if defined(_WIN32) && !defined(__MINGW32__) 219 | fd_ = -1; 220 | FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); 221 | # else 222 | FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); 223 | # endif 224 | if (fd_ == -1) 225 | FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); 226 | } 227 | 228 | file::~file() FMT_NOEXCEPT { 229 | // Don't retry close in case of EINTR! 230 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 231 | if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) 232 | report_system_error(errno, "cannot close file"); 233 | } 234 | 235 | void file::close() { 236 | if (fd_ == -1) return; 237 | // Don't retry close in case of EINTR! 238 | // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html 239 | int result = FMT_POSIX_CALL(close(fd_)); 240 | fd_ = -1; 241 | if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); 242 | } 243 | 244 | long long file::size() const { 245 | # ifdef _WIN32 246 | // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT 247 | // is less than 0x0500 as is the case with some default MinGW builds. 248 | // Both functions support large file sizes. 249 | DWORD size_upper = 0; 250 | HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); 251 | DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); 252 | if (size_lower == INVALID_FILE_SIZE) { 253 | DWORD error = GetLastError(); 254 | if (error != NO_ERROR) 255 | FMT_THROW(windows_error(GetLastError(), "cannot get file size")); 256 | } 257 | unsigned long long long_size = size_upper; 258 | return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; 259 | # else 260 | using Stat = struct stat; 261 | Stat file_stat = Stat(); 262 | if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) 263 | FMT_THROW(system_error(errno, "cannot get file attributes")); 264 | static_assert(sizeof(long long) >= sizeof(file_stat.st_size), 265 | "return type of file::size is not large enough"); 266 | return file_stat.st_size; 267 | # endif 268 | } 269 | 270 | std::size_t file::read(void* buffer, std::size_t count) { 271 | rwresult result = 0; 272 | FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); 273 | if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); 274 | return detail::to_unsigned(result); 275 | } 276 | 277 | std::size_t file::write(const void* buffer, std::size_t count) { 278 | rwresult result = 0; 279 | FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); 280 | if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); 281 | return detail::to_unsigned(result); 282 | } 283 | 284 | file file::dup(int fd) { 285 | // Don't retry as dup doesn't return EINTR. 286 | // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html 287 | int new_fd = FMT_POSIX_CALL(dup(fd)); 288 | if (new_fd == -1) 289 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); 290 | return file(new_fd); 291 | } 292 | 293 | void file::dup2(int fd) { 294 | int result = 0; 295 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 296 | if (result == -1) { 297 | FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}", 298 | fd_, fd)); 299 | } 300 | } 301 | 302 | void file::dup2(int fd, std::error_code& ec) FMT_NOEXCEPT { 303 | int result = 0; 304 | FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); 305 | if (result == -1) ec = std::error_code(errno, std::generic_category()); 306 | } 307 | 308 | void file::pipe(file& read_end, file& write_end) { 309 | // Close the descriptors first to make sure that assignments don't throw 310 | // and there are no leaks. 311 | read_end.close(); 312 | write_end.close(); 313 | int fds[2] = {}; 314 | # ifdef _WIN32 315 | // Make the default pipe capacity same as on Linux 2.6.11+. 316 | enum { DEFAULT_CAPACITY = 65536 }; 317 | int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); 318 | # else 319 | // Don't retry as the pipe function doesn't return EINTR. 320 | // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html 321 | int result = FMT_POSIX_CALL(pipe(fds)); 322 | # endif 323 | if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe")); 324 | // The following assignments don't throw because read_fd and write_fd 325 | // are closed. 326 | read_end = file(fds[0]); 327 | write_end = file(fds[1]); 328 | } 329 | 330 | buffered_file file::fdopen(const char* mode) { 331 | // Don't retry as fdopen doesn't return EINTR. 332 | # if defined(__MINGW32__) && defined(_POSIX_) 333 | FILE* f = ::fdopen(fd_, mode); 334 | # else 335 | FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); 336 | # endif 337 | if (!f) 338 | FMT_THROW( 339 | system_error(errno, "cannot associate stream with file descriptor")); 340 | buffered_file bf(f); 341 | fd_ = -1; 342 | return bf; 343 | } 344 | 345 | long getpagesize() { 346 | # ifdef _WIN32 347 | SYSTEM_INFO si; 348 | GetSystemInfo(&si); 349 | return si.dwPageSize; 350 | # else 351 | long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); 352 | if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size")); 353 | return size; 354 | # endif 355 | } 356 | 357 | FMT_API void ostream::grow(size_t) { 358 | if (this->size() == this->capacity()) flush(); 359 | } 360 | #endif // FMT_USE_FCNTL 361 | FMT_END_NAMESPACE 362 | -------------------------------------------------------------------------------- /src/nanolog.cc: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2019 - 2020 Light Transport Entertainment Inc. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include "nanolog.hh" 25 | 26 | #ifdef __clang__ 27 | #pragma clang diagnostic push 28 | #pragma clang diagnostic ignored "-Weverything" 29 | #endif 30 | 31 | #if NANOLOG_USE_FMTLIB 32 | 33 | #include "fmt/chrono.h" 34 | #include "fmt/core.h" 35 | 36 | #elif defined(NANOLOG_USE_PPRINTPP) 37 | 38 | #include "pprintpp.hpp" 39 | 40 | #else 41 | 42 | #include 43 | #include 44 | 45 | #endif 46 | 47 | #ifdef __clang__ 48 | #pragma clang diagnostic pop 49 | #endif 50 | 51 | #include 52 | #include 53 | #include 54 | #include // std::put_time 55 | #include 56 | #include 57 | 58 | #if !defined(NANOLOG_NO_EXCEPTION_AT_FATAL) 59 | #include 60 | #endif 61 | 62 | #if defined(__ANDROID__) && !defined(NANOLOG_ANDROID_USE_STDIO) 63 | #include 64 | #endif 65 | 66 | namespace nanolog { 67 | 68 | static enum loglevel g_level = kINFO; 69 | static bool g_color = true; 70 | static std::mutex g_mutex; 71 | 72 | #ifdef __clang__ 73 | #pragma clang diagnostic push 74 | #pragma clang diagnostic ignored "-Wexit-time-destructors" 75 | #pragma clang diagnostic ignored "-Wglobal-constructors" 76 | #endif 77 | static std::string g_apptag; 78 | 79 | #ifdef __clang__ 80 | #pragma clang diagnostic pop 81 | #endif 82 | 83 | #if defined(__ANDROID__) && !defined(NANOLOG_ANDROID_USE_STDIO) 84 | // Android logcat prints time, so no need to print time in nanolog messsage. 85 | static bool g_printtime = false; 86 | #else 87 | static bool g_printtime = true; 88 | #endif 89 | 90 | void set_level(enum loglevel level) { g_level = level; } 91 | 92 | void set_color(bool enabled) { g_color = enabled; } 93 | 94 | void set_apptag(const std::string &name) { g_apptag = name; } 95 | 96 | void set_printtime(bool enabled) { g_printtime = enabled; } 97 | 98 | #ifdef __clang__ 99 | #pragma clang diagnostic push 100 | #pragma clang diagnostic ignored "-Wformat-nonliteral" 101 | #endif 102 | 103 | #if defined(NANOLOG_USE_PPRINTPP) 104 | void log(int level, const char *file, const char *funcname, int line, 105 | const char *formatted_str, ...) { 106 | if (level < g_level) { 107 | return; 108 | } 109 | 110 | std::string lv_str; 111 | if (level == kTRACE) { 112 | lv_str = "trace"; 113 | } else if (level == kDEBUG) { 114 | lv_str = "debug"; 115 | } else if (level == kINFO) { 116 | lv_str = "info"; 117 | } else if (level == kWARN) { 118 | if (g_color) { 119 | lv_str = "\033[33mwarn\033[m"; 120 | } else { 121 | lv_str = "warn"; 122 | } 123 | } else if (level == kERROR) { 124 | if (g_color) { 125 | lv_str = "\033[31merror\033[m"; 126 | } else { 127 | lv_str = "error"; 128 | } 129 | } else if (level == kFATAL) { 130 | if (g_color) { 131 | lv_str = "\033[31m\033[1mfatal\033[m"; 132 | } else { 133 | lv_str = "fatal"; 134 | } 135 | } else { 136 | lv_str = "UNKNOWN"; 137 | } 138 | 139 | std::string log_fmt; 140 | std::string header = "[" + lv_str + "] [" + file + ":" + funcname + ":" + 141 | std::to_string(line) + "] "; 142 | 143 | #if defined(__ANDROID__) && !defined(NANOLOG_ANDROID_USE_STDIO) 144 | 145 | if (g_printtime) { 146 | std::time_t tm = std::time(nullptr); 147 | std::tm ttm = *std::localtime(&tm); 148 | 149 | // datetime 150 | std::stringstream ss; 151 | ss << std::put_time(&ttm, "[%Y-%m-%d %H:%M:%S] "); 152 | std::string date_header = ss.str(); 153 | 154 | log_fmt += date_header + header; 155 | } else { 156 | log_fmt += header; 157 | } 158 | 159 | log_fmt += std::string(formatted_str); 160 | 161 | android_LogPriority priority = ANDROID_LOG_DEFAULT; 162 | 163 | if (level == kTRACE) { 164 | priority = ANDROID_LOG_VERBOSE; 165 | } else if (level == kDEBUG) { 166 | priority = ANDROID_LOG_DEBUG; 167 | } else if (level == kINFO) { 168 | priority = ANDROID_LOG_INFO; 169 | } else if (level == kWARN) { 170 | priority = ANDROID_LOG_WARN; 171 | } else if (level == kERROR) { 172 | priority = ANDROID_LOG_ERROR; 173 | } else if (level == kFATAL) { 174 | priority = ANDROID_LOG_FATAL; 175 | } else { 176 | priority = ANDROID_LOG_UNKNOWN; 177 | } 178 | 179 | std::string tag = g_apptag.empty() ? "nanolog" : g_apptag; 180 | 181 | va_list args; 182 | va_start(args, formatted_str); 183 | // it looks Android logging is thread safe, so do not use the lock 184 | __android_log_vprint(priority, tag.c_str(), log_fmt.c_str(), args); 185 | va_end(args); 186 | 187 | #else 188 | 189 | if (!g_apptag.empty()) { 190 | log_fmt += "[" + g_apptag + "] "; 191 | } 192 | 193 | if (g_printtime) { 194 | // datetime 195 | 196 | std::time_t tm = std::time(nullptr); 197 | std::tm ttm = *std::localtime(&tm); 198 | 199 | std::stringstream ss; 200 | ss << std::put_time(&ttm, "[%Y-%m-%d %H:%M:%S] "); 201 | std::string date_header = ss.str(); 202 | 203 | log_fmt += date_header + header; 204 | } else { 205 | log_fmt += header; 206 | } 207 | 208 | // append newline 209 | log_fmt += std::string(formatted_str) + '\n'; 210 | 211 | va_list args; 212 | va_start(args, formatted_str); 213 | 214 | { 215 | std::lock_guard lock(g_mutex); 216 | vprintf(log_fmt.c_str(), args); 217 | } 218 | 219 | va_end(args); 220 | #endif 221 | 222 | if (level == kFATAL) { 223 | #if !defined(NANOLOG_NO_EXCEPTION_AT_FATAL) 224 | std::runtime_error("fatal"); 225 | #endif 226 | } 227 | } 228 | #elif defined(NANOLOG_USE_FMTLIB) 229 | void log(int level, const char *file, const char *funcname, int line, 230 | const char *fmt_str, fmt::format_args args) { 231 | if (level < g_level) { 232 | return; 233 | } 234 | 235 | #if defined(__ANDROID__) && !defined(NANOLOG_ANDROID_USE_STDIO) 236 | 237 | std::string log_fmt; 238 | 239 | std::time_t tm = std::time(nullptr); 240 | 241 | // datetime 242 | std::string date_header = 243 | fmt::format("[{:%Y-%m-%d %H:%M:%S}] ", *std::localtime(&tm)); 244 | 245 | std::string header = fmt::format("[{}:{}:{}] ", file, funcname, line); 246 | log_fmt += date_header + header; 247 | 248 | log_fmt += std::string(fmt_str); 249 | 250 | std::string s = fmt::vformat(log_fmt, args); 251 | 252 | android_LogPriority priority = ANDROID_LOG_DEFAULT; 253 | 254 | if (level == kTRACE) { 255 | priority = ANDROID_LOG_VERBOSE; 256 | } else if (level == kDEBUG) { 257 | priority = ANDROID_LOG_DEBUG; 258 | } else if (level == kINFO) { 259 | priority = ANDROID_LOG_INFO; 260 | } else if (level == kWARN) { 261 | priority = ANDROID_LOG_WARN; 262 | } else if (level == kERROR) { 263 | priority = ANDROID_LOG_ERROR; 264 | } else if (level == kFATAL) { 265 | priority = ANDROID_LOG_FATAL; 266 | } else { 267 | priority = ANDROID_LOG_UNKNOWN; 268 | } 269 | 270 | std::string tag = g_apptag.empty() ? "nanolog" : g_apptag; 271 | 272 | // it looks Android logging is thread safe, so do not use the lock 273 | __android_log_print(priority, tag.c_str(), "%s", s.c_str()); 274 | 275 | #else 276 | 277 | std::string log_fmt; 278 | 279 | std::string lv_str; 280 | if (level == kTRACE) { 281 | lv_str = "trace"; 282 | } else if (level == kDEBUG) { 283 | lv_str = "debug"; 284 | } else if (level == kINFO) { 285 | lv_str = "info"; 286 | } else if (level == kWARN) { 287 | if (g_color) { 288 | lv_str = "\033[33mwarn\033[m"; 289 | } else { 290 | lv_str = "warn"; 291 | } 292 | } else if (level == kERROR) { 293 | if (g_color) { 294 | lv_str = "\033[31merror\033[m"; 295 | } else { 296 | lv_str = "error"; 297 | } 298 | } else if (level == kFATAL) { 299 | if (g_color) { 300 | lv_str = "\033[31m\033[1mfatal\033[m"; 301 | } else { 302 | lv_str = "fatal"; 303 | } 304 | } else { 305 | lv_str = "UNKNOWN"; 306 | } 307 | 308 | std::time_t tm = std::time(nullptr); 309 | 310 | // datetime 311 | std::string date_header = 312 | fmt::format("[{:%Y-%m-%d %H:%M:%S}] ", *std::localtime(&tm)); 313 | 314 | std::string header = 315 | fmt::format("[{}] [{}:{}:{}] ", lv_str, file, funcname, line); 316 | if (!g_apptag.empty()) { 317 | log_fmt += "[" + g_apptag + "] "; 318 | } 319 | 320 | log_fmt += date_header + header; 321 | 322 | // append newline 323 | log_fmt += std::string(fmt_str) + '\n'; 324 | 325 | { 326 | std::lock_guard lock(g_mutex); 327 | fmt::vprint(log_fmt, args); 328 | } 329 | #endif 330 | 331 | if (level == kFATAL) { 332 | #if !defined(NANOLOG_NO_EXCEPTION_AT_FATAL) 333 | std::runtime_error("fatal"); 334 | #endif 335 | } 336 | } 337 | #else 338 | 339 | void LogMsg::emit() { 340 | // TODO: Support Android 341 | 342 | if (_level < g_level) { 343 | return; 344 | } 345 | 346 | std::string str(_msg); 347 | 348 | // find the number of occurences of `{}` 349 | // https://stackoverflow.com/questions/4034750/find-all-a-substrings-occurrences-and-locations 350 | std::vector positions; 351 | size_t pos = str.find("{}", 0); 352 | while (pos != std::string::npos) { 353 | positions.push_back(pos); 354 | pos = str.find("{}", pos + 1); 355 | } 356 | 357 | // std::vector toks = split_string(_msg, "{}"); 358 | 359 | if (positions.size() != arg_strs.size()) { 360 | std::stringstream ss; 361 | 362 | ss << "Mismatch in the number of arguments! " << positions.size() 363 | << " for `{}` and " << arg_strs.size() << " for args. [" << _fname 364 | << ":" << _funname << ":" << _line << "]\n"; 365 | // Argument mismatch 366 | 367 | #if defined(__ANDROID__) && !defined(NANOLOG_ANDROID_USE_STDIO) 368 | std::string tag = g_apptag.empty() ? "nanolog" : g_apptag; 369 | 370 | __android_log_print(ANDROID_LOG_ERROR, tag.c_str(), "%s", ss.str().c_str()); 371 | #else 372 | std::cerr << ss.str(); 373 | #endif 374 | return; 375 | } 376 | 377 | // replace string from the end to the beginning so that we don't need to 378 | // adjust the location in `positions`. 379 | size_t n = positions.size(); 380 | for (size_t i = 0; i < n; i++) { 381 | str.replace(positions[n - i - 1], /* strlen("{}") */ 2, 382 | arg_strs[n - i - 1]); 383 | } 384 | 385 | std::string lv_str; 386 | if (_level == kTRACE) { 387 | lv_str = "trace"; 388 | } else if (_level == kDEBUG) { 389 | lv_str = "debug"; 390 | } else if (_level == kINFO) { 391 | lv_str = "info"; 392 | } else if (_level == kWARN) { 393 | if (g_color) { 394 | lv_str = "\033[33mwarn\033[m"; 395 | } else { 396 | lv_str = "warn"; 397 | } 398 | } else if (_level == kERROR) { 399 | if (g_color) { 400 | lv_str = "\033[31merror\033[m"; 401 | } else { 402 | lv_str = "error"; 403 | } 404 | } else if (_level == kFATAL) { 405 | if (g_color) { 406 | lv_str = "\033[31m\033[1mfatal\033[m"; 407 | } else { 408 | lv_str = "fatal"; 409 | } 410 | } else { 411 | lv_str = "UNKNOWN"; 412 | } 413 | 414 | // `std::localtime` may not be thread safe, so take a lock before calling it. 415 | std::lock_guard lock(g_mutex); 416 | 417 | std::string date_header; 418 | 419 | // datetime 420 | char buf[64]; 421 | 422 | std::time_t t = std::time(nullptr); 423 | struct tm tmbuf; 424 | #ifdef _MSC_VER 425 | // MS CRT has different function signature compared to the C11 standard 426 | errno_t tmerr = localtime_s(&tmbuf, &t); 427 | if (tmerr != 0) { 428 | date_header = "[DATE_ERROR] "; 429 | } else if (strftime(buf, sizeof buf, "[%Y-%m-%d %H:%M:%S] ", &tmbuf)) { 430 | date_header = std::string(buf, strlen(buf)); 431 | } else { 432 | date_header = "[DATE_UNKNOWN] "; 433 | } 434 | #else 435 | // TODO: Try to use localtime_s with __STDC_LIB_EXT1__ 436 | // https://en.cppreference.com/w/c/chrono/localtime 437 | tmbuf = *std::localtime(&t); 438 | 439 | if (strftime(buf, sizeof buf, "[%Y-%m-%d %H:%M:%S] ", &tmbuf)) { 440 | date_header = std::string(buf, strlen(buf)); 441 | } else { 442 | date_header = "[DATE_UNKNOWN] "; 443 | // ??? 444 | } 445 | 446 | #endif 447 | 448 | 449 | std::string log_fmt; 450 | 451 | if (!g_apptag.empty()) { 452 | log_fmt += "[" + g_apptag + "] "; 453 | } 454 | 455 | log_fmt += date_header + "[" + lv_str + ":" + _fname + ":" + _funname + ":" + std::to_string(_line) + "] "; 456 | 457 | log_fmt += str; 458 | 459 | printf("%s\n", log_fmt.c_str()); 460 | } 461 | 462 | // internal 463 | #endif 464 | 465 | #ifdef __clang__ 466 | #pragma clang diagnostic pop 467 | #endif 468 | 469 | } // namespace nanolog 470 | -------------------------------------------------------------------------------- /deps/fmt/include/fmt/os.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OS_H_ 9 | #define FMT_OS_H_ 10 | 11 | #include 12 | #include // locale_t 13 | #include 14 | #include 15 | #include // strtod_l 16 | #include // std::system_error 17 | 18 | #if defined __APPLE__ || defined(__FreeBSD__) 19 | # include // for LC_NUMERIC_MASK on OS X 20 | #endif 21 | 22 | #include "format.h" 23 | 24 | #ifndef FMT_USE_FCNTL 25 | // UWP doesn't provide _pipe. 26 | # if FMT_HAS_INCLUDE("winapifamily.h") 27 | # include 28 | # endif 29 | # if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ 30 | defined(__linux__)) && \ 31 | (!defined(WINAPI_FAMILY) || \ 32 | (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) 33 | # include // for O_RDONLY 34 | # define FMT_USE_FCNTL 1 35 | # else 36 | # define FMT_USE_FCNTL 0 37 | # endif 38 | #endif 39 | 40 | #ifndef FMT_POSIX 41 | # if defined(_WIN32) && !defined(__MINGW32__) 42 | // Fix warnings about deprecated symbols. 43 | # define FMT_POSIX(call) _##call 44 | # else 45 | # define FMT_POSIX(call) call 46 | # endif 47 | #endif 48 | 49 | // Calls to system functions are wrapped in FMT_SYSTEM for testability. 50 | #ifdef FMT_SYSTEM 51 | # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) 52 | #else 53 | # define FMT_SYSTEM(call) ::call 54 | # ifdef _WIN32 55 | // Fix warnings about deprecated symbols. 56 | # define FMT_POSIX_CALL(call) ::_##call 57 | # else 58 | # define FMT_POSIX_CALL(call) ::call 59 | # endif 60 | #endif 61 | 62 | // Retries the expression while it evaluates to error_result and errno 63 | // equals to EINTR. 64 | #ifndef _WIN32 65 | # define FMT_RETRY_VAL(result, expression, error_result) \ 66 | do { \ 67 | (result) = (expression); \ 68 | } while ((result) == (error_result) && errno == EINTR) 69 | #else 70 | # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) 71 | #endif 72 | 73 | #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) 74 | 75 | FMT_BEGIN_NAMESPACE 76 | FMT_MODULE_EXPORT_BEGIN 77 | 78 | /** 79 | \rst 80 | A reference to a null-terminated string. It can be constructed from a C 81 | string or ``std::string``. 82 | 83 | You can use one of the following type aliases for common character types: 84 | 85 | +---------------+-----------------------------+ 86 | | Type | Definition | 87 | +===============+=============================+ 88 | | cstring_view | basic_cstring_view | 89 | +---------------+-----------------------------+ 90 | | wcstring_view | basic_cstring_view | 91 | +---------------+-----------------------------+ 92 | 93 | This class is most useful as a parameter type to allow passing 94 | different types of strings to a function, for example:: 95 | 96 | template 97 | std::string format(cstring_view format_str, const Args & ... args); 98 | 99 | format("{}", 42); 100 | format(std::string("{}"), 42); 101 | \endrst 102 | */ 103 | template class basic_cstring_view { 104 | private: 105 | const Char* data_; 106 | 107 | public: 108 | /** Constructs a string reference object from a C string. */ 109 | basic_cstring_view(const Char* s) : data_(s) {} 110 | 111 | /** 112 | \rst 113 | Constructs a string reference from an ``std::string`` object. 114 | \endrst 115 | */ 116 | basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} 117 | 118 | /** Returns the pointer to a C string. */ 119 | const Char* c_str() const { return data_; } 120 | }; 121 | 122 | using cstring_view = basic_cstring_view; 123 | using wcstring_view = basic_cstring_view; 124 | 125 | template struct formatter { 126 | template 127 | FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 128 | return ctx.begin(); 129 | } 130 | 131 | template 132 | FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const 133 | -> decltype(ctx.out()) { 134 | auto out = ctx.out(); 135 | out = detail::write_bytes(out, ec.category().name(), 136 | basic_format_specs()); 137 | out = detail::write(out, Char(':')); 138 | out = detail::write(out, ec.value()); 139 | return out; 140 | } 141 | }; 142 | 143 | #ifdef _WIN32 144 | FMT_API const std::error_category& system_category() FMT_NOEXCEPT; 145 | 146 | FMT_BEGIN_DETAIL_NAMESPACE 147 | // A converter from UTF-16 to UTF-8. 148 | // It is only provided for Windows since other systems support UTF-8 natively. 149 | class utf16_to_utf8 { 150 | private: 151 | memory_buffer buffer_; 152 | 153 | public: 154 | utf16_to_utf8() {} 155 | FMT_API explicit utf16_to_utf8(basic_string_view s); 156 | operator string_view() const { return string_view(&buffer_[0], size()); } 157 | size_t size() const { return buffer_.size() - 1; } 158 | const char* c_str() const { return &buffer_[0]; } 159 | std::string str() const { return std::string(&buffer_[0], size()); } 160 | 161 | // Performs conversion returning a system error code instead of 162 | // throwing exception on conversion error. This method may still throw 163 | // in case of memory allocation error. 164 | FMT_API int convert(basic_string_view s); 165 | }; 166 | 167 | FMT_API void format_windows_error(buffer& out, int error_code, 168 | const char* message) FMT_NOEXCEPT; 169 | FMT_END_DETAIL_NAMESPACE 170 | 171 | FMT_API std::system_error vwindows_error(int error_code, string_view format_str, 172 | format_args args); 173 | 174 | /** 175 | \rst 176 | Constructs a :class:`std::system_error` object with the description 177 | of the form 178 | 179 | .. parsed-literal:: 180 | **: ** 181 | 182 | where ** is the formatted message and ** is the 183 | system message corresponding to the error code. 184 | *error_code* is a Windows error code as given by ``GetLastError``. 185 | If *error_code* is not a valid error code such as -1, the system message 186 | will look like "error -1". 187 | 188 | **Example**:: 189 | 190 | // This throws a system_error with the description 191 | // cannot open file 'madeup': The system cannot find the file specified. 192 | // or similar (system message may vary). 193 | const char *filename = "madeup"; 194 | LPOFSTRUCT of = LPOFSTRUCT(); 195 | HFILE file = OpenFile(filename, &of, OF_READ); 196 | if (file == HFILE_ERROR) { 197 | throw fmt::windows_error(GetLastError(), 198 | "cannot open file '{}'", filename); 199 | } 200 | \endrst 201 | */ 202 | template 203 | std::system_error windows_error(int error_code, string_view message, 204 | const Args&... args) { 205 | return vwindows_error(error_code, message, fmt::make_format_args(args...)); 206 | } 207 | 208 | // Reports a Windows error without throwing an exception. 209 | // Can be used to report errors from destructors. 210 | FMT_API void report_windows_error(int error_code, 211 | const char* message) FMT_NOEXCEPT; 212 | #else 213 | inline const std::error_category& system_category() FMT_NOEXCEPT { 214 | return std::system_category(); 215 | } 216 | #endif // _WIN32 217 | 218 | // std::system is not available on some platforms such as iOS (#2248). 219 | #ifdef __OSX__ 220 | template > 221 | void say(const S& format_str, Args&&... args) { 222 | std::system(format("say \"{}\"", format(format_str, args...)).c_str()); 223 | } 224 | #endif 225 | 226 | // A buffered file. 227 | class buffered_file { 228 | private: 229 | FILE* file_; 230 | 231 | friend class file; 232 | 233 | explicit buffered_file(FILE* f) : file_(f) {} 234 | 235 | public: 236 | buffered_file(const buffered_file&) = delete; 237 | void operator=(const buffered_file&) = delete; 238 | 239 | // Constructs a buffered_file object which doesn't represent any file. 240 | buffered_file() FMT_NOEXCEPT : file_(nullptr) {} 241 | 242 | // Destroys the object closing the file it represents if any. 243 | FMT_API ~buffered_file() FMT_NOEXCEPT; 244 | 245 | public: 246 | buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { 247 | other.file_ = nullptr; 248 | } 249 | 250 | buffered_file& operator=(buffered_file&& other) { 251 | close(); 252 | file_ = other.file_; 253 | other.file_ = nullptr; 254 | return *this; 255 | } 256 | 257 | // Opens a file. 258 | FMT_API buffered_file(cstring_view filename, cstring_view mode); 259 | 260 | // Closes the file. 261 | FMT_API void close(); 262 | 263 | // Returns the pointer to a FILE object representing this file. 264 | FILE* get() const FMT_NOEXCEPT { return file_; } 265 | 266 | // We place parentheses around fileno to workaround a bug in some versions 267 | // of MinGW that define fileno as a macro. 268 | FMT_API int(fileno)() const; 269 | 270 | void vprint(string_view format_str, format_args args) { 271 | fmt::vprint(file_, format_str, args); 272 | } 273 | 274 | template 275 | inline void print(string_view format_str, const Args&... args) { 276 | vprint(format_str, fmt::make_format_args(args...)); 277 | } 278 | }; 279 | 280 | #if FMT_USE_FCNTL 281 | // A file. Closed file is represented by a file object with descriptor -1. 282 | // Methods that are not declared with FMT_NOEXCEPT may throw 283 | // fmt::system_error in case of failure. Note that some errors such as 284 | // closing the file multiple times will cause a crash on Windows rather 285 | // than an exception. You can get standard behavior by overriding the 286 | // invalid parameter handler with _set_invalid_parameter_handler. 287 | class file { 288 | private: 289 | int fd_; // File descriptor. 290 | 291 | // Constructs a file object with a given descriptor. 292 | explicit file(int fd) : fd_(fd) {} 293 | 294 | public: 295 | // Possible values for the oflag argument to the constructor. 296 | enum { 297 | RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. 298 | WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. 299 | RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. 300 | CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. 301 | APPEND = FMT_POSIX(O_APPEND), // Open in append mode. 302 | TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. 303 | }; 304 | 305 | // Constructs a file object which doesn't represent any file. 306 | file() FMT_NOEXCEPT : fd_(-1) {} 307 | 308 | // Opens a file and constructs a file object representing this file. 309 | FMT_API file(cstring_view path, int oflag); 310 | 311 | public: 312 | file(const file&) = delete; 313 | void operator=(const file&) = delete; 314 | 315 | file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } 316 | 317 | // Move assignment is not noexcept because close may throw. 318 | file& operator=(file&& other) { 319 | close(); 320 | fd_ = other.fd_; 321 | other.fd_ = -1; 322 | return *this; 323 | } 324 | 325 | // Destroys the object closing the file it represents if any. 326 | FMT_API ~file() FMT_NOEXCEPT; 327 | 328 | // Returns the file descriptor. 329 | int descriptor() const FMT_NOEXCEPT { return fd_; } 330 | 331 | // Closes the file. 332 | FMT_API void close(); 333 | 334 | // Returns the file size. The size has signed type for consistency with 335 | // stat::st_size. 336 | FMT_API long long size() const; 337 | 338 | // Attempts to read count bytes from the file into the specified buffer. 339 | FMT_API size_t read(void* buffer, size_t count); 340 | 341 | // Attempts to write count bytes from the specified buffer to the file. 342 | FMT_API size_t write(const void* buffer, size_t count); 343 | 344 | // Duplicates a file descriptor with the dup function and returns 345 | // the duplicate as a file object. 346 | FMT_API static file dup(int fd); 347 | 348 | // Makes fd be the copy of this file descriptor, closing fd first if 349 | // necessary. 350 | FMT_API void dup2(int fd); 351 | 352 | // Makes fd be the copy of this file descriptor, closing fd first if 353 | // necessary. 354 | FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT; 355 | 356 | // Creates a pipe setting up read_end and write_end file objects for reading 357 | // and writing respectively. 358 | FMT_API static void pipe(file& read_end, file& write_end); 359 | 360 | // Creates a buffered_file object associated with this file and detaches 361 | // this file object from the file. 362 | FMT_API buffered_file fdopen(const char* mode); 363 | }; 364 | 365 | // Returns the memory page size. 366 | long getpagesize(); 367 | 368 | FMT_BEGIN_DETAIL_NAMESPACE 369 | 370 | struct buffer_size { 371 | buffer_size() = default; 372 | size_t value = 0; 373 | buffer_size operator=(size_t val) const { 374 | auto bs = buffer_size(); 375 | bs.value = val; 376 | return bs; 377 | } 378 | }; 379 | 380 | struct ostream_params { 381 | int oflag = file::WRONLY | file::CREATE | file::TRUNC; 382 | size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; 383 | 384 | ostream_params() {} 385 | 386 | template 387 | ostream_params(T... params, int new_oflag) : ostream_params(params...) { 388 | oflag = new_oflag; 389 | } 390 | 391 | template 392 | ostream_params(T... params, detail::buffer_size bs) 393 | : ostream_params(params...) { 394 | this->buffer_size = bs.value; 395 | } 396 | 397 | // Intel has a bug that results in failure to deduce a constructor 398 | // for empty parameter packs. 399 | # if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 400 | ostream_params(int new_oflag) : oflag(new_oflag) {} 401 | ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} 402 | # endif 403 | }; 404 | 405 | FMT_END_DETAIL_NAMESPACE 406 | 407 | // Added {} below to work around default constructor error known to 408 | // occur in Xcode versions 7.2.1 and 8.2.1. 409 | constexpr detail::buffer_size buffer_size{}; 410 | 411 | /** A fast output stream which is not thread-safe. */ 412 | class FMT_API ostream final : private detail::buffer { 413 | private: 414 | file file_; 415 | 416 | void grow(size_t) override; 417 | 418 | ostream(cstring_view path, const detail::ostream_params& params) 419 | : file_(path, params.oflag) { 420 | set(new char[params.buffer_size], params.buffer_size); 421 | } 422 | 423 | public: 424 | ostream(ostream&& other) 425 | : detail::buffer(other.data(), other.size(), other.capacity()), 426 | file_(std::move(other.file_)) { 427 | other.clear(); 428 | other.set(nullptr, 0); 429 | } 430 | ~ostream() { 431 | flush(); 432 | delete[] data(); 433 | } 434 | 435 | void flush() { 436 | if (size() == 0) return; 437 | file_.write(data(), size()); 438 | clear(); 439 | } 440 | 441 | template 442 | friend ostream output_file(cstring_view path, T... params); 443 | 444 | void close() { 445 | flush(); 446 | file_.close(); 447 | } 448 | 449 | /** 450 | Formats ``args`` according to specifications in ``fmt`` and writes the 451 | output to the file. 452 | */ 453 | template void print(format_string fmt, T&&... args) { 454 | vformat_to(detail::buffer_appender(*this), fmt, 455 | fmt::make_format_args(args...)); 456 | } 457 | }; 458 | 459 | /** 460 | \rst 461 | Opens a file for writing. Supported parameters passed in *params*: 462 | 463 | * ````: Flags passed to `open 464 | `_ 465 | (``file::WRONLY | file::CREATE`` by default) 466 | * ``buffer_size=``: Output buffer size 467 | 468 | **Example**:: 469 | 470 | auto out = fmt::output_file("guide.txt"); 471 | out.print("Don't {}", "Panic"); 472 | \endrst 473 | */ 474 | template 475 | inline ostream output_file(cstring_view path, T... params) { 476 | return {path, detail::ostream_params(params...)}; 477 | } 478 | #endif // FMT_USE_FCNTL 479 | 480 | #ifdef FMT_LOCALE 481 | // A "C" numeric locale. 482 | class locale { 483 | private: 484 | # ifdef _WIN32 485 | using locale_t = _locale_t; 486 | 487 | static void freelocale(locale_t loc) { _free_locale(loc); } 488 | 489 | static double strtod_l(const char* nptr, char** endptr, _locale_t loc) { 490 | return _strtod_l(nptr, endptr, loc); 491 | } 492 | # endif 493 | 494 | locale_t locale_; 495 | 496 | public: 497 | using type = locale_t; 498 | locale(const locale&) = delete; 499 | void operator=(const locale&) = delete; 500 | 501 | locale() { 502 | # ifndef _WIN32 503 | locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr)); 504 | # else 505 | locale_ = _create_locale(LC_NUMERIC, "C"); 506 | # endif 507 | if (!locale_) FMT_THROW(system_error(errno, "cannot create locale")); 508 | } 509 | ~locale() { freelocale(locale_); } 510 | 511 | type get() const { return locale_; } 512 | 513 | // Converts string to floating-point number and advances str past the end 514 | // of the parsed input. 515 | FMT_DEPRECATED double strtod(const char*& str) const { 516 | char* end = nullptr; 517 | double result = strtod_l(str, &end, locale_); 518 | str = end; 519 | return result; 520 | } 521 | }; 522 | using Locale FMT_DEPRECATED_ALIAS = locale; 523 | #endif // FMT_LOCALE 524 | FMT_MODULE_EXPORT_END 525 | FMT_END_NAMESPACE 526 | 527 | #endif // FMT_OS_H_ 528 | -------------------------------------------------------------------------------- /deps/fmt/README.rst: -------------------------------------------------------------------------------- 1 | {fmt} 2 | ===== 3 | 4 | .. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg 5 | :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux 6 | 7 | .. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg 8 | :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos 9 | 10 | .. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg 11 | :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows 12 | 13 | .. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v?svg=true 14 | :target: https://ci.appveyor.com/project/vitaut/fmt 15 | 16 | .. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg 17 | :alt: fmt is continuously fuzzed at oss-fuzz 18 | :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\ 19 | colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\ 20 | Summary&q=proj%3Dfmt&can=1 21 | 22 | .. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg 23 | :alt: Ask questions at StackOverflow with the tag fmt 24 | :target: https://stackoverflow.com/questions/tagged/fmt 25 | 26 | **{fmt}** is an open-source formatting library providing a fast and safe 27 | alternative to C stdio and C++ iostreams. 28 | 29 | If you like this project, please consider donating to the BYSOL 30 | Foundation that helps victims of political repressions in Belarus: 31 | https://bysol.org/en/bs/general/. 32 | 33 | `Documentation `__ 34 | 35 | Q&A: ask questions on `StackOverflow with the tag fmt 36 | `_. 37 | 38 | Try {fmt} in `Compiler Explorer `_. 39 | 40 | Features 41 | -------- 42 | 43 | * Simple `format API `_ with positional arguments 44 | for localization 45 | * Implementation of `C++20 std::format 46 | `__ 47 | * `Format string syntax `_ similar to Python's 48 | `format `_ 49 | * Fast IEEE 754 floating-point formatter with correct rounding, shortness and 50 | round-trip guarantees 51 | * Safe `printf implementation 52 | `_ including the POSIX 53 | extension for positional arguments 54 | * Extensibility: `support for user-defined types 55 | `_ 56 | * High performance: faster than common standard library implementations of 57 | ``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ 58 | and `Converting a hundred million integers to strings per second 59 | `_ 60 | * Small code size both in terms of source code with the minimum configuration 61 | consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``, 62 | and compiled code; see `Compile time and code bloat`_ 63 | * Reliability: the library has an extensive set of `tests 64 | `_ and is `continuously fuzzed 65 | `_ 67 | * Safety: the library is fully type safe, errors in format strings can be 68 | reported at compile time, automatic memory management prevents buffer overflow 69 | errors 70 | * Ease of use: small self-contained code base, no external dependencies, 71 | permissive MIT `license 72 | `_ 73 | * `Portability `_ with 74 | consistent output across platforms and support for older compilers 75 | * Clean warning-free codebase even on high warning levels such as 76 | ``-Wall -Wextra -pedantic`` 77 | * Locale-independence by default 78 | * Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro 79 | 80 | See the `documentation `_ for more details. 81 | 82 | Examples 83 | -------- 84 | 85 | **Print to stdout** (`run `_) 86 | 87 | .. code:: c++ 88 | 89 | #include 90 | 91 | int main() { 92 | fmt::print("Hello, world!\n"); 93 | } 94 | 95 | **Format a string** (`run `_) 96 | 97 | .. code:: c++ 98 | 99 | std::string s = fmt::format("The answer is {}.", 42); 100 | // s == "The answer is 42." 101 | 102 | **Format a string using positional arguments** (`run `_) 103 | 104 | .. code:: c++ 105 | 106 | std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); 107 | // s == "I'd rather be happy than right." 108 | 109 | **Print chrono durations** (`run `_) 110 | 111 | .. code:: c++ 112 | 113 | #include 114 | 115 | int main() { 116 | using namespace std::literals::chrono_literals; 117 | fmt::print("Default format: {} {}\n", 42s, 100ms); 118 | fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); 119 | } 120 | 121 | Output:: 122 | 123 | Default format: 42s 100ms 124 | strftime-like format: 03:15:30 125 | 126 | **Print a container** (`run `_) 127 | 128 | .. code:: c++ 129 | 130 | #include 131 | #include 132 | 133 | int main() { 134 | std::vector v = {1, 2, 3}; 135 | fmt::print("{}\n", v); 136 | } 137 | 138 | Output:: 139 | 140 | [1, 2, 3] 141 | 142 | **Check a format string at compile time** 143 | 144 | .. code:: c++ 145 | 146 | std::string s = fmt::format("{:d}", "I am not a number"); 147 | 148 | This gives a compile-time error in C++20 because ``d`` is an invalid format 149 | specifier for a string. 150 | 151 | **Write a file from a single thread** 152 | 153 | .. code:: c++ 154 | 155 | #include 156 | 157 | int main() { 158 | auto out = fmt::output_file("guide.txt"); 159 | out.print("Don't {}", "Panic"); 160 | } 161 | 162 | This can be `5 to 9 times faster than fprintf 163 | `_. 164 | 165 | **Print with colors and text styles** 166 | 167 | .. code:: c++ 168 | 169 | #include 170 | 171 | int main() { 172 | fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, 173 | "Hello, {}!\n", "world"); 174 | fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | 175 | fmt::emphasis::underline, "Hello, {}!\n", "мир"); 176 | fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, 177 | "Hello, {}!\n", "世界"); 178 | } 179 | 180 | Output on a modern terminal: 181 | 182 | .. image:: https://user-images.githubusercontent.com/ 183 | 576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png 184 | 185 | Benchmarks 186 | ---------- 187 | 188 | Speed tests 189 | ~~~~~~~~~~~ 190 | 191 | ================= ============= =========== 192 | Library Method Run Time, s 193 | ================= ============= =========== 194 | libc printf 1.04 195 | libc++ std::ostream 3.05 196 | {fmt} 6.1.1 fmt::print 0.75 197 | Boost Format 1.67 boost::format 7.24 198 | Folly Format folly::format 2.23 199 | ================= ============= =========== 200 | 201 | {fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``. 202 | 203 | The above results were generated by building ``tinyformat_test.cpp`` on macOS 204 | 10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the 205 | best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` 206 | or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for 207 | further details refer to the `source 208 | `_. 209 | 210 | {fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on 211 | floating-point formatting (`dtoa-benchmark `_) 212 | and faster than `double-conversion `_ and 213 | `ryu `_: 214 | 215 | .. image:: https://user-images.githubusercontent.com/576385/ 216 | 95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png 217 | :target: https://fmt.dev/unknown_mac64_clang12.0.html 218 | 219 | Compile time and code bloat 220 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 221 | 222 | The script `bloat-test.py 223 | `_ 224 | from `format-benchmark `_ 225 | tests compile time and code bloat for nontrivial projects. 226 | It generates 100 translation units and uses ``printf()`` or its alternative 227 | five times in each to simulate a medium sized project. The resulting 228 | executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), 229 | macOS Sierra, best of three) is shown in the following tables. 230 | 231 | **Optimized build (-O3)** 232 | 233 | ============= =============== ==================== ================== 234 | Method Compile Time, s Executable size, KiB Stripped size, KiB 235 | ============= =============== ==================== ================== 236 | printf 2.6 29 26 237 | printf+string 16.4 29 26 238 | iostreams 31.1 59 55 239 | {fmt} 19.0 37 34 240 | Boost Format 91.9 226 203 241 | Folly Format 115.7 101 88 242 | ============= =============== ==================== ================== 243 | 244 | As you can see, {fmt} has 60% less overhead in terms of resulting binary code 245 | size compared to iostreams and comes pretty close to ``printf``. Boost Format 246 | and Folly Format have the largest overheads. 247 | 248 | ``printf+string`` is the same as ``printf`` but with extra ```` 249 | include to measure the overhead of the latter. 250 | 251 | **Non-optimized build** 252 | 253 | ============= =============== ==================== ================== 254 | Method Compile Time, s Executable size, KiB Stripped size, KiB 255 | ============= =============== ==================== ================== 256 | printf 2.2 33 30 257 | printf+string 16.0 33 30 258 | iostreams 28.3 56 52 259 | {fmt} 18.2 59 50 260 | Boost Format 54.1 365 303 261 | Folly Format 79.9 445 430 262 | ============= =============== ==================== ================== 263 | 264 | ``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to 265 | compare formatting function overhead only. Boost Format is a 266 | header-only library so it doesn't provide any linkage options. 267 | 268 | Running the tests 269 | ~~~~~~~~~~~~~~~~~ 270 | 271 | Please refer to `Building the library`__ for the instructions on how to build 272 | the library and run the unit tests. 273 | 274 | __ https://fmt.dev/latest/usage.html#building-the-library 275 | 276 | Benchmarks reside in a separate repository, 277 | `format-benchmarks `_, 278 | so to run the benchmarks you first need to clone this repository and 279 | generate Makefiles with CMake:: 280 | 281 | $ git clone --recursive https://github.com/fmtlib/format-benchmark.git 282 | $ cd format-benchmark 283 | $ cmake . 284 | 285 | Then you can run the speed test:: 286 | 287 | $ make speed-test 288 | 289 | or the bloat test:: 290 | 291 | $ make bloat-test 292 | 293 | Migrating code 294 | -------------- 295 | 296 | `clang-tidy-fmt `_ provides clang 297 | tidy checks for converting occurrences of ``printf`` and ``fprintf`` to 298 | ``fmt::print``. 299 | 300 | Projects using this library 301 | --------------------------- 302 | 303 | * `0 A.D. `_: a free, open-source, cross-platform 304 | real-time strategy game 305 | 306 | * `2GIS `_: free business listings with a city map 307 | 308 | * `AMPL/MP `_: 309 | an open-source library for mathematical programming 310 | 311 | * `Aseprite `_: 312 | animated sprite editor & pixel art tool 313 | 314 | * `AvioBook `_: a comprehensive aircraft 315 | operations suite 316 | 317 | * `Blizzard Battle.net `_: an online gaming platform 318 | 319 | * `Celestia `_: real-time 3D visualization of space 320 | 321 | * `Ceph `_: a scalable distributed storage system 322 | 323 | * `ccache `_: a compiler cache 324 | 325 | * `ClickHouse `_: analytical database 326 | management system 327 | 328 | * `CUAUV `_: Cornell University's autonomous underwater 329 | vehicle 330 | 331 | * `Drake `_: a planning, control, and analysis toolbox 332 | for nonlinear dynamical systems (MIT) 333 | 334 | * `Envoy `_: C++ L7 proxy and communication bus 335 | (Lyft) 336 | 337 | * `FiveM `_: a modification framework for GTA V 338 | 339 | * `fmtlog `_: a performant fmtlib-style 340 | logging library with latency in nanoseconds 341 | 342 | * `Folly `_: Facebook open-source library 343 | 344 | * `Grand Mountain Adventure 345 | `_: 346 | A beautiful open-world ski & snowboarding game 347 | 348 | * `HarpyWar/pvpgn `_: 349 | Player vs Player Gaming Network with tweaks 350 | 351 | * `KBEngine `_: an open-source MMOG server 352 | engine 353 | 354 | * `Keypirinha `_: a semantic launcher for Windows 355 | 356 | * `Kodi `_ (formerly xbmc): home theater software 357 | 358 | * `Knuth `_: high-performance Bitcoin full-node 359 | 360 | * `Microsoft Verona `_: 361 | research programming language for concurrent ownership 362 | 363 | * `MongoDB `_: distributed document database 364 | 365 | * `MongoDB Smasher `_: a small tool to 366 | generate randomized datasets 367 | 368 | * `OpenSpace `_: an open-source 369 | astrovisualization framework 370 | 371 | * `PenUltima Online (POL) `_: 372 | an MMO server, compatible with most Ultima Online clients 373 | 374 | * `PyTorch `_: an open-source machine 375 | learning library 376 | 377 | * `quasardb `_: a distributed, high-performance, 378 | associative database 379 | 380 | * `Quill `_: asynchronous low-latency logging library 381 | 382 | * `QKW `_: generalizing aliasing to simplify 383 | navigation, and executing complex multi-line terminal command sequences 384 | 385 | * `redis-cerberus `_: a Redis cluster 386 | proxy 387 | 388 | * `redpanda `_: a 10x faster Kafka® replacement 389 | for mission critical systems written in C++ 390 | 391 | * `rpclib `_: a modern C++ msgpack-RPC server and client 392 | library 393 | 394 | * `Salesforce Analytics Cloud 395 | `_: 396 | business intelligence software 397 | 398 | * `Scylla `_: a Cassandra-compatible NoSQL data store 399 | that can handle 1 million transactions per second on a single server 400 | 401 | * `Seastar `_: an advanced, open-source C++ 402 | framework for high-performance server applications on modern hardware 403 | 404 | * `spdlog `_: super fast C++ logging library 405 | 406 | * `Stellar `_: financial platform 407 | 408 | * `Touch Surgery `_: surgery simulator 409 | 410 | * `TrinityCore `_: open-source 411 | MMORPG framework 412 | 413 | * `Windows Terminal `_: the new Windows 414 | terminal 415 | 416 | `More... `_ 417 | 418 | If you are aware of other projects using this library, please let me know 419 | by `email `_ or by submitting an 420 | `issue `_. 421 | 422 | Motivation 423 | ---------- 424 | 425 | So why yet another formatting library? 426 | 427 | There are plenty of methods for doing this task, from standard ones like 428 | the printf family of function and iostreams to Boost Format and FastFormat 429 | libraries. The reason for creating a new library is that every existing 430 | solution that I found either had serious issues or didn't provide 431 | all the features I needed. 432 | 433 | printf 434 | ~~~~~~ 435 | 436 | The good thing about ``printf`` is that it is pretty fast and readily available 437 | being a part of the C standard library. The main drawback is that it 438 | doesn't support user-defined types. ``printf`` also has safety issues although 439 | they are somewhat mitigated with `__attribute__ ((format (printf, ...)) 440 | `_ in GCC. 441 | There is a POSIX extension that adds positional arguments required for 442 | `i18n `_ 443 | to ``printf`` but it is not a part of C99 and may not be available on some 444 | platforms. 445 | 446 | iostreams 447 | ~~~~~~~~~ 448 | 449 | The main issue with iostreams is best illustrated with an example: 450 | 451 | .. code:: c++ 452 | 453 | std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n"; 454 | 455 | which is a lot of typing compared to printf: 456 | 457 | .. code:: c++ 458 | 459 | printf("%.2f\n", 1.23456); 460 | 461 | Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams 462 | don't support positional arguments by design. 463 | 464 | The good part is that iostreams support user-defined types and are safe although 465 | error handling is awkward. 466 | 467 | Boost Format 468 | ~~~~~~~~~~~~ 469 | 470 | This is a very powerful library which supports both ``printf``-like format 471 | strings and positional arguments. Its main drawback is performance. According to 472 | various benchmarks, it is much slower than other methods considered here. Boost 473 | Format also has excessive build times and severe code bloat issues (see 474 | `Benchmarks`_). 475 | 476 | FastFormat 477 | ~~~~~~~~~~ 478 | 479 | This is an interesting library which is fast, safe and has positional arguments. 480 | However, it has significant limitations, citing its author: 481 | 482 | Three features that have no hope of being accommodated within the 483 | current design are: 484 | 485 | * Leading zeros (or any other non-space padding) 486 | * Octal/hexadecimal encoding 487 | * Runtime width/alignment specification 488 | 489 | It is also quite big and has a heavy dependency, STLSoft, which might be too 490 | restrictive for using it in some projects. 491 | 492 | Boost Spirit.Karma 493 | ~~~~~~~~~~~~~~~~~~ 494 | 495 | This is not really a formatting library but I decided to include it here for 496 | completeness. As iostreams, it suffers from the problem of mixing verbatim text 497 | with arguments. The library is pretty fast, but slower on integer formatting 498 | than ``fmt::format_to`` with format string compilation on Karma's own benchmark, 499 | see `Converting a hundred million integers to strings per second 500 | `_. 501 | 502 | License 503 | ------- 504 | 505 | {fmt} is distributed under the MIT `license 506 | `_. 507 | 508 | Documentation License 509 | --------------------- 510 | 511 | The `Format String Syntax `_ 512 | section in the documentation is based on the one from Python `string module 513 | documentation `_. 514 | For this reason the documentation is distributed under the Python Software 515 | Foundation license available in `doc/python-license.txt 516 | `_. 517 | It only applies if you distribute the documentation of {fmt}. 518 | 519 | Maintainers 520 | ----------- 521 | 522 | The {fmt} library is maintained by Victor Zverovich (`vitaut 523 | `_) and Jonathan Müller (`foonathan 524 | `_) with contributions from many other people. 525 | See `Contributors `_ and 526 | `Releases `_ for some of the names. 527 | Let us know if your contribution is not listed or mentioned incorrectly and 528 | we'll make it right. 529 | -------------------------------------------------------------------------------- /deps/fmt/include/fmt/printf.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - legacy printf implementation 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_PRINTF_H_ 9 | #define FMT_PRINTF_H_ 10 | 11 | #include // std::max 12 | #include // std::numeric_limits 13 | #include 14 | 15 | #include "format.h" 16 | 17 | FMT_BEGIN_NAMESPACE 18 | FMT_MODULE_EXPORT_BEGIN 19 | 20 | template struct printf_formatter { printf_formatter() = delete; }; 21 | 22 | template 23 | class basic_printf_parse_context : public basic_format_parse_context { 24 | using basic_format_parse_context::basic_format_parse_context; 25 | }; 26 | 27 | template class basic_printf_context { 28 | private: 29 | OutputIt out_; 30 | basic_format_args args_; 31 | 32 | public: 33 | using char_type = Char; 34 | using format_arg = basic_format_arg; 35 | using parse_context_type = basic_printf_parse_context; 36 | template using formatter_type = printf_formatter; 37 | 38 | /** 39 | \rst 40 | Constructs a ``printf_context`` object. References to the arguments are 41 | stored in the context object so make sure they have appropriate lifetimes. 42 | \endrst 43 | */ 44 | basic_printf_context(OutputIt out, 45 | basic_format_args args) 46 | : out_(out), args_(args) {} 47 | 48 | OutputIt out() { return out_; } 49 | void advance_to(OutputIt it) { out_ = it; } 50 | 51 | detail::locale_ref locale() { return {}; } 52 | 53 | format_arg arg(int id) const { return args_.get(id); } 54 | 55 | FMT_CONSTEXPR void on_error(const char* message) { 56 | detail::error_handler().on_error(message); 57 | } 58 | }; 59 | 60 | FMT_BEGIN_DETAIL_NAMESPACE 61 | 62 | // Checks if a value fits in int - used to avoid warnings about comparing 63 | // signed and unsigned integers. 64 | template struct int_checker { 65 | template static bool fits_in_int(T value) { 66 | unsigned max = max_value(); 67 | return value <= max; 68 | } 69 | static bool fits_in_int(bool) { return true; } 70 | }; 71 | 72 | template <> struct int_checker { 73 | template static bool fits_in_int(T value) { 74 | return value >= (std::numeric_limits::min)() && 75 | value <= max_value(); 76 | } 77 | static bool fits_in_int(int) { return true; } 78 | }; 79 | 80 | class printf_precision_handler { 81 | public: 82 | template ::value)> 83 | int operator()(T value) { 84 | if (!int_checker::is_signed>::fits_in_int(value)) 85 | FMT_THROW(format_error("number is too big")); 86 | return (std::max)(static_cast(value), 0); 87 | } 88 | 89 | template ::value)> 90 | int operator()(T) { 91 | FMT_THROW(format_error("precision is not integer")); 92 | return 0; 93 | } 94 | }; 95 | 96 | // An argument visitor that returns true iff arg is a zero integer. 97 | class is_zero_int { 98 | public: 99 | template ::value)> 100 | bool operator()(T value) { 101 | return value == 0; 102 | } 103 | 104 | template ::value)> 105 | bool operator()(T) { 106 | return false; 107 | } 108 | }; 109 | 110 | template struct make_unsigned_or_bool : std::make_unsigned {}; 111 | 112 | template <> struct make_unsigned_or_bool { using type = bool; }; 113 | 114 | template class arg_converter { 115 | private: 116 | using char_type = typename Context::char_type; 117 | 118 | basic_format_arg& arg_; 119 | char_type type_; 120 | 121 | public: 122 | arg_converter(basic_format_arg& arg, char_type type) 123 | : arg_(arg), type_(type) {} 124 | 125 | void operator()(bool value) { 126 | if (type_ != 's') operator()(value); 127 | } 128 | 129 | template ::value)> 130 | void operator()(U value) { 131 | bool is_signed = type_ == 'd' || type_ == 'i'; 132 | using target_type = conditional_t::value, U, T>; 133 | if (const_check(sizeof(target_type) <= sizeof(int))) { 134 | // Extra casts are used to silence warnings. 135 | if (is_signed) { 136 | arg_ = detail::make_arg( 137 | static_cast(static_cast(value))); 138 | } else { 139 | using unsigned_type = typename make_unsigned_or_bool::type; 140 | arg_ = detail::make_arg( 141 | static_cast(static_cast(value))); 142 | } 143 | } else { 144 | if (is_signed) { 145 | // glibc's printf doesn't sign extend arguments of smaller types: 146 | // std::printf("%lld", -42); // prints "4294967254" 147 | // but we don't have to do the same because it's a UB. 148 | arg_ = detail::make_arg(static_cast(value)); 149 | } else { 150 | arg_ = detail::make_arg( 151 | static_cast::type>(value)); 152 | } 153 | } 154 | } 155 | 156 | template ::value)> 157 | void operator()(U) {} // No conversion needed for non-integral types. 158 | }; 159 | 160 | // Converts an integer argument to T for printf, if T is an integral type. 161 | // If T is void, the argument is converted to corresponding signed or unsigned 162 | // type depending on the type specifier: 'd' and 'i' - signed, other - 163 | // unsigned). 164 | template 165 | void convert_arg(basic_format_arg& arg, Char type) { 166 | visit_format_arg(arg_converter(arg, type), arg); 167 | } 168 | 169 | // Converts an integer argument to char for printf. 170 | template class char_converter { 171 | private: 172 | basic_format_arg& arg_; 173 | 174 | public: 175 | explicit char_converter(basic_format_arg& arg) : arg_(arg) {} 176 | 177 | template ::value)> 178 | void operator()(T value) { 179 | arg_ = detail::make_arg( 180 | static_cast(value)); 181 | } 182 | 183 | template ::value)> 184 | void operator()(T) {} // No conversion needed for non-integral types. 185 | }; 186 | 187 | // An argument visitor that return a pointer to a C string if argument is a 188 | // string or null otherwise. 189 | template struct get_cstring { 190 | template const Char* operator()(T) { return nullptr; } 191 | const Char* operator()(const Char* s) { return s; } 192 | }; 193 | 194 | // Checks if an argument is a valid printf width specifier and sets 195 | // left alignment if it is negative. 196 | template class printf_width_handler { 197 | private: 198 | using format_specs = basic_format_specs; 199 | 200 | format_specs& specs_; 201 | 202 | public: 203 | explicit printf_width_handler(format_specs& specs) : specs_(specs) {} 204 | 205 | template ::value)> 206 | unsigned operator()(T value) { 207 | auto width = static_cast>(value); 208 | if (detail::is_negative(value)) { 209 | specs_.align = align::left; 210 | width = 0 - width; 211 | } 212 | unsigned int_max = max_value(); 213 | if (width > int_max) FMT_THROW(format_error("number is too big")); 214 | return static_cast(width); 215 | } 216 | 217 | template ::value)> 218 | unsigned operator()(T) { 219 | FMT_THROW(format_error("width is not integer")); 220 | return 0; 221 | } 222 | }; 223 | 224 | // The ``printf`` argument formatter. 225 | template 226 | class printf_arg_formatter : public arg_formatter { 227 | private: 228 | using base = arg_formatter; 229 | using context_type = basic_printf_context; 230 | using format_specs = basic_format_specs; 231 | 232 | context_type& context_; 233 | 234 | OutputIt write_null_pointer(bool is_string = false) { 235 | auto s = this->specs; 236 | s.type = presentation_type::none; 237 | return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); 238 | } 239 | 240 | public: 241 | printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx) 242 | : base{iter, s, locale_ref()}, context_(ctx) {} 243 | 244 | OutputIt operator()(monostate value) { return base::operator()(value); } 245 | 246 | template ::value)> 247 | OutputIt operator()(T value) { 248 | // MSVC2013 fails to compile separate overloads for bool and Char so use 249 | // std::is_same instead. 250 | if (std::is_same::value) { 251 | format_specs fmt_specs = this->specs; 252 | if (fmt_specs.type != presentation_type::none && 253 | fmt_specs.type != presentation_type::chr) { 254 | return (*this)(static_cast(value)); 255 | } 256 | fmt_specs.sign = sign::none; 257 | fmt_specs.alt = false; 258 | fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. 259 | // align::numeric needs to be overwritten here since the '0' flag is 260 | // ignored for non-numeric types 261 | if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) 262 | fmt_specs.align = align::right; 263 | return write(this->out, static_cast(value), fmt_specs); 264 | } 265 | return base::operator()(value); 266 | } 267 | 268 | template ::value)> 269 | OutputIt operator()(T value) { 270 | return base::operator()(value); 271 | } 272 | 273 | /** Formats a null-terminated C string. */ 274 | OutputIt operator()(const char* value) { 275 | if (value) return base::operator()(value); 276 | return write_null_pointer(this->specs.type != presentation_type::pointer); 277 | } 278 | 279 | /** Formats a null-terminated wide C string. */ 280 | OutputIt operator()(const wchar_t* value) { 281 | if (value) return base::operator()(value); 282 | return write_null_pointer(this->specs.type != presentation_type::pointer); 283 | } 284 | 285 | OutputIt operator()(basic_string_view value) { 286 | return base::operator()(value); 287 | } 288 | 289 | /** Formats a pointer. */ 290 | OutputIt operator()(const void* value) { 291 | return value ? base::operator()(value) : write_null_pointer(); 292 | } 293 | 294 | /** Formats an argument of a custom (user-defined) type. */ 295 | OutputIt operator()(typename basic_format_arg::handle handle) { 296 | auto parse_ctx = 297 | basic_printf_parse_context(basic_string_view()); 298 | handle.format(parse_ctx, context_); 299 | return this->out; 300 | } 301 | }; 302 | 303 | template 304 | void parse_flags(basic_format_specs& specs, const Char*& it, 305 | const Char* end) { 306 | for (; it != end; ++it) { 307 | switch (*it) { 308 | case '-': 309 | specs.align = align::left; 310 | break; 311 | case '+': 312 | specs.sign = sign::plus; 313 | break; 314 | case '0': 315 | specs.fill[0] = '0'; 316 | break; 317 | case ' ': 318 | if (specs.sign != sign::plus) { 319 | specs.sign = sign::space; 320 | } 321 | break; 322 | case '#': 323 | specs.alt = true; 324 | break; 325 | default: 326 | return; 327 | } 328 | } 329 | } 330 | 331 | template 332 | int parse_header(const Char*& it, const Char* end, 333 | basic_format_specs& specs, GetArg get_arg) { 334 | int arg_index = -1; 335 | Char c = *it; 336 | if (c >= '0' && c <= '9') { 337 | // Parse an argument index (if followed by '$') or a width possibly 338 | // preceded with '0' flag(s). 339 | int value = parse_nonnegative_int(it, end, -1); 340 | if (it != end && *it == '$') { // value is an argument index 341 | ++it; 342 | arg_index = value != -1 ? value : max_value(); 343 | } else { 344 | if (c == '0') specs.fill[0] = '0'; 345 | if (value != 0) { 346 | // Nonzero value means that we parsed width and don't need to 347 | // parse it or flags again, so return now. 348 | if (value == -1) FMT_THROW(format_error("number is too big")); 349 | specs.width = value; 350 | return arg_index; 351 | } 352 | } 353 | } 354 | parse_flags(specs, it, end); 355 | // Parse width. 356 | if (it != end) { 357 | if (*it >= '0' && *it <= '9') { 358 | specs.width = parse_nonnegative_int(it, end, -1); 359 | if (specs.width == -1) FMT_THROW(format_error("number is too big")); 360 | } else if (*it == '*') { 361 | ++it; 362 | specs.width = static_cast(visit_format_arg( 363 | detail::printf_width_handler(specs), get_arg(-1))); 364 | } 365 | } 366 | return arg_index; 367 | } 368 | 369 | template 370 | void vprintf(buffer& buf, basic_string_view format, 371 | basic_format_args args) { 372 | using OutputIt = buffer_appender; 373 | auto out = OutputIt(buf); 374 | auto context = basic_printf_context(out, args); 375 | auto parse_ctx = basic_printf_parse_context(format); 376 | 377 | // Returns the argument with specified index or, if arg_index is -1, the next 378 | // argument. 379 | auto get_arg = [&](int arg_index) { 380 | if (arg_index < 0) 381 | arg_index = parse_ctx.next_arg_id(); 382 | else 383 | parse_ctx.check_arg_id(--arg_index); 384 | return detail::get_arg(context, arg_index); 385 | }; 386 | 387 | const Char* start = parse_ctx.begin(); 388 | const Char* end = parse_ctx.end(); 389 | auto it = start; 390 | while (it != end) { 391 | if (!detail::find(it, end, '%', it)) { 392 | it = end; // detail::find leaves it == nullptr if it doesn't find '%' 393 | break; 394 | } 395 | Char c = *it++; 396 | if (it != end && *it == c) { 397 | out = detail::write( 398 | out, basic_string_view(start, detail::to_unsigned(it - start))); 399 | start = ++it; 400 | continue; 401 | } 402 | out = detail::write(out, basic_string_view( 403 | start, detail::to_unsigned(it - 1 - start))); 404 | 405 | basic_format_specs specs; 406 | specs.align = align::right; 407 | 408 | // Parse argument index, flags and width. 409 | int arg_index = parse_header(it, end, specs, get_arg); 410 | if (arg_index == 0) parse_ctx.on_error("argument not found"); 411 | 412 | // Parse precision. 413 | if (it != end && *it == '.') { 414 | ++it; 415 | c = it != end ? *it : 0; 416 | if ('0' <= c && c <= '9') { 417 | specs.precision = parse_nonnegative_int(it, end, 0); 418 | } else if (c == '*') { 419 | ++it; 420 | specs.precision = static_cast( 421 | visit_format_arg(detail::printf_precision_handler(), get_arg(-1))); 422 | } else { 423 | specs.precision = 0; 424 | } 425 | } 426 | 427 | auto arg = get_arg(arg_index); 428 | // For d, i, o, u, x, and X conversion specifiers, if a precision is 429 | // specified, the '0' flag is ignored 430 | if (specs.precision >= 0 && arg.is_integral()) 431 | specs.fill[0] = 432 | ' '; // Ignore '0' flag for non-numeric types or if '-' present. 433 | if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { 434 | auto str = visit_format_arg(detail::get_cstring(), arg); 435 | auto str_end = str + specs.precision; 436 | auto nul = std::find(str, str_end, Char()); 437 | arg = detail::make_arg>( 438 | basic_string_view( 439 | str, detail::to_unsigned(nul != str_end ? nul - str 440 | : specs.precision))); 441 | } 442 | if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) 443 | specs.alt = false; 444 | if (specs.fill[0] == '0') { 445 | if (arg.is_arithmetic() && specs.align != align::left) 446 | specs.align = align::numeric; 447 | else 448 | specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' 449 | // flag is also present. 450 | } 451 | 452 | // Parse length and convert the argument to the required type. 453 | c = it != end ? *it++ : 0; 454 | Char t = it != end ? *it : 0; 455 | using detail::convert_arg; 456 | switch (c) { 457 | case 'h': 458 | if (t == 'h') { 459 | ++it; 460 | t = it != end ? *it : 0; 461 | convert_arg(arg, t); 462 | } else { 463 | convert_arg(arg, t); 464 | } 465 | break; 466 | case 'l': 467 | if (t == 'l') { 468 | ++it; 469 | t = it != end ? *it : 0; 470 | convert_arg(arg, t); 471 | } else { 472 | convert_arg(arg, t); 473 | } 474 | break; 475 | case 'j': 476 | convert_arg(arg, t); 477 | break; 478 | case 'z': 479 | convert_arg(arg, t); 480 | break; 481 | case 't': 482 | convert_arg(arg, t); 483 | break; 484 | case 'L': 485 | // printf produces garbage when 'L' is omitted for long double, no 486 | // need to do the same. 487 | break; 488 | default: 489 | --it; 490 | convert_arg(arg, c); 491 | } 492 | 493 | // Parse type. 494 | if (it == end) FMT_THROW(format_error("invalid format string")); 495 | char type = static_cast(*it++); 496 | if (arg.is_integral()) { 497 | // Normalize type. 498 | switch (type) { 499 | case 'i': 500 | case 'u': 501 | type = 'd'; 502 | break; 503 | case 'c': 504 | visit_format_arg( 505 | detail::char_converter>(arg), 506 | arg); 507 | break; 508 | } 509 | } 510 | specs.type = parse_presentation_type(type); 511 | if (specs.type == presentation_type::none) 512 | parse_ctx.on_error("invalid type specifier"); 513 | 514 | start = it; 515 | 516 | // Format argument. 517 | out = visit_format_arg( 518 | detail::printf_arg_formatter(out, specs, context), arg); 519 | } 520 | detail::write(out, basic_string_view(start, to_unsigned(it - start))); 521 | } 522 | FMT_END_DETAIL_NAMESPACE 523 | 524 | template 525 | using basic_printf_context_t = 526 | basic_printf_context, Char>; 527 | 528 | using printf_context = basic_printf_context_t; 529 | using wprintf_context = basic_printf_context_t; 530 | 531 | using printf_args = basic_format_args; 532 | using wprintf_args = basic_format_args; 533 | 534 | /** 535 | \rst 536 | Constructs an `~fmt::format_arg_store` object that contains references to 537 | arguments and can be implicitly converted to `~fmt::printf_args`. 538 | \endrst 539 | */ 540 | template 541 | inline auto make_printf_args(const T&... args) 542 | -> format_arg_store { 543 | return {args...}; 544 | } 545 | 546 | /** 547 | \rst 548 | Constructs an `~fmt::format_arg_store` object that contains references to 549 | arguments and can be implicitly converted to `~fmt::wprintf_args`. 550 | \endrst 551 | */ 552 | template 553 | inline auto make_wprintf_args(const T&... args) 554 | -> format_arg_store { 555 | return {args...}; 556 | } 557 | 558 | template > 559 | inline auto vsprintf( 560 | const S& fmt, 561 | basic_format_args>> args) 562 | -> std::basic_string { 563 | basic_memory_buffer buffer; 564 | vprintf(buffer, to_string_view(fmt), args); 565 | return to_string(buffer); 566 | } 567 | 568 | /** 569 | \rst 570 | Formats arguments and returns the result as a string. 571 | 572 | **Example**:: 573 | 574 | std::string message = fmt::sprintf("The answer is %d", 42); 575 | \endrst 576 | */ 577 | template ::value, char_t>> 579 | inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { 580 | using context = basic_printf_context_t; 581 | return vsprintf(to_string_view(fmt), fmt::make_format_args(args...)); 582 | } 583 | 584 | template > 585 | inline auto vfprintf( 586 | std::FILE* f, const S& fmt, 587 | basic_format_args>> args) 588 | -> int { 589 | basic_memory_buffer buffer; 590 | vprintf(buffer, to_string_view(fmt), args); 591 | size_t size = buffer.size(); 592 | return std::fwrite(buffer.data(), sizeof(Char), size, f) < size 593 | ? -1 594 | : static_cast(size); 595 | } 596 | 597 | /** 598 | \rst 599 | Prints formatted data to the file *f*. 600 | 601 | **Example**:: 602 | 603 | fmt::fprintf(stderr, "Don't %s!", "panic"); 604 | \endrst 605 | */ 606 | template > 607 | inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { 608 | using context = basic_printf_context_t; 609 | return vfprintf(f, to_string_view(fmt), 610 | fmt::make_format_args(args...)); 611 | } 612 | 613 | template > 614 | inline auto vprintf( 615 | const S& fmt, 616 | basic_format_args>> args) 617 | -> int { 618 | return vfprintf(stdout, to_string_view(fmt), args); 619 | } 620 | 621 | /** 622 | \rst 623 | Prints formatted data to ``stdout``. 624 | 625 | **Example**:: 626 | 627 | fmt::printf("Elapsed time: %.2f seconds", 1.23); 628 | \endrst 629 | */ 630 | template ::value)> 631 | inline auto printf(const S& fmt, const T&... args) -> int { 632 | return vprintf( 633 | to_string_view(fmt), 634 | fmt::make_format_args>>(args...)); 635 | } 636 | 637 | template > 638 | FMT_DEPRECATED auto vfprintf( 639 | std::basic_ostream& os, const S& fmt, 640 | basic_format_args>> args) 641 | -> int { 642 | basic_memory_buffer buffer; 643 | vprintf(buffer, to_string_view(fmt), args); 644 | os.write(buffer.data(), static_cast(buffer.size())); 645 | return static_cast(buffer.size()); 646 | } 647 | template > 648 | FMT_DEPRECATED auto fprintf(std::basic_ostream& os, const S& fmt, 649 | const T&... args) -> int { 650 | return vfprintf(os, to_string_view(fmt), 651 | fmt::make_format_args>(args...)); 652 | } 653 | 654 | FMT_MODULE_EXPORT_END 655 | FMT_END_NAMESPACE 656 | 657 | #endif // FMT_PRINTF_H_ 658 | -------------------------------------------------------------------------------- /deps/fmt/include/fmt/compile.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental format string compilation 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich and fmt contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_COMPILE_H_ 9 | #define FMT_COMPILE_H_ 10 | 11 | #include "format.h" 12 | 13 | FMT_BEGIN_NAMESPACE 14 | namespace detail { 15 | 16 | // An output iterator that counts the number of objects written to it and 17 | // discards them. 18 | class counting_iterator { 19 | private: 20 | size_t count_; 21 | 22 | public: 23 | using iterator_category = std::output_iterator_tag; 24 | using difference_type = std::ptrdiff_t; 25 | using pointer = void; 26 | using reference = void; 27 | using _Unchecked_type = counting_iterator; // Mark iterator as checked. 28 | 29 | struct value_type { 30 | template void operator=(const T&) {} 31 | }; 32 | 33 | counting_iterator() : count_(0) {} 34 | 35 | size_t count() const { return count_; } 36 | 37 | counting_iterator& operator++() { 38 | ++count_; 39 | return *this; 40 | } 41 | counting_iterator operator++(int) { 42 | auto it = *this; 43 | ++*this; 44 | return it; 45 | } 46 | 47 | friend counting_iterator operator+(counting_iterator it, difference_type n) { 48 | it.count_ += static_cast(n); 49 | return it; 50 | } 51 | 52 | value_type operator*() const { return {}; } 53 | }; 54 | 55 | template 56 | inline counting_iterator copy_str(InputIt begin, InputIt end, 57 | counting_iterator it) { 58 | return it + (end - begin); 59 | } 60 | 61 | template class truncating_iterator_base { 62 | protected: 63 | OutputIt out_; 64 | size_t limit_; 65 | size_t count_ = 0; 66 | 67 | truncating_iterator_base() : out_(), limit_(0) {} 68 | 69 | truncating_iterator_base(OutputIt out, size_t limit) 70 | : out_(out), limit_(limit) {} 71 | 72 | public: 73 | using iterator_category = std::output_iterator_tag; 74 | using value_type = typename std::iterator_traits::value_type; 75 | using difference_type = std::ptrdiff_t; 76 | using pointer = void; 77 | using reference = void; 78 | using _Unchecked_type = 79 | truncating_iterator_base; // Mark iterator as checked. 80 | 81 | OutputIt base() const { return out_; } 82 | size_t count() const { return count_; } 83 | }; 84 | 85 | // An output iterator that truncates the output and counts the number of objects 86 | // written to it. 87 | template ::value_type>::type> 90 | class truncating_iterator; 91 | 92 | template 93 | class truncating_iterator 94 | : public truncating_iterator_base { 95 | mutable typename truncating_iterator_base::value_type blackhole_; 96 | 97 | public: 98 | using value_type = typename truncating_iterator_base::value_type; 99 | 100 | truncating_iterator() = default; 101 | 102 | truncating_iterator(OutputIt out, size_t limit) 103 | : truncating_iterator_base(out, limit) {} 104 | 105 | truncating_iterator& operator++() { 106 | if (this->count_++ < this->limit_) ++this->out_; 107 | return *this; 108 | } 109 | 110 | truncating_iterator operator++(int) { 111 | auto it = *this; 112 | ++*this; 113 | return it; 114 | } 115 | 116 | value_type& operator*() const { 117 | return this->count_ < this->limit_ ? *this->out_ : blackhole_; 118 | } 119 | }; 120 | 121 | template 122 | class truncating_iterator 123 | : public truncating_iterator_base { 124 | public: 125 | truncating_iterator() = default; 126 | 127 | truncating_iterator(OutputIt out, size_t limit) 128 | : truncating_iterator_base(out, limit) {} 129 | 130 | template truncating_iterator& operator=(T val) { 131 | if (this->count_++ < this->limit_) *this->out_++ = val; 132 | return *this; 133 | } 134 | 135 | truncating_iterator& operator++() { return *this; } 136 | truncating_iterator& operator++(int) { return *this; } 137 | truncating_iterator& operator*() { return *this; } 138 | }; 139 | 140 | // A compile-time string which is compiled into fast formatting code. 141 | class compiled_string {}; 142 | 143 | template 144 | struct is_compiled_string : std::is_base_of {}; 145 | 146 | /** 147 | \rst 148 | Converts a string literal *s* into a format string that will be parsed at 149 | compile time and converted into efficient formatting code. Requires C++17 150 | ``constexpr if`` compiler support. 151 | 152 | **Example**:: 153 | 154 | // Converts 42 into std::string using the most efficient method and no 155 | // runtime format string processing. 156 | std::string s = fmt::format(FMT_COMPILE("{}"), 42); 157 | \endrst 158 | */ 159 | #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 160 | # define FMT_COMPILE(s) \ 161 | FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) 162 | #else 163 | # define FMT_COMPILE(s) FMT_STRING(s) 164 | #endif 165 | 166 | #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 167 | template Str> 169 | struct udl_compiled_string : compiled_string { 170 | using char_type = Char; 171 | constexpr operator basic_string_view() const { 172 | return {Str.data, N - 1}; 173 | } 174 | }; 175 | #endif 176 | 177 | template 178 | const T& first(const T& value, const Tail&...) { 179 | return value; 180 | } 181 | 182 | #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 183 | template struct type_list {}; 184 | 185 | // Returns a reference to the argument at index N from [first, rest...]. 186 | template 187 | constexpr const auto& get([[maybe_unused]] const T& first, 188 | [[maybe_unused]] const Args&... rest) { 189 | static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); 190 | if constexpr (N == 0) 191 | return first; 192 | else 193 | return detail::get(rest...); 194 | } 195 | 196 | template 197 | constexpr int get_arg_index_by_name(basic_string_view name, 198 | type_list) { 199 | return get_arg_index_by_name(name); 200 | } 201 | 202 | template struct get_type_impl; 203 | 204 | template struct get_type_impl> { 205 | using type = 206 | remove_cvref_t(std::declval()...))>; 207 | }; 208 | 209 | template 210 | using get_type = typename get_type_impl::type; 211 | 212 | template struct is_compiled_format : std::false_type {}; 213 | 214 | template struct text { 215 | basic_string_view data; 216 | using char_type = Char; 217 | 218 | template 219 | constexpr OutputIt format(OutputIt out, const Args&...) const { 220 | return write(out, data); 221 | } 222 | }; 223 | 224 | template 225 | struct is_compiled_format> : std::true_type {}; 226 | 227 | template 228 | constexpr text make_text(basic_string_view s, size_t pos, 229 | size_t size) { 230 | return {{&s[pos], size}}; 231 | } 232 | 233 | template struct code_unit { 234 | Char value; 235 | using char_type = Char; 236 | 237 | template 238 | constexpr OutputIt format(OutputIt out, const Args&...) const { 239 | return write(out, value); 240 | } 241 | }; 242 | 243 | // This ensures that the argument type is convertible to `const T&`. 244 | template 245 | constexpr const T& get_arg_checked(const Args&... args) { 246 | const auto& arg = detail::get(args...); 247 | if constexpr (detail::is_named_arg>()) { 248 | return arg.value; 249 | } else { 250 | return arg; 251 | } 252 | } 253 | 254 | template 255 | struct is_compiled_format> : std::true_type {}; 256 | 257 | // A replacement field that refers to argument N. 258 | template struct field { 259 | using char_type = Char; 260 | 261 | template 262 | constexpr OutputIt format(OutputIt out, const Args&... args) const { 263 | return write(out, get_arg_checked(args...)); 264 | } 265 | }; 266 | 267 | template 268 | struct is_compiled_format> : std::true_type {}; 269 | 270 | // A replacement field that refers to argument with name. 271 | template struct runtime_named_field { 272 | using char_type = Char; 273 | basic_string_view name; 274 | 275 | template 276 | constexpr static bool try_format_argument( 277 | OutputIt& out, 278 | // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 279 | [[maybe_unused]] basic_string_view arg_name, const T& arg) { 280 | if constexpr (is_named_arg::type>::value) { 281 | if (arg_name == arg.name) { 282 | out = write(out, arg.value); 283 | return true; 284 | } 285 | } 286 | return false; 287 | } 288 | 289 | template 290 | constexpr OutputIt format(OutputIt out, const Args&... args) const { 291 | bool found = (try_format_argument(out, name, args) || ...); 292 | if (!found) { 293 | FMT_THROW(format_error("argument with specified name is not found")); 294 | } 295 | return out; 296 | } 297 | }; 298 | 299 | template 300 | struct is_compiled_format> : std::true_type {}; 301 | 302 | // A replacement field that refers to argument N and has format specifiers. 303 | template struct spec_field { 304 | using char_type = Char; 305 | formatter fmt; 306 | 307 | template 308 | constexpr FMT_INLINE OutputIt format(OutputIt out, 309 | const Args&... args) const { 310 | const auto& vargs = 311 | fmt::make_format_args>(args...); 312 | basic_format_context ctx(out, vargs); 313 | return fmt.format(get_arg_checked(args...), ctx); 314 | } 315 | }; 316 | 317 | template 318 | struct is_compiled_format> : std::true_type {}; 319 | 320 | template struct concat { 321 | L lhs; 322 | R rhs; 323 | using char_type = typename L::char_type; 324 | 325 | template 326 | constexpr OutputIt format(OutputIt out, const Args&... args) const { 327 | out = lhs.format(out, args...); 328 | return rhs.format(out, args...); 329 | } 330 | }; 331 | 332 | template 333 | struct is_compiled_format> : std::true_type {}; 334 | 335 | template 336 | constexpr concat make_concat(L lhs, R rhs) { 337 | return {lhs, rhs}; 338 | } 339 | 340 | struct unknown_format {}; 341 | 342 | template 343 | constexpr size_t parse_text(basic_string_view str, size_t pos) { 344 | for (size_t size = str.size(); pos != size; ++pos) { 345 | if (str[pos] == '{' || str[pos] == '}') break; 346 | } 347 | return pos; 348 | } 349 | 350 | template 351 | constexpr auto compile_format_string(S format_str); 352 | 353 | template 354 | constexpr auto parse_tail(T head, S format_str) { 355 | if constexpr (POS != 356 | basic_string_view(format_str).size()) { 357 | constexpr auto tail = compile_format_string(format_str); 358 | if constexpr (std::is_same, 359 | unknown_format>()) 360 | return tail; 361 | else 362 | return make_concat(head, tail); 363 | } else { 364 | return head; 365 | } 366 | } 367 | 368 | template struct parse_specs_result { 369 | formatter fmt; 370 | size_t end; 371 | int next_arg_id; 372 | }; 373 | 374 | constexpr int manual_indexing_id = -1; 375 | 376 | template 377 | constexpr parse_specs_result parse_specs(basic_string_view str, 378 | size_t pos, int next_arg_id) { 379 | str.remove_prefix(pos); 380 | auto ctx = basic_format_parse_context(str, {}, next_arg_id); 381 | auto f = formatter(); 382 | auto end = f.parse(ctx); 383 | return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, 384 | next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; 385 | } 386 | 387 | template struct arg_id_handler { 388 | arg_ref arg_id; 389 | 390 | constexpr int operator()() { 391 | FMT_ASSERT(false, "handler cannot be used with automatic indexing"); 392 | return 0; 393 | } 394 | constexpr int operator()(int id) { 395 | arg_id = arg_ref(id); 396 | return 0; 397 | } 398 | constexpr int operator()(basic_string_view id) { 399 | arg_id = arg_ref(id); 400 | return 0; 401 | } 402 | 403 | constexpr void on_error(const char* message) { 404 | FMT_THROW(format_error(message)); 405 | } 406 | }; 407 | 408 | template struct parse_arg_id_result { 409 | arg_ref arg_id; 410 | const Char* arg_id_end; 411 | }; 412 | 413 | template 414 | constexpr auto parse_arg_id(const Char* begin, const Char* end) { 415 | auto handler = arg_id_handler{arg_ref{}}; 416 | auto arg_id_end = parse_arg_id(begin, end, handler); 417 | return parse_arg_id_result{handler.arg_id, arg_id_end}; 418 | } 419 | 420 | template struct field_type { 421 | using type = remove_cvref_t; 422 | }; 423 | 424 | template 425 | struct field_type::value>> { 426 | using type = remove_cvref_t; 427 | }; 428 | 429 | template 431 | constexpr auto parse_replacement_field_then_tail(S format_str) { 432 | using char_type = typename S::char_type; 433 | constexpr auto str = basic_string_view(format_str); 434 | constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); 435 | if constexpr (c == '}') { 436 | return parse_tail( 437 | field::type, ARG_INDEX>(), 438 | format_str); 439 | } else if constexpr (c == ':') { 440 | constexpr auto result = parse_specs::type>( 441 | str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); 442 | return parse_tail( 443 | spec_field::type, ARG_INDEX>{ 444 | result.fmt}, 445 | format_str); 446 | } 447 | } 448 | 449 | // Compiles a non-empty format string and returns the compiled representation 450 | // or unknown_format() on unrecognized input. 451 | template 452 | constexpr auto compile_format_string(S format_str) { 453 | using char_type = typename S::char_type; 454 | constexpr auto str = basic_string_view(format_str); 455 | if constexpr (str[POS] == '{') { 456 | if constexpr (POS + 1 == str.size()) 457 | FMT_THROW(format_error("unmatched '{' in format string")); 458 | if constexpr (str[POS + 1] == '{') { 459 | return parse_tail(make_text(str, POS, 1), format_str); 460 | } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { 461 | static_assert(ID != manual_indexing_id, 462 | "cannot switch from manual to automatic argument indexing"); 463 | constexpr auto next_id = 464 | ID != manual_indexing_id ? ID + 1 : manual_indexing_id; 465 | return parse_replacement_field_then_tail, Args, 466 | POS + 1, ID, next_id>( 467 | format_str); 468 | } else { 469 | constexpr auto arg_id_result = 470 | parse_arg_id(str.data() + POS + 1, str.data() + str.size()); 471 | constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); 472 | constexpr char_type c = 473 | arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); 474 | static_assert(c == '}' || c == ':', "missing '}' in format string"); 475 | if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { 476 | static_assert( 477 | ID == manual_indexing_id || ID == 0, 478 | "cannot switch from automatic to manual argument indexing"); 479 | constexpr auto arg_index = arg_id_result.arg_id.val.index; 480 | return parse_replacement_field_then_tail, 481 | Args, arg_id_end_pos, 482 | arg_index, manual_indexing_id>( 483 | format_str); 484 | } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { 485 | constexpr auto arg_index = 486 | get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); 487 | if constexpr (arg_index != invalid_arg_index) { 488 | constexpr auto next_id = 489 | ID != manual_indexing_id ? ID + 1 : manual_indexing_id; 490 | return parse_replacement_field_then_tail< 491 | decltype(get_type::value), Args, arg_id_end_pos, 492 | arg_index, next_id>(format_str); 493 | } else { 494 | if constexpr (c == '}') { 495 | return parse_tail( 496 | runtime_named_field{arg_id_result.arg_id.val.name}, 497 | format_str); 498 | } else if constexpr (c == ':') { 499 | return unknown_format(); // no type info for specs parsing 500 | } 501 | } 502 | } 503 | } 504 | } else if constexpr (str[POS] == '}') { 505 | if constexpr (POS + 1 == str.size()) 506 | FMT_THROW(format_error("unmatched '}' in format string")); 507 | return parse_tail(make_text(str, POS, 1), format_str); 508 | } else { 509 | constexpr auto end = parse_text(str, POS + 1); 510 | if constexpr (end - POS > 1) { 511 | return parse_tail(make_text(str, POS, end - POS), 512 | format_str); 513 | } else { 514 | return parse_tail(code_unit{str[POS]}, 515 | format_str); 516 | } 517 | } 518 | } 519 | 520 | template ::value)> 522 | constexpr auto compile(S format_str) { 523 | constexpr auto str = basic_string_view(format_str); 524 | if constexpr (str.size() == 0) { 525 | return detail::make_text(str, 0, 0); 526 | } else { 527 | constexpr auto result = 528 | detail::compile_format_string, 0, 0>( 529 | format_str); 530 | return result; 531 | } 532 | } 533 | #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 534 | } // namespace detail 535 | 536 | FMT_MODULE_EXPORT_BEGIN 537 | 538 | #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 539 | 540 | template ::value)> 543 | FMT_INLINE std::basic_string format(const CompiledFormat& cf, 544 | const Args&... args) { 545 | auto s = std::basic_string(); 546 | cf.format(std::back_inserter(s), args...); 547 | return s; 548 | } 549 | 550 | template ::value)> 552 | constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, 553 | const Args&... args) { 554 | return cf.format(out, args...); 555 | } 556 | 557 | template ::value)> 559 | FMT_INLINE std::basic_string format(const S&, 560 | Args&&... args) { 561 | if constexpr (std::is_same::value) { 562 | constexpr auto str = basic_string_view(S()); 563 | if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { 564 | const auto& first = detail::first(args...); 565 | if constexpr (detail::is_named_arg< 566 | remove_cvref_t>::value) { 567 | return fmt::to_string(first.value); 568 | } else { 569 | return fmt::to_string(first); 570 | } 571 | } 572 | } 573 | constexpr auto compiled = detail::compile(S()); 574 | if constexpr (std::is_same, 575 | detail::unknown_format>()) { 576 | return format(static_cast>(S()), 577 | std::forward(args)...); 578 | } else { 579 | return format(compiled, std::forward(args)...); 580 | } 581 | } 582 | 583 | template ::value)> 585 | FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { 586 | constexpr auto compiled = detail::compile(S()); 587 | if constexpr (std::is_same, 588 | detail::unknown_format>()) { 589 | return format_to(out, 590 | static_cast>(S()), 591 | std::forward(args)...); 592 | } else { 593 | return format_to(out, compiled, std::forward(args)...); 594 | } 595 | } 596 | #endif 597 | 598 | template ::value)> 600 | format_to_n_result format_to_n(OutputIt out, size_t n, 601 | const S& format_str, Args&&... args) { 602 | auto it = format_to(detail::truncating_iterator(out, n), format_str, 603 | std::forward(args)...); 604 | return {it.base(), it.count()}; 605 | } 606 | 607 | template ::value)> 609 | size_t formatted_size(const S& format_str, const Args&... args) { 610 | return format_to(detail::counting_iterator(), format_str, args...).count(); 611 | } 612 | 613 | template ::value)> 615 | void print(std::FILE* f, const S& format_str, const Args&... args) { 616 | memory_buffer buffer; 617 | format_to(std::back_inserter(buffer), format_str, args...); 618 | detail::print(f, {buffer.data(), buffer.size()}); 619 | } 620 | 621 | template ::value)> 623 | void print(const S& format_str, const Args&... args) { 624 | print(stdout, format_str, args...); 625 | } 626 | 627 | #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 628 | inline namespace literals { 629 | template 630 | constexpr detail::udl_compiled_string< 631 | remove_cvref_t, 632 | sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> 633 | operator""_cf() { 634 | return {}; 635 | } 636 | } // namespace literals 637 | #endif 638 | 639 | FMT_MODULE_EXPORT_END 640 | FMT_END_NAMESPACE 641 | 642 | #endif // FMT_COMPILE_H_ 643 | --------------------------------------------------------------------------------