From 0824ca7ded73cc959702c4107f264edf8190bf04 Mon Sep 17 00:00:00 2001 From: Pete Gadomski Date: Thu, 12 Jan 2017 13:35:48 -0700 Subject: [PATCH] Several intertwined changes to our API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were enough wires to rip up that I'm mashing all of these changes into one big changeset for the v0.5 update. The big changes include: - Remove the `Runner`, move all the common iterative logic to a templated `Transform` virtual base class, from which `Rigid` and friends derive. - Remove vendored upstream dependencies (other than gtest). Managing vendored dependencies was a big pain. Instead, I'm trying out "components", e.g. the jsoncpp functionality is included as a component that requires an external jsoncpp to be present. We'll see how that goes. - Remove all logging, for now. I might add it back (via the component mechanism mentioned above) but I'm not sure. While it's useful for keeping tabs, it mucks up the business part of the code a lot. I have a vauge idea about adding callbacks into the main cpd loop and using those for logging, but nothing's there yet. - Refactored tests and fixtures, to hopefully make it easier to modify/etc in the future. This rewrite mostly happened becuase I couldn't bring myself to rip apart the v0.4 test suite without just redoing it all. Note that a lot of the business code in this rewrite was *not* touched — this is more of an interface change than anything that affects the actual math. Closes #95, `Runner`s don't exist anymore. Fixes #90. --- .appveyor.yml | 2 +- .travis.yml | 1 + CMakeLists.txt | 102 +- README.md | 46 +- cmake/cpd-config.cmake.in | 13 +- cmake/cpd_test.cmake | 9 + components/jsoncpp/CMakeLists.txt | 21 + .../jsoncpp/include/cpd/jsoncpp.hpp | 29 +- components/jsoncpp/src/jsoncpp.cpp | 67 + components/jsoncpp/tests/CMakeLists.txt | 3 + .../jsoncpp/tests/jsoncpp.cpp | 13 +- docs/Doxyfile.in | 2 +- examples/CMakeLists.txt | 49 +- examples/artificial-displacement.cpp | 103 - examples/rigid.cpp | 33 +- src/affinity.hpp => examples/version.cpp | 13 +- include/cpd/affine.hpp | 61 +- include/cpd/comparer/base.hpp | 48 - include/cpd/comparer/direct.hpp | 35 - include/cpd/gauss_transform.hpp | 62 + .../fgt.hpp => gauss_transform_fgt.hpp} | 51 +- include/cpd/matrix.hpp | 7 +- include/cpd/nonrigid.hpp | 74 +- .../cpd/{normalize.hpp => normalization.hpp} | 20 +- include/cpd/probabilities.hpp | 42 - include/cpd/rigid.hpp | 90 +- include/cpd/runner.hpp | 212 - include/cpd/transform.hpp | 208 + include/cpd/utils.hpp | 12 +- include/cpd/vendor/spdlog/async_logger.h | 77 - include/cpd/vendor/spdlog/common.h | 143 - .../vendor/spdlog/details/async_log_helper.h | 390 -- .../vendor/spdlog/details/async_logger_impl.h | 89 - .../cpd/vendor/spdlog/details/file_helper.h | 118 - include/cpd/vendor/spdlog/details/log_msg.h | 46 - .../cpd/vendor/spdlog/details/logger_impl.h | 293 -- .../vendor/spdlog/details/mpmc_bounded_q.h | 172 - .../cpd/vendor/spdlog/details/null_mutex.h | 45 - include/cpd/vendor/spdlog/details/os.h | 360 -- .../spdlog/details/pattern_formatter_impl.h | 636 --- include/cpd/vendor/spdlog/details/registry.h | 185 - .../cpd/vendor/spdlog/details/spdlog_impl.h | 182 - .../cpd/vendor/spdlog/fmt/bundled/format.cc | 557 --- .../cpd/vendor/spdlog/fmt/bundled/format.h | 4455 ----------------- .../cpd/vendor/spdlog/fmt/bundled/ostream.cc | 36 - .../cpd/vendor/spdlog/fmt/bundled/ostream.h | 118 - .../cpd/vendor/spdlog/fmt/bundled/printf.h | 642 --- include/cpd/vendor/spdlog/fmt/fmt.h | 28 - include/cpd/vendor/spdlog/fmt/ostr.h | 17 - include/cpd/vendor/spdlog/formatter.h | 45 - include/cpd/vendor/spdlog/logger.h | 94 - .../cpd/vendor/spdlog/sinks/android_sink.h | 75 - .../cpd/vendor/spdlog/sinks/ansicolor_sink.h | 116 - include/cpd/vendor/spdlog/sinks/base_sink.h | 45 - include/cpd/vendor/spdlog/sinks/dist_sink.h | 71 - include/cpd/vendor/spdlog/sinks/file_sinks.h | 237 - include/cpd/vendor/spdlog/sinks/msvc_sink.h | 50 - include/cpd/vendor/spdlog/sinks/null_sink.h | 34 - .../cpd/vendor/spdlog/sinks/ostream_sink.h | 47 - include/cpd/vendor/spdlog/sinks/sink.h | 50 - .../cpd/vendor/spdlog/sinks/stdout_sinks.h | 75 - include/cpd/vendor/spdlog/sinks/syslog_sink.h | 81 - include/cpd/vendor/spdlog/spdlog.h | 170 - include/cpd/vendor/spdlog/tweakme.h | 103 - scripts/travis-install-cmake.sh | 2 +- scripts/travis-install-jsoncpp.sh | 18 + scripts/travis-script.sh | 2 + src/affine.cpp | 35 +- src/comparer/base.cpp | 42 - .../direct.cpp => gauss_transform.cpp} | 10 +- .../fgt.cpp => gauss_transform_fgt.cpp} | 51 +- .../gauss_transform_make_default.cpp | 13 +- src/nonrigid.cpp | 39 +- src/{affinity.cpp => normalization.cpp} | 26 +- src/normalize.cpp | 40 - src/rigid.cpp | 45 +- src/transform.cpp | 26 + src/utils.cpp | 17 +- test/CMakeLists.txt | 39 - test/comparer/direct.cpp | 54 - test/comparer/fgt.cpp | 42 - test/data/README.md | 6 - test/rigid.cpp | 152 - test/support.hpp.in | 48 - test/utils.cpp | 50 - tests/CMakeLists.txt | 15 + {test => tests}/affine.cpp | 28 +- {test/data => tests/fixtures}/face.csv | 0 .../base.cpp => tests/fixtures/face.hpp | 23 +- .../fixtures/face_distorted.csv | 0 {test/data => tests/fixtures}/fish.csv | 0 tests/fixtures/fish.hpp | 33 + .../fixtures/fish_correspondence.csv | 0 .../fixtures/fish_distorted.csv | 0 {test/data => tests/fixtures}/helheim.csv | 0 tests/fixtures/helheim.hpp | 31 + tests/gauss_transform.cpp | 69 + .../gauss_transform_fgt.cpp | 29 +- {test => tests}/nonrigid.cpp | 16 +- test/normalize.cpp => tests/normalization.cpp | 18 +- tests/rigid.cpp | 48 + tests/support.hpp.in | 29 + test/affinity.cpp => tests/utils.cpp | 21 +- {test => tests}/version.cpp | 0 vendor/docopt.cpp-af03fa0/.gitignore | 32 - vendor/docopt.cpp-af03fa0/.travis.yml | 76 - vendor/docopt.cpp-af03fa0/CMakeLists.txt | 151 - vendor/docopt.cpp-af03fa0/LICENSE-Boost-1.0 | 23 - vendor/docopt.cpp-af03fa0/LICENSE-MIT | 23 - vendor/docopt.cpp-af03fa0/README.rst | 444 -- vendor/docopt.cpp-af03fa0/docopt-config.cmake | 1 - vendor/docopt.cpp-af03fa0/docopt.cpp | 685 --- vendor/docopt.cpp-af03fa0/docopt.h | 94 - vendor/docopt.cpp-af03fa0/docopt_private.h | 674 --- vendor/docopt.cpp-af03fa0/docopt_util.h | 120 - vendor/docopt.cpp-af03fa0/docopt_value.h | 340 -- .../examples/naval_fate.cpp | 36 - vendor/docopt.cpp-af03fa0/main.cpp | 16 - vendor/docopt.cpp-af03fa0/run_testcase.cpp | 40 - vendor/docopt.cpp-af03fa0/run_tests.py | 72 - vendor/docopt.cpp-af03fa0/testcases.docopt | 957 ---- 121 files changed, 1110 insertions(+), 15101 deletions(-) create mode 100644 cmake/cpd_test.cmake create mode 100644 components/jsoncpp/CMakeLists.txt rename include/cpd/exceptions.hpp => components/jsoncpp/include/cpd/jsoncpp.hpp (64%) create mode 100644 components/jsoncpp/src/jsoncpp.cpp create mode 100644 components/jsoncpp/tests/CMakeLists.txt rename test/runner.cpp => components/jsoncpp/tests/jsoncpp.cpp (76%) delete mode 100644 examples/artificial-displacement.cpp rename src/affinity.hpp => examples/version.cpp (78%) delete mode 100644 include/cpd/comparer/base.hpp delete mode 100644 include/cpd/comparer/direct.hpp create mode 100644 include/cpd/gauss_transform.hpp rename include/cpd/{comparer/fgt.hpp => gauss_transform_fgt.hpp} (58%) rename include/cpd/{normalize.hpp => normalization.hpp} (81%) delete mode 100644 include/cpd/probabilities.hpp delete mode 100644 include/cpd/runner.hpp create mode 100644 include/cpd/transform.hpp delete mode 100644 include/cpd/vendor/spdlog/async_logger.h delete mode 100644 include/cpd/vendor/spdlog/common.h delete mode 100644 include/cpd/vendor/spdlog/details/async_log_helper.h delete mode 100644 include/cpd/vendor/spdlog/details/async_logger_impl.h delete mode 100644 include/cpd/vendor/spdlog/details/file_helper.h delete mode 100644 include/cpd/vendor/spdlog/details/log_msg.h delete mode 100644 include/cpd/vendor/spdlog/details/logger_impl.h delete mode 100644 include/cpd/vendor/spdlog/details/mpmc_bounded_q.h delete mode 100644 include/cpd/vendor/spdlog/details/null_mutex.h delete mode 100644 include/cpd/vendor/spdlog/details/os.h delete mode 100644 include/cpd/vendor/spdlog/details/pattern_formatter_impl.h delete mode 100644 include/cpd/vendor/spdlog/details/registry.h delete mode 100644 include/cpd/vendor/spdlog/details/spdlog_impl.h delete mode 100644 include/cpd/vendor/spdlog/fmt/bundled/format.cc delete mode 100644 include/cpd/vendor/spdlog/fmt/bundled/format.h delete mode 100644 include/cpd/vendor/spdlog/fmt/bundled/ostream.cc delete mode 100644 include/cpd/vendor/spdlog/fmt/bundled/ostream.h delete mode 100644 include/cpd/vendor/spdlog/fmt/bundled/printf.h delete mode 100644 include/cpd/vendor/spdlog/fmt/fmt.h delete mode 100644 include/cpd/vendor/spdlog/fmt/ostr.h delete mode 100644 include/cpd/vendor/spdlog/formatter.h delete mode 100644 include/cpd/vendor/spdlog/logger.h delete mode 100644 include/cpd/vendor/spdlog/sinks/android_sink.h delete mode 100644 include/cpd/vendor/spdlog/sinks/ansicolor_sink.h delete mode 100644 include/cpd/vendor/spdlog/sinks/base_sink.h delete mode 100644 include/cpd/vendor/spdlog/sinks/dist_sink.h delete mode 100644 include/cpd/vendor/spdlog/sinks/file_sinks.h delete mode 100644 include/cpd/vendor/spdlog/sinks/msvc_sink.h delete mode 100644 include/cpd/vendor/spdlog/sinks/null_sink.h delete mode 100644 include/cpd/vendor/spdlog/sinks/ostream_sink.h delete mode 100644 include/cpd/vendor/spdlog/sinks/sink.h delete mode 100644 include/cpd/vendor/spdlog/sinks/stdout_sinks.h delete mode 100644 include/cpd/vendor/spdlog/sinks/syslog_sink.h delete mode 100644 include/cpd/vendor/spdlog/spdlog.h delete mode 100644 include/cpd/vendor/spdlog/tweakme.h create mode 100755 scripts/travis-install-jsoncpp.sh delete mode 100644 src/comparer/base.cpp rename src/{comparer/direct.cpp => gauss_transform.cpp} (86%) rename src/{comparer/fgt.cpp => gauss_transform_fgt.cpp} (82%) rename include/cpd/logging.hpp => src/gauss_transform_make_default.cpp (76%) rename src/{affinity.cpp => normalization.cpp} (58%) delete mode 100644 src/normalize.cpp create mode 100644 src/transform.cpp delete mode 100644 test/CMakeLists.txt delete mode 100644 test/comparer/direct.cpp delete mode 100644 test/comparer/fgt.cpp delete mode 100644 test/data/README.md delete mode 100644 test/rigid.cpp delete mode 100644 test/support.hpp.in delete mode 100644 test/utils.cpp create mode 100644 tests/CMakeLists.txt rename {test => tests}/affine.cpp (67%) rename {test/data => tests/fixtures}/face.csv (100%) rename test/comparer/base.cpp => tests/fixtures/face.hpp (69%) rename test/data/face-distorted.csv => tests/fixtures/face_distorted.csv (100%) rename {test/data => tests/fixtures}/fish.csv (100%) create mode 100644 tests/fixtures/fish.hpp rename test/data/fish-correspondence.csv => tests/fixtures/fish_correspondence.csv (100%) rename test/data/fish-distorted.csv => tests/fixtures/fish_distorted.csv (100%) rename {test/data => tests/fixtures}/helheim.csv (100%) create mode 100644 tests/fixtures/helheim.hpp create mode 100644 tests/gauss_transform.cpp rename test/comparer/common.hpp => tests/gauss_transform_fgt.cpp (67%) rename {test => tests}/nonrigid.cpp (77%) rename test/normalize.cpp => tests/normalization.cpp (64%) create mode 100644 tests/rigid.cpp create mode 100644 tests/support.hpp.in rename test/affinity.cpp => tests/utils.cpp (65%) rename {test => tests}/version.cpp (100%) delete mode 100644 vendor/docopt.cpp-af03fa0/.gitignore delete mode 100644 vendor/docopt.cpp-af03fa0/.travis.yml delete mode 100644 vendor/docopt.cpp-af03fa0/CMakeLists.txt delete mode 100644 vendor/docopt.cpp-af03fa0/LICENSE-Boost-1.0 delete mode 100644 vendor/docopt.cpp-af03fa0/LICENSE-MIT delete mode 100644 vendor/docopt.cpp-af03fa0/README.rst delete mode 100644 vendor/docopt.cpp-af03fa0/docopt-config.cmake delete mode 100644 vendor/docopt.cpp-af03fa0/docopt.cpp delete mode 100644 vendor/docopt.cpp-af03fa0/docopt.h delete mode 100644 vendor/docopt.cpp-af03fa0/docopt_private.h delete mode 100644 vendor/docopt.cpp-af03fa0/docopt_util.h delete mode 100644 vendor/docopt.cpp-af03fa0/docopt_value.h delete mode 100644 vendor/docopt.cpp-af03fa0/examples/naval_fate.cpp delete mode 100644 vendor/docopt.cpp-af03fa0/main.cpp delete mode 100644 vendor/docopt.cpp-af03fa0/run_testcase.cpp delete mode 100755 vendor/docopt.cpp-af03fa0/run_tests.py delete mode 100644 vendor/docopt.cpp-af03fa0/testcases.docopt diff --git a/.appveyor.yml b/.appveyor.yml index 0131f655..0898a0a4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -27,7 +27,7 @@ install: before_build: # TODO use build matrix to test with and w/o libfgt - - cmd: cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_INSTALL_PREFIX=C:\projects\local -Dgtest_force_shared_crt=ON -DBUILD_SHARED_LIBS=OFF -DWITH_FGT=ON . + - cmd: cmake -G "Visual Studio 14 2015 Win64" -DEIGEN3_INCLUDE_DIR=C:\projects\eigen-eigen-07105f7124f9 -DCMAKE_INSTALL_PREFIX=C:\projects\local -Dgtest_force_shared_crt=ON -DBUILD_SHARED_LIBS=OFF -DWITH_FGT=ON . build: parallel: true diff --git a/.travis.yml b/.travis.yml index c33bc486..64073a05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ before_install: install: - scripts/travis-install-cmake.sh && export PATH=$(pwd)/cmake/bin:$PATH - scripts/travis-install-eigen3.sh + - scripts/travis-install-jsoncpp.sh - scripts/travis-install-doxygen.sh - if [ "$CPD_WITH_FGT" = "ON" ]; then scripts/travis-install-fgt.sh; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 8da2b344..5935b5d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ -cmake_minimum_required(VERSION 2.8.12) -set(CPD_LANGUAGES CXX C) -set(CPD_VERSION 0.4.3) +cmake_minimum_required(VERSION 3.1) +set(CPD_LANGUAGES CXX) +set(CPD_VERSION 0.5.0) set(CPD_SOVERSION 0) -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + # Policies if(POLICY CMP0048) # Project version @@ -20,15 +20,17 @@ if(POLICY CMP0054) # Quoted variables in if statements cmake_policy(SET CMP0054 NEW) endif() -# Depdencies + +# Upstream +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +find_package(Eigen3 REQUIRED) find_package(Fgt QUIET) -option(WITH_FGT "Build with libfgt" ${Fgt_FOUND}) +option(WITH_FGT "Build with fgt" ${Fgt_FOUND}) if(WITH_FGT) find_package(Fgt REQUIRED) -else() - find_package(Eigen3 REQUIRED) endif() + # Configuration if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") option(BUILD_SHARED_LIBS "Build shared libraries" OFF) @@ -42,37 +44,40 @@ configure_file(cmake/cpd-config.cmake.in "${PROJECT_BINARY_DIR}/cmake/cpd-config install(FILES "${PROJECT_BINARY_DIR}/cmake/cpd-config.cmake" "${PROJECT_BINARY_DIR}/cmake/cpd-config-version.cmake" DESTINATION lib/cmake/cpd) configure_file(src/version.cpp.in "${PROJECT_BINARY_DIR}/src/version.cpp") -# Targets -set(library_src + +# C++ library +set(library-src src/affine.cpp - src/affinity.cpp - src/comparer/base.cpp - src/comparer/direct.cpp + src/gauss_transform.cpp src/nonrigid.cpp - src/normalize.cpp + src/normalization.cpp src/rigid.cpp + src/transform.cpp src/utils.cpp "${PROJECT_BINARY_DIR}/src/version.cpp" ) if(WITH_FGT) - list(APPEND library_src src/comparer/fgt.cpp) + list(APPEND library-src src/gauss_transform_fgt.cpp) +else() + list(APPEND library-src src/gauss_transform_make_default.cpp) endif() -add_library(Library-C++ ${library_src}) +add_library(Library-C++ ${library-src}) set_target_properties(Library-C++ PROPERTIES OUTPUT_NAME cpd VERSION ${CPD_VERSION} SOVERSION ${CPD_SOVERSION} ) target_include_directories(Library-C++ - INTERFACE $ $ - PRIVATE include include/cpd/vendor) + PUBLIC + $ + $ + ${EIGEN3_INCLUDE_DIR} + ) +target_compile_options(Library-C++ PUBLIC -std=c++11) if(WITH_FGT) target_link_libraries(Library-C++ PUBLIC Fgt::Library-C++) target_compile_definitions(Library-C++ PUBLIC CPD_WITH_FGT) -else() - target_include_directories(Library-C++ PUBLIC ${EIGEN3_INCLUDE_DIR}) - target_compile_options(Library-C++ PUBLIC -std=c++11) endif() option(WITH_STRICT_WARNINGS "Build with stricter warnings" ON) @@ -82,48 +87,37 @@ if(WITH_STRICT_WARNINGS) endif() endif() -install(TARGETS Library-C++ DESTINATION lib EXPORT cpd-targets) -install(FILES - include/cpd/affine.hpp - include/cpd/exceptions.hpp - include/cpd/logging.hpp - include/cpd/matrix.hpp - include/cpd/nonrigid.hpp - include/cpd/normalize.hpp - include/cpd/probabilities.hpp - include/cpd/rigid.hpp - include/cpd/runner.hpp - include/cpd/utils.hpp - include/cpd/version.hpp - DESTINATION include/cpd - ) -install(FILES - include/cpd/comparer/base.hpp - include/cpd/comparer/direct.hpp - DESTINATION include/cpd/comparer - ) -if(WITH_FGT) - install(FILES - include/cpd/comparer/fgt.hpp - DESTINATION include/cpd/comparer - ) -endif() -install(DIRECTORY include/cpd/vendor DESTINATION include/cpd) -install(EXPORT cpd-targets NAMESPACE Cpd:: DESTINATION lib/cmake/cpd) -# Optional targets +# Tests option(WITH_TESTS "Build test suite" ON) if(WITH_TESTS) enable_testing() - set(GOOGLETEST_DIR "${PROJECT_SOURCE_DIR}/vendor/googletest-release-1.8.0/googletest") - add_subdirectory(${GOOGLETEST_DIR}) - add_subdirectory(test) + add_subdirectory("${PROJECT_SOURCE_DIR}/vendor/googletest-release-1.8.0/googletest") + add_subdirectory(tests) endif() + +# Install +install(TARGETS Library-C++ DESTINATION lib EXPORT cpd-targets) +install(DIRECTORY include/cpd DESTINATION include) +install(EXPORT cpd-targets NAMESPACE Cpd:: DESTINATION lib/cmake/cpd) + + +# Docs find_package(Doxygen QUIET) option(WITH_DOCS "Add documentation target" ${Doxygen_FOUND}) if(WITH_DOCS) find_package(Doxygen REQUIRED) configure_file(docs/Doxyfile.in "${PROJECT_BINARY_DIR}/docs/Doxyfile") - add_custom_target(docs COMMAND ${DOXYGEN_EXECUTABLE} "${PROJECT_BINARY_DIR}/docs/Doxyfile") + add_custom_target(doc COMMAND ${DOXYGEN_EXECUTABLE} "${PROJECT_BINARY_DIR}/docs/Doxyfile") +endif() + + +# Components +find_package(jsoncpp QUIET) +option(WITH_JSONCPP "Build with jsoncpp" ${jsoncpp_FOUND}) +if(WITH_JSONCPP) + find_package(jsoncpp REQUIRED) + set(CMAKE_WITH_JSONCPP ON) + add_subdirectory(components/jsoncpp) endif() diff --git a/README.md b/README.md index fe1d7441..dc31796f 100644 --- a/README.md +++ b/README.md @@ -33,22 +33,39 @@ Basic, default usage can be accomplished via some namespace-level methods: int main(int argc, char** argv) { cpd::Matrix fixed = load_points_from_somewhere(); cpd::Matrix moving = load_points_from_somewhere(); - cpd::Rigid::Result result = cpd::rigid(fixed, moving); + cpd::RigidResult result = cpd::rigid(fixed, moving); + return 0; } ``` -More advanced configuration can be accomplished by using a `Runner`: +Configuration is possible via `Rigid`, `Nonrigid`, and `Affine`: ```cpp #include -#include int main(int argc, char** argv) { cpd::Matrix fixed = load_points_from_somewhere(); cpd::Matrix moving = load_points_from_somewhere(); - cpd::Runner runner; - runner.correspondence(true).outliers(0.2); - cpd::Rigid::Result result = runner.run(fixed, moving); + cpd::Rigid rigid; + rigid.correspondence(true).outliers(0.2); + cpd::RigidResult result = runner.run(fixed, moving); + return 0; +} +``` + +If cpd is built with the `jsoncpp` component (see `examples/` for a demonstration of the CMake configuration), the results of the cpd run can be converted to json: + +```cpp +#include +#include +#include + +int main(int argc, char** argv) { + cpd::Matrix fixed = load_points_from_somewhere(); + cpd::Matrix moving = load_points_from_somewhere(); + cpd::RigidResult result = cpd::rigid(fixed, moving); + std::cout << cpd::to_json(result) << std::endl; + return 0; } ``` @@ -62,6 +79,7 @@ See `examples/` in this code repository for some basic usage examples, including **cpd** depends on and [CMake](https://cmake.org/) and [Eigen](http://eigen.tuxfamily.org/index.php?title=Main_Page) at build time only — no runtime dependencies. For additional speed, it can also built with [fgt](https://github.com/gadomski/fgt). +For json output of results, it can be built with [jsoncpp](https://github.com/open-source-parsers/jsoncpp). ### On OSX @@ -100,6 +118,14 @@ target_link_libraries(my-great-library The `Cpd::Library-C++` target includes all the interface settings you need, so you shouldn't need any other calls to get set up. +If you'd like to enable json support, use the jsoncpp component: + +```cmake +find_package(Cpd COMPONENTS jsoncpp REQUIRED) +add_library(my-great-library the_code.cpp) +target_link_libraries(my-great-library PUBLIC Cpd::Library-C++ Cpd::Jsoncpp) +``` + ## OpenMP Both fgt and Eigen support OpenMP for some operations. @@ -124,9 +150,15 @@ If you require some of that old functionality, use the [v0.2](https://github.com If you need armadillo-5.x, which is required for the old **cpd** but is no longer available from the armadillo website, you can use [my mirror](https://github.com/gadomski/armadillo). Thanks for your understanding during this switch. +## Publications + +This library has been used in the following publications: + +- Gadomski, P.J. (December 2016). *Measuring Glacier Surface Velocities With LiDAR: A Comparison of Three-Dimensional Change Detection Methods*. Master's thesis, University of Houston, Geosensing Systems Engineering and Sciences. + ## License -This library is GPL2, copyright 2016 Peter J. Gadomski. +This library is GPL2, copyright 2017 Peter J. Gadomski. See LICENSE.txt for the full license text. This work is directly inspired by Andriy Myronenko's reference implementation, and we owe him many thanks. diff --git a/cmake/cpd-config.cmake.in b/cmake/cpd-config.cmake.in index a07b3584..71189071 100644 --- a/cmake/cpd-config.cmake.in +++ b/cmake/cpd-config.cmake.in @@ -56,8 +56,17 @@ macro(find_dependency dep) endif() endmacro() -set(CPD_WITH_FGT @WITH_FGT@) -if(${CPD_WITH_FGT}) +if(@WITH_FGT@) find_dependency(Fgt @Fgt_VERSION@ EXACT) endif() include("${CMAKE_CURRENT_LIST_DIR}/cpd-targets.cmake") + +foreach(component ${Cpd_FIND_COMPONENTS}) + if("jsoncpp" STREQUAL ${component}) + include("${CMAKE_CURRENT_LIST_DIR}/cpd-${component}-targets.cmake") + find_dependency(jsoncpp) + else() + set(Cpd_FOUND FALSE) + message(FATAL_ERROR "Unsupported component: ${component}") + endif() +endforeach() diff --git a/cmake/cpd_test.cmake b/cmake/cpd_test.cmake new file mode 100644 index 00000000..52963336 --- /dev/null +++ b/cmake/cpd_test.cmake @@ -0,0 +1,9 @@ +function(cpd_test name) + set(src ${name}.cpp) + set(target ${name}-test) + add_executable(${target} ${src}) + set_target_properties(${target} PROPERTIES OUTPUT_NAME ${name}) + add_test(NAME ${name} COMMAND ${target}) + target_link_libraries(${target} PRIVATE Library-C++ ${ARGN} gtest_main) + target_include_directories(${target} PRIVATE "${PROJECT_BINARY_DIR}") +endfunction() diff --git a/components/jsoncpp/CMakeLists.txt b/components/jsoncpp/CMakeLists.txt new file mode 100644 index 00000000..784adcba --- /dev/null +++ b/components/jsoncpp/CMakeLists.txt @@ -0,0 +1,21 @@ +add_library(Jsoncpp src/jsoncpp.cpp) +target_link_libraries(Jsoncpp PUBLIC Library-C++ jsoncpp_lib) +set_target_properties(Jsoncpp PROPERTIES + OUTPUT_NAME cpd-jsoncpp + VERSION ${CPD_VERSION} + SOVERSION ${CPD_SOVERSION} + ) +target_include_directories(Jsoncpp + PUBLIC + $ + $ + ) +target_compile_definitions(Jsoncpp PUBLIC CPD_WITH_JSONCPP) + +install(TARGETS Jsoncpp DESTINATION lib EXPORT cpd-jsoncpp-targets) +install(DIRECTORY include/cpd DESTINATION include) +install(EXPORT cpd-jsoncpp-targets NAMESPACE Cpd:: DESTINATION lib/cmake/cpd) + +if(WITH_TESTS) + add_subdirectory(tests) +endif() diff --git a/include/cpd/exceptions.hpp b/components/jsoncpp/include/cpd/jsoncpp.hpp similarity index 64% rename from include/cpd/exceptions.hpp rename to components/jsoncpp/include/cpd/jsoncpp.hpp index f9be743b..b173a28a 100644 --- a/include/cpd/exceptions.hpp +++ b/components/jsoncpp/include/cpd/jsoncpp.hpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,24 +15,19 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -/// \file -/// Exceptions. - #pragma once -namespace cpd { +#include +#include +#include +#include +#include -/// Base class for all cpd errors. -class cpd_error : public std::runtime_error { -public: - cpd_error(const std::string& what) - : std::runtime_error(what) {} -}; +namespace cpd { -/// The requested comparer is unknown. -class unknown_comparer : public cpd_error { -public: - unknown_comparer(const std::string& name) - : cpd_error(name) {} -}; +Json::Value to_json(const Result& result); +Json::Value to_json(const RigidResult& result); +Json::Value to_json(const AffineResult& result); +Json::Value to_json(const NonrigidResult& result); +Json::Value to_json(const Matrix& matrix); } diff --git a/components/jsoncpp/src/jsoncpp.cpp b/components/jsoncpp/src/jsoncpp.cpp new file mode 100644 index 00000000..c1638410 --- /dev/null +++ b/components/jsoncpp/src/jsoncpp.cpp @@ -0,0 +1,67 @@ +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include + +namespace cpd { + +Json::Value to_json(const Result& result) { + Json::Value root; + root["sigma2"] = result.sigma2; + root["runtime"] = double(result.runtime.count()) / 1e6; + root["iterations"] = Json::UInt64(result.iterations); + return root; +} + +Json::Value to_json(const RigidResult& result) { + const Result* base = &result; + Json::Value root = to_json(*base); + root["rotation"] = cpd::to_json(result.rotation); + root["translation"] = cpd::to_json(result.translation); + root["scale"] = result.scale; + return root; +} + +Json::Value to_json(const AffineResult& result) { + const Result* base = &result; + Json::Value root = to_json(*base); + root["transform"] = cpd::to_json(result.transform); + root["translation"] = cpd::to_json(result.translation); + return root; +} + +Json::Value to_json(const NonrigidResult& result) { + const Result* base = &result; + return to_json(*base); +} + +Json::Value to_json(const Matrix& matrix) { + Json::Value root; + for (Matrix::Index row = 0; row < matrix.rows(); ++row) { + root[Json::ArrayIndex(row)] = Json::Value(Json::ValueType::arrayValue); + for (Matrix::Index col = 0; col < matrix.cols(); ++col) { + root[Json::ArrayIndex(row)][Json::ArrayIndex(col)] = + matrix(row, col); + } + } + return root; +} + +std::ostream& operator<<(std::ostream& ostream, const Result& result) { + return ostream; +} +} diff --git a/components/jsoncpp/tests/CMakeLists.txt b/components/jsoncpp/tests/CMakeLists.txt new file mode 100644 index 00000000..68009b79 --- /dev/null +++ b/components/jsoncpp/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +include(cpd_test) + +cpd_test(jsoncpp Jsoncpp) diff --git a/test/runner.cpp b/components/jsoncpp/tests/jsoncpp.cpp similarity index 76% rename from test/runner.cpp rename to components/jsoncpp/tests/jsoncpp.cpp index a5e76e34..02d80898 100644 --- a/test/runner.cpp +++ b/components/jsoncpp/tests/jsoncpp.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,15 +15,14 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/runner.hpp" -#include "cpd/rigid.hpp" #include "gtest/gtest.h" +#include +#include namespace cpd { -TEST(Runner, SetComparer) { - auto comparer = Comparer::create(); - Runner runner; - runner.comparer(std::move(comparer)); +TEST(RigidResult, ConvertsToJson) { + RigidResult result; + Json::Value json = cpd::to_json(result); } } diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 5fe997e0..76088cd5 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -771,7 +771,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = @PROJECT_SOURCE_DIR@/README.md @PROJECT_SOURCE_DIR@/include/cpd/ @PROJECT_SOURCE_DIR@/include/cpd/comparer @PROJECT_SOURCE_DIR@/docs/ @PROJECT_SOURCE_DIR@/LICENSE.txt +INPUT = @PROJECT_SOURCE_DIR@/README.md @PROJECT_SOURCE_DIR@/include/cpd/ @PROJECT_SOURCE_DIR@/docs/ @PROJECT_SOURCE_DIR@/LICENSE.txt # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b765acd9..bd7b4cd0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,47 +1,10 @@ project(cpd-examples CXX) cmake_minimum_required(VERSION 2.8.12) -include(CMakeParseArguments) -set(DOCOPT_DIR "../vendor/docopt.cpp-af03fa0") -find_package(Cpd REQUIRED) -find_package(PDAL QUIET) - -function(cpd_example name) - set(options WITH_DOCOPT) - set(multi_value_args LINK_LIBRARIES INCLUDE_DIRECTORIES) - cmake_parse_arguments(cpd_example "${options}" "" "${multi_value_args}" ${ARGN}) - - set(target ${name}-example) - set(src ${name}.cpp) - if(cpd_example_WITH_DOCOPT) - list(APPEND src "${DOCOPT_DIR}/docopt.cpp") - list(APPEND cpd_example_INCLUDE_DIRECTORIES "${DOCOPT_DIR}") - if(CMAKE_COMPILER_IS_GNUCXX) - execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) - # Docopt.cpp requires gcc 4.9 or boost's regex - if(GCC_VERSION VERSION_LESS 4.9) - add_definitions("-DDOCTOPT_USE_BOOST_REGEX") - set(Boost_USE_STATIC_LIBS OFF) - find_package(Boost 1.53 REQUIRED COMPONENTS regex) - list(APPEND cpd_example_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}") - list(APPEND cpd_example_LINK_LIBRARIES "${Boost_LIBRARIES}") - endif() - endif() - endif() +find_package(Cpd COMPONENTS jsoncpp REQUIRED) +add_executable(cpd-rigid rigid.cpp) +target_link_libraries(cpd-rigid PRIVATE Cpd::Library-C++ Cpd::Jsoncpp) - add_executable(${target} ${src}) - set_target_properties(${target} PROPERTIES OUTPUT_NAME ${name}) - target_link_libraries(${target} PRIVATE Cpd::Library-C++ ${cpd_example_LINK_LIBRARIES}) - target_include_directories(${target} PRIVATE "${cpd_example_INCLUDE_DIRECTORIES}") -endfunction() - -option(WITH_RIGID "Build the rigid registration example" ON) -if(${WITH_RIGID}) - cpd_example(rigid) -endif() - -option(WITH_ARTIFICIAL_DISPLACEMENT "Build the artificial displacement example (requires PDAL)" ${PDAL_FOUND}) -if(${WITH_ARTIFICIAL_DISPLACEMENT}) - find_package(PDAL REQUIRED) - cpd_example(artificial-displacement WITH_DOCOPT LINK_LIBRARIES ${PDAL_LIBRARIES}) -endif() +find_package(Cpd REQUIRED) +add_executable(cpd-version version.cpp) +target_link_libraries(cpd-version PRIVATE Cpd::Library-C++) diff --git a/examples/artificial-displacement.cpp b/examples/artificial-displacement.cpp deleted file mode 100644 index 5520fd3d..00000000 --- a/examples/artificial-displacement.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/// Reads a point cloud, applies an artificial translation (with some noise), -/// then recovers that translation. -/// -/// This example uses [PDAL](http://www.pdal.io/) to read a point cloud file. -/// The points are then translated, and the translation is recovered with rigid -/// CPD. This examples demonstrates configuring the runner, comparer, and -/// transformation. - -#include "docopt.h" -#include -#include -#include -#include -#include -#include -#include -#include - -static const char USAGE[] = - R"(Artificial displacement and noise testing of a rigid transformation. - -Usage: - artificial-displacement [--bounds=] [--comparer=] [--sigma2=] - -Options: - -h --help Show this screen. - --bounds= Crop the data to these bounds. - --comparer= Comparer to use (direct or fgt) [default: fgt]. - --sigma2= Initial sigma2 [default: 0.0]. -)"; - -cpd::Matrix read_matrix(const std::string& infile, const std::string& bounds); -cpd::Matrix create_noise_matrix(cpd::Matrix::Index rows, - cpd::Matrix::Index cols, double stddev); - -int main(int argc, char** argv) { - auto log = spdlog::stderr_logger_mt(cpd::LOGGER_NAME); - std::map args = - docopt::docopt(USAGE, { argv + 1, argv + argc }, true, - "CPD artificial displacement", true); - cpd::Matrix original = read_matrix(args.at("").asString(), - args.at("--bounds").asString()); - Eigen::Vector3d translation(std::stod(args.at("").asString()), - std::stod(args.at("").asString()), - std::stod(args.at("").asString())); - cpd::Matrix noise = - create_noise_matrix(original.rows(), original.cols(), - std::stod(args.at("").asString())); - cpd::Matrix modified = - original + translation.transpose().replicate(original.rows(), 1) + - noise; - cpd::Rigid rigid; - rigid.allow_reflections(false).scale(false); - cpd::Runner runner(rigid); - std::unique_ptr comparer = - cpd::Comparer::from_name(args.at("--comparer").asString()); - runner.comparer(std::move(comparer)) - .normalize(true) - .sigma2(std::stod(args.at("--sigma2").asString())); - cpd::Rigid::Result result = runner.run(modified, original); - cpd::Vector calculated_translation = - (result.points - original).colwise().mean(); - Eigen::IOFormat format(3, 0, ", ", ", ", "", "", "", ""); - std::cout << std::fixed << calculated_translation.format(format) << ", " - << (translation - calculated_translation).format(format) - << std::endl; - return 0; -} - -cpd::Matrix read_matrix(const std::string& infile, const std::string& bounds) { - pdal::StageFactory factory(false); - std::string driver = factory.inferReaderDriver(infile); - pdal::Stage* reader = factory.createStage(driver); - pdal::Options reader_options; - reader_options.add("filename", infile); - reader->setOptions(reader_options); - pdal::Stage* stage = reader; - if (!bounds.empty()) { - stage = factory.createStage("filters.crop"); - stage->setInput(*reader); - pdal::Options crop_options; - crop_options.add("bounds", bounds); - stage->setOptions(crop_options); - } - pdal::PointTable table; - stage->prepare(table); - pdal::PointViewSet pointViewSet = stage->execute(table); - return pdal::eigen::pointViewToEigen(**pointViewSet.begin()); -} - -cpd::Matrix create_noise_matrix(cpd::Matrix::Index rows, - cpd::Matrix::Index cols, double stddev) { - std::random_device device; - std::default_random_engine engine(device()); - std::normal_distribution<> distribution(0.0, stddev); - cpd::Matrix noise(rows, cols); - for (cpd::Matrix::Index i = 0; i < noise.rows(); ++i) { - for (cpd::Matrix::Index j = 0; j < noise.cols(); ++j) { - noise(i, j) = distribution(engine); - } - } - return noise; -} diff --git a/examples/rigid.cpp b/examples/rigid.cpp index fbd2cddf..1ffee34d 100644 --- a/examples/rigid.cpp +++ b/examples/rigid.cpp @@ -1,22 +1,33 @@ -/// Computes the rigid transformation between two text files. +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include - -#include +#include #include -#include -#include +#include int main(int argc, char** argv) { - auto logger = spdlog::stdout_logger_mt(cpd::LOGGER_NAME); if (argc != 3) { - logger->critical("Invalid number of arguments"); - logger->info("USAGE: rigid "); + std::cout << "ERROR: invalid usage" << std::endl; + std::cout << "USAGE: cpd-rigid " << std::endl; return 1; } cpd::Matrix fixed = cpd::matrix_from_path(argv[1]); cpd::Matrix moving = cpd::matrix_from_path(argv[2]); - cpd::Rigid::Result result = cpd::rigid(fixed, moving); - std::cout << result << std::endl; + cpd::RigidResult result = cpd::rigid(fixed, moving); + std::cout << cpd::to_json(result) << std::endl; return 0; } diff --git a/src/affinity.hpp b/examples/version.cpp similarity index 78% rename from src/affinity.hpp rename to examples/version.cpp index 351e8e17..f10ceffe 100644 --- a/src/affinity.hpp +++ b/examples/version.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,11 +15,10 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#pragma once +#include +#include -#include "cpd/matrix.hpp" - -namespace cpd { - -Matrix affinity(const Matrix& x, const Matrix& y, double beta); +int main(int argc, char** argv) { + std::cout << cpd::version() << std::endl; + return 0; } diff --git a/include/cpd/affine.hpp b/include/cpd/affine.hpp index 238bea3c..2b302608 100644 --- a/include/cpd/affine.hpp +++ b/include/cpd/affine.hpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,53 +16,36 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. /// \file -/// Affine cpd registration. +/// +/// Affine (translation, rotation, skew, scaling) transformation. #pragma once -#include - -#include -#include -#include +#include namespace cpd { -/// An affine transformation. -class Affine { -public: - /// The result of an affine CPD run. - struct Result { - /// The affine transformation. - Matrix transform; - /// The ranslation vector. - Vector translation; - /// The transformed points. - Matrix points; - /// The final sigma2. - double sigma2; - /// The correspondence vector (optional). - IndexVector correspondence; - /// The runtime. - std::chrono::microseconds runtime; - /// The number of iterations until convergence. - size_t iterations; - }; - - /// Initialize an affine transformation. - void init(const Matrix&, const Matrix&) {} +/// The result of a affine coherent point drift run. +struct AffineResult : public Result { + /// The affine transformation. + Matrix transform; - /// No modification of probabilities necessary. - void modify_probabilities(Probabilities&) const {} + /// The translation vector. + Vector translation; - /// Computes one iteration of the affine transform. - Result compute(const Matrix& fixed, const Matrix& moving, - const Probabilities& probabilities, double sigma2) const; + /// Denormalize this result. + void denormalize(const Normalization& normalization); +}; - /// Denormalizes a result. - void denormalize(const Normalization& normalization, Result& result) const; +/// Affine coherent point drift. +class Affine : public Transform { +public: + /// Computes one iteration of the affine transformation. + AffineResult compute_one(const Matrix& fixed, const Matrix& moving, + const Probabilities& probabilities, + double sigma2) const; }; -/// Computes an affine transformation from the target onto the source. -Affine::Result affine(const Matrix& fixed, const Matrix& moving); +/// Runs a affine registration on two matrices. +AffineResult affine(const Matrix& fixed, const Matrix& moving); } diff --git a/include/cpd/comparer/base.hpp b/include/cpd/comparer/base.hpp deleted file mode 100644 index 1891704b..00000000 --- a/include/cpd/comparer/base.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -/// \file -/// Base class for comparers. - -#pragma once - -#include -#include -#include - -namespace cpd { - -/// An abstract base class. -/// -/// Comparers compare two data sets and produce several probability matrices -/// that describe the alignment between those two data sets. -class Comparer { -public: - /// Creates the default comparer. - static std::unique_ptr create(); - - /// Creates a default comparer from the provided name. - /// - /// Acceptable names are "direct" and (if cpd is compiled with fgt support) - /// "fgt"). - static std::unique_ptr from_name(const std::string& name); - - /// Computes the probabilities of alignment between fixed and moving. - virtual Probabilities compute(const Matrix& fixed, const Matrix& moving, - double sigma2, double outliers) const = 0; -}; -} diff --git a/include/cpd/comparer/direct.hpp b/include/cpd/comparer/direct.hpp deleted file mode 100644 index 0d830308..00000000 --- a/include/cpd/comparer/direct.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -/// \file -/// Compute correpondence proabilities using the direct method. - -#pragma once - -#include -#include -#include - -namespace cpd { - -/// Use Myronenko's slow direct method to calculate probabilities. -class DirectComparer : public Comparer { -public: - Probabilities compute(const Matrix& fixed, const Matrix& moving, - double sigma2, double outliers) const; -}; -} diff --git a/include/cpd/gauss_transform.hpp b/include/cpd/gauss_transform.hpp new file mode 100644 index 00000000..38be6901 --- /dev/null +++ b/include/cpd/gauss_transform.hpp @@ -0,0 +1,62 @@ +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +/// \file +/// +/// Basic correspondence/error calculation between two datasets, using the +/// direct method of the Gauss transform. + +#pragma once + +#include +#include + +namespace cpd { + +/// Probability matrices produced by comparing two data sets with a +/// `GaussTransform`. +struct Probabilities { + /// The probability matrix, multiplied by the identity matrix. + Vector p1; + /// The probability matrix, transposes, multiplied by the identity matrix. + Vector pt1; + /// The probability matrix multiplied by the fixed points. + Matrix px; + /// The total error. + double l; + /// The correspondence vector between the two datasets. + IndexVector correspondence; +}; + +/// Abstract base class for Gauss transforms. +class GaussTransform { +public: + /// Returns the default Gauss transform as a unique ptr. + static std::unique_ptr make_default(); + + /// Computes the Gauss transform. + virtual Probabilities compute(const Matrix& fixed, const Matrix& moving, + double sigma2, double outliers) const = 0; +}; + +/// The direct Gauss transform. +class GaussTransformDirect : public GaussTransform { +public: + Probabilities compute(const Matrix& fixed, const Matrix& moving, + double sigma2, double outliers) const; +}; +} diff --git a/include/cpd/comparer/fgt.hpp b/include/cpd/gauss_transform_fgt.hpp similarity index 58% rename from include/cpd/comparer/fgt.hpp rename to include/cpd/gauss_transform_fgt.hpp index 28eebd53..2eb9b7ac 100644 --- a/include/cpd/comparer/fgt.hpp +++ b/include/cpd/gauss_transform_fgt.hpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,67 +16,60 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. /// \file -/// Compute correpondence proabilities using libfgt. +/// +/// Gauss transforms using the [fgt](https://github.com/gadomski/fgt) library. #pragma once -#include -#include +#include #include namespace cpd { -/// Choosing an fgt method. -enum class FgtMethod { - /// Use the direct tree method - /// - /// This is the standard transform but with simplification via clustering. +/// The method(s) by which the fgt Gauss transform will compute the differences. +enum FgtMethod { + /// Use only the direct-tree fgt method. DirectTree, - /// Use the improved fast gauss transform. - /// - /// This method does clustering simplifications and a series expansion to - /// reduce the computational complexity, but works poorly with few points or - /// small bandwidths. + /// Use only the improved fast Gauss transform method. Ifgt, - /// Switch between DirectTree and Ifgt at a breakpoint. + /// Switch between direct-tree and ifgt at a certain breakpoint. Switched, }; -/// Default error tolerance for fgt. -const double DEFAULT_EPSILON = 1e-4; -/// Default Fgt method. -const FgtMethod DEFAULT_METHOD = FgtMethod::DirectTree; -/// Default ifgt->direct-tree breakpoint for fgt. +/// The default fgt method +const FgtMethod DEFAULT_FGT_METHOD = FgtMethod::DirectTree; +/// The default switched fgt breakpoint. const double DEFAULT_BREAKPOINT = 0.2; +/// The default fgt epsilon. +const double DEFAULT_EPSILON = 1e-4; -/// Use the fgt library to calculate probabilities. -class FgtComparer : public Comparer { +/// The Gauss transform using the fgt library. +class GaussTransformFgt : public GaussTransform { public: - FgtComparer() - : Comparer() + GaussTransformFgt() + : GaussTransform() , m_breakpoint(DEFAULT_BREAKPOINT) , m_epsilon(DEFAULT_EPSILON) - , m_method(DEFAULT_METHOD) {} + , m_method(DEFAULT_FGT_METHOD) {} /// Sets the ifgt->direct-tree breakpoint. - FgtComparer& breakpoint(double breakpoint) { + GaussTransformFgt& breakpoint(double breakpoint) { m_breakpoint = breakpoint; return *this; } /// Sets the epsilon. - FgtComparer& epsilon(double epsilon) { + GaussTransformFgt& epsilon(double epsilon) { m_epsilon = epsilon; return *this; } /// Sets the method. - FgtComparer& method(FgtMethod method) { + GaussTransformFgt& method(FgtMethod method) { m_method = method; return *this; } - /// Computes the probabilities using the fgt library. Probabilities compute(const Matrix& fixed, const Matrix& moving, double sigma2, double outliers) const; diff --git a/include/cpd/matrix.hpp b/include/cpd/matrix.hpp index 87eb9f59..c6ad8c59 100644 --- a/include/cpd/matrix.hpp +++ b/include/cpd/matrix.hpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,7 +16,8 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. /// \file -/// typedefs for matrices used in cpd. +/// +/// Basic typedefs for our flavors of Eigen matrices. #pragma once @@ -24,7 +25,7 @@ namespace cpd { -/// Typedef for our specific type of matrix. +/// Our base matrix class. typedef Eigen::MatrixXd Matrix; /// Typedef for our specific type of vector. diff --git a/include/cpd/nonrigid.hpp b/include/cpd/nonrigid.hpp index d5dcdd3c..f8890c6b 100644 --- a/include/cpd/nonrigid.hpp +++ b/include/cpd/nonrigid.hpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,15 +16,12 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. /// \file -/// Nonrigid cpd registration. +/// +/// Nonrigid coherent point drift transform. #pragma once -#include - -#include -#include -#include +#include namespace cpd { @@ -33,68 +30,47 @@ const double DEFAULT_BETA = 3.0; /// Default value for lambda. const double DEFAULT_LAMBDA = 3.0; -/// A nonrigid registration. -class Nonrigid { -public: - /// The result of a nonrigid transformation. - struct Result { - /// The resultant points. - Matrix points; - /// The last sigma2. - double sigma2; - /// The correspondence vector (optional). - IndexVector correspondence; - /// The runtime. - std::chrono::microseconds runtime; - /// The number of iterations until convergence. - size_t iterations; - }; +/// The result of a nonrigid coherent point drift run. +struct NonrigidResult : public Result {}; - /// Creates a default nonrigid registration. +/// Nonrigid coherent point drift. +class Nonrigid : public Transform { +public: Nonrigid() - : m_beta(DEFAULT_BETA) + : Transform() , m_lambda(DEFAULT_LAMBDA) - , m_g() - , m_w() {} + , m_beta(DEFAULT_BETA) {} + + /// Initialize this transform for the provided matrices. + void init(const Matrix& fixed, const Matrix& moving); - /// Returns the value for beta. - double beta() const { return m_beta; } + /// Modifies the probabilities with some affinity and weight information. + void modify_probabilities(Probabilities& probabilities) const; - /// Sets the beta value. + /// Sets the beta. Nonrigid& beta(double beta) { m_beta = beta; return *this; } - /// Returns the lambda value. - double lambda() const { return m_lambda; } - - /// Sets the lambda value. + /// Sets the lambda. Nonrigid& lambda(double lambda) { m_lambda = lambda; return *this; } - /// Initializes the nonrigid transformation. - void init(const Matrix& fixed, const Matrix& moving); - - /// Modifies the probabilities after calculation. - void modify_probabilities(Probabilities& probabilities) const; - /// Computes one iteration of the nonrigid transformation. - Result compute(const Matrix& fixed, const Matrix& moving, - const Probabilities& probabilities, double sigma2); - - /// Denormalizes a result. - void denormalize(const Normalization& normalization, Result& result) const; + NonrigidResult compute_one(const Matrix& fixed, const Matrix& moving, + const Probabilities& probabilities, + double sigma2) const; private: - double m_beta; - double m_lambda; Matrix m_g; Matrix m_w; + double m_lambda; + double m_beta; }; -/// Runs nonrigid registration using sensible defaults. -Nonrigid::Result nonrigid(const Matrix& fixed, const Matrix& moving); +/// Runs a nonrigid registration on two matrices. +NonrigidResult nonrigid(const Matrix& fixed, const Matrix& moving); } diff --git a/include/cpd/normalize.hpp b/include/cpd/normalization.hpp similarity index 81% rename from include/cpd/normalize.hpp rename to include/cpd/normalization.hpp index 3c83b99e..f37e9c62 100644 --- a/include/cpd/normalize.hpp +++ b/include/cpd/normalization.hpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,7 +16,9 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. /// \file -/// Normalize points to (roughly) a unit cube (or whatever dimensionality). +/// +/// Utility to scale/offset points to (roughly) conform to unit shape centered +/// at zero. #pragma once @@ -26,18 +28,18 @@ namespace cpd { /// The results of normalizing data to a unit cube (or whatever dimensionality). struct Normalization { - /// The fixed points. - Matrix fixed; - /// The moving points. - Matrix moving; /// The average of the fixed points, that was subtracted from those data. Vector fixed_mean; + /// The fixed points. + Matrix fixed; /// The average of the moving points, that was subtracted from those data. Vector moving_mean; + /// The moving points. + Matrix moving; /// The scaling factor for the points. double scale; -}; -/// Compute normalized points for two matrices. -Normalization normalize(const Matrix& fixed, const Matrix& moving); + /// Creates a new normalization for the provided matrices. + Normalization(const Matrix& fixed, const Matrix& moving); +}; } diff --git a/include/cpd/probabilities.hpp b/include/cpd/probabilities.hpp deleted file mode 100644 index 8da13a67..00000000 --- a/include/cpd/probabilities.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -/// \file -/// Compute correpondence proabilities between two datasets. - -#pragma once - -#include - -namespace cpd { - -/// Probabilities of correspondence between two datasets. -struct Probabilities { - /// The probablity matrix times a column vector of all ones. - Vector p1; - /// The tranpsosed probability matrix times a column vector of all ones. - Vector pt1; - /// The probability matrix times the fixed matrix. - Matrix px; - /// The total accumulated error. - double l; - /// Corrsepondence vector between the two datasets. - /// - /// Only produced by the DirectProbabilityComputer. - IndexVector correspondence; -}; -} diff --git a/include/cpd/rigid.hpp b/include/cpd/rigid.hpp index 41a93909..b9b9bf55 100644 --- a/include/cpd/rigid.hpp +++ b/include/cpd/rigid.hpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,92 +16,64 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. /// \file -/// Rigid cpd registration. +/// +/// Rigid coherent point drift. #pragma once -#include - -#include -#include -#include +#include namespace cpd { /// Should rigid registrations allow reflections by default? -const bool DEFAULT_ALLOW_REFLECTIONS = false; +const bool DEFAULT_REFLECTIONS = false; /// Should rigid registrations scale the data by default? const bool DEFAULT_SCALE = false; +/// The result of a rigid coherent point drift run. +struct RigidResult : public Result { + /// The rotation component of the transformation. + Matrix rotation; + /// The translation component of the transformation. + Vector translation; + /// The scaling component of the transformation. + double scale; + + void denormalize(const Normalization& normalization); +}; + /// Rigid coherent point drift. /// -/// Rotation, translation, and possibly scaling. -class Rigid { +/// Scaling and reflections can be turned on and off. +class Rigid : public Transform { public: - /// The result of a rigid transformation. - struct Result { - /// The rotation from the moving points onto the fixed points. - Matrix rotation; - /// The translation from the moving points onto the fixed points. - Vector translation; - /// The scaling factor. - double scale; - /// The transformed points. - Matrix points; - /// The final sigma2. - double sigma2; - /// The correspondence vector (optional). - IndexVector correspondence; - /// The runtime. - std::chrono::microseconds runtime; - /// The number of iterations until convergence. - size_t iterations; - }; - - /// Creates a default rigid transformation. Rigid() - : m_allow_reflections(DEFAULT_ALLOW_REFLECTIONS) + : Transform() + , m_reflections(DEFAULT_REFLECTIONS) , m_scale(DEFAULT_SCALE) {} - /// No initialization necessary. - void init(const Matrix&, const Matrix&) {} - - /// No modification of probabilities necessary. - void modify_probabilities(Probabilities&) const {} - - /// Returns whether this rigid transformation allows reflections. - bool allow_reflections() const { return m_allow_reflections; } - - /// Sets whether this rigid allows reflections. - Rigid& allow_reflections(bool allow_reflections) { - m_allow_reflections = allow_reflections; + /// Sets whether this rigid transform allows reflections. + Rigid& reflections(bool reflections) { + m_reflections = reflections; return *this; } - /// Returns whether this rigid allows scaling. - bool scale() const { return m_scale; } - - /// Sets whether this rigid allows scaling or not. + /// Sets whether this rigid transform allows scaling. Rigid& scale(bool scale) { m_scale = scale; return *this; } /// Computes one iteration of the rigid transformation. - Result compute(const Matrix& fixed, const Matrix& moving, - const Probabilities& probabilities, double sigma2) const; - - /// Denormalizes a result. - void denormalize(const Normalization& normalization, Result& result) const; + RigidResult compute_one(const Matrix& fixed, const Matrix& moving, + const Probabilities& probabilities, + double sigma2) const; private: - bool m_allow_reflections; + bool m_reflections; bool m_scale; }; -/// Computes a rigid transformation from the target onto the source. -Rigid::Result rigid(const Matrix& fixed, const Matrix& moving); - -/// Prints the rigid result to an ostream. -std::ostream& operator<<(std::ostream& stream, const Rigid::Result& result); +/// Runs a rigid registration on two matrices. +RigidResult rigid(const Matrix& fixed, const Matrix& moving); } diff --git a/include/cpd/runner.hpp b/include/cpd/runner.hpp deleted file mode 100644 index ce816448..00000000 --- a/include/cpd/runner.hpp +++ /dev/null @@ -1,212 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -/// \file -/// Configure cpd registration runs. - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace cpd { - -/// The default number of iterations allowed. -const size_t DEFAULT_MAX_ITERATIONS = 150; -/// Whether points should be normalized by default. -const bool DEFAULT_NORMALIZE = true; -/// The default outlier weight. -const double DEFAULT_OUTLIERS = 0.1; -/// The default tolerance. -const double DEFAULT_TOLERANCE = 1e-5; -/// The default sigma2. -const double DEFAULT_SIGMA2 = 0.0; -/// Whether correspondence vector should be computed by default. -const bool DEFAULT_CORRESPONDENCE = false; - -/// Template class for running cpd registrations. -/// -/// The template argument is the type of transform (e.g. Rigid). -template -class Runner { -public: - /// Creates a default Runner. - Runner() - : Runner(Transform()) {} - - /// Creates a runner with a pre-constructed probability computer and - /// transform. - Runner(Transform transform) - : m_comparer(Comparer::create()) - , m_correspondence(DEFAULT_CORRESPONDENCE) - , m_logger(spdlog::get(LOGGER_NAME)) - , m_max_iterations(DEFAULT_MAX_ITERATIONS) - , m_normalize(DEFAULT_NORMALIZE) - , m_outliers(DEFAULT_OUTLIERS) - , m_sigma2(DEFAULT_SIGMA2) - , m_tolerance(DEFAULT_TOLERANCE) - , m_transform(transform) {} - - /// Sets the comparer. - Runner& comparer(std::unique_ptr comparer) { - m_comparer = std::move(comparer); - return *this; - }; - - /// Returrns whether the correspondence vector should be calculated. - bool correspondence() const { return m_correspondence; } - - /// Sets whether the correspondence vector should be calculated. - Runner& correspondence(bool correspondence) { - m_correspondence = correspondence; - return *this; - } - - /// Returns the maximum number of iterations allowed. - size_t max_iterations() const { return m_max_iterations; } - - /// Sets the maximum number of iterations allowed. - Runner& max_iterations(size_t max_iterations) { - m_max_iterations = max_iterations; - return *this; - } - - /// Returns whether the points should be normalized before running. - bool normalize() const { return m_normalize; } - - /// Sets whether the points should be normalized before running. - Runner& normalize(bool normalize) { - m_normalize = normalize; - return *this; - } - - /// Returns the sigma2 value. - double sigma2() const { return m_sigma2; } - - /// Sets the initial sigma2. - /// - /// If this value is not set, it will be computed to a reasonable default. - Runner& sigma2(double sigma2) { - m_sigma2 = sigma2; - return *this; - } - - /// Returns the tolerance value. - double tolerance() const { return m_tolerance; } - - /// Sets the tolerance. - Runner& tolerance(double tolerance) { - m_tolerance = tolerance; - return *this; - } - - /// Returns the outliers value. - double outliers() const { return m_outliers; } - - /// Sets the outlier weight. - Runner& outliers(double outliers) { - m_outliers = outliers; - return *this; - } - - /// Runs the cpd registration. - typename Transform::Result run(const Matrix& fixed, const Matrix& moving) { - if (m_logger) { - m_logger->info("Number of points in fixed matrix: {}", - fixed.rows()); - m_logger->info("Number of points in moving matrix: {}", - moving.rows()); - } - const Matrix* fixed_ptr(&fixed); - const Matrix* moving_ptr(&moving); - Normalization normalization; - const auto tic = std::chrono::high_resolution_clock::now(); - if (m_normalize) { - normalization = cpd::normalize(fixed, moving); - fixed_ptr = &normalization.fixed; - moving_ptr = &normalization.moving; - } - m_transform.init(*fixed_ptr, *moving_ptr); - size_t iter = 0; - double ntol = m_tolerance + 10.0; - double l = 0.0; - typename Transform::Result result; - result.points = *moving_ptr; - if (m_sigma2 == 0.0) { - result.sigma2 = default_sigma2(*fixed_ptr, *moving_ptr); - if (m_logger) { - m_logger->info("Initializing sigma2 to {}", result.sigma2); - } - } else { - result.sigma2 = m_sigma2; - if (m_logger) { - m_logger->info("sigma2 previously set to {}", result.sigma2); - } - } - while (iter < m_max_iterations && ntol > m_tolerance && - result.sigma2 > 10 * std::numeric_limits::epsilon()) { - auto probabilities = m_comparer->compute(*fixed_ptr, result.points, - result.sigma2, m_outliers); - m_transform.modify_probabilities(probabilities); - ntol = std::abs((probabilities.l - l) / probabilities.l); - if (m_logger) { - m_logger->info("iter={}, dL={:.8f}, sigma2={:.8f}", iter, ntol, - result.sigma2); - } - l = probabilities.l; - result = m_transform.compute(*fixed_ptr, *moving_ptr, probabilities, - result.sigma2); - ++iter; - } - if (m_normalize) { - m_transform.denormalize(normalization, result); - } - if (m_correspondence) { - auto probabilities = DirectComparer().compute(fixed, result.points, - m_sigma2, m_outliers); - result.correspondence = probabilities.correspondence; - } - const auto toc = std::chrono::high_resolution_clock::now(); - result.runtime = - std::chrono::duration_cast(toc - tic); - result.iterations = iter; - return result; - } - -private: - std::unique_ptr m_comparer; - bool m_correspondence; - std::shared_ptr m_logger; - size_t m_max_iterations; - bool m_normalize; - double m_outliers; - double m_sigma2; - double m_tolerance; - Transform m_transform; -}; - -/// Runs a registration with a default comparer. -template -typename Transform::Result run(const Matrix& fixed, const Matrix& source) { - Runner runner; - return runner.run(fixed, source); -} -} diff --git a/include/cpd/transform.hpp b/include/cpd/transform.hpp new file mode 100644 index 00000000..65c9e065 --- /dev/null +++ b/include/cpd/transform.hpp @@ -0,0 +1,208 @@ +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +/// \file +/// +/// Generic coherent point drift transform. +/// +/// Downstreams shouldn't need to include this file directly, use a realization +/// of a transform (e.g. `Rigid`) instead. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace cpd { + +/// The default number of iterations allowed. +const size_t DEFAULT_MAX_ITERATIONS = 150; +/// Whether points should be normalized by default. +const bool DEFAULT_NORMALIZE = true; +/// The default outlier weight. +const double DEFAULT_OUTLIERS = 0.1; +/// The default tolerance. +const double DEFAULT_TOLERANCE = 1e-5; +/// The default sigma2. +const double DEFAULT_SIGMA2 = 0.0; +/// Whether correspondence vector should be computed by default. +const bool DEFAULT_CORRESPONDENCE = false; + +/// The result of a generic transform run. +struct Result { + /// The final moved points. + Matrix points; + /// The final sigma2 value. + double sigma2; + /// The correspondence vector. + IndexVector correspondence; + /// The runtime. + std::chrono::microseconds runtime; + /// The number of iterations. + size_t iterations; + + /// De-normalize this result. + /// + /// Generally, this scales the points back, and sometimes adjust transforms + /// or shifts or the like. + virtual void denormalize(const Normalization& normalization); +}; + +/// Generic coherent point drift transform. +/// +/// An abstract base class for real transforms, e.g. `Rigid` or `Nonrigid`. +template +class Transform { +public: + Transform() + : m_correspondence(DEFAULT_CORRESPONDENCE) + , m_gauss_transform(GaussTransform::make_default()) + , m_max_iterations(DEFAULT_MAX_ITERATIONS) + , m_normalize(DEFAULT_NORMALIZE) + , m_outliers(DEFAULT_OUTLIERS) + , m_sigma2(DEFAULT_SIGMA2) + , m_tolerance(DEFAULT_TOLERANCE) {} + + /// Sets whether the correspondence vector will be computed. + Transform& correspondence(bool correspondence) { + m_correspondence = correspondence; + return *this; + } + + /// Sets the gauss transform. + Transform& gauss_transform( + std::unique_ptr gauss_transform) { + m_gauss_transform = gauss_transform; + return *this; + } + + /// Sets the max iterations for this transform. + Transform& max_iterations(double max_iterations) { + m_max_iterations = max_iterations; + return *this; + } + + /// Sets whether to normalize the points before running cpd. + Transform& normalize(bool normalize) { + m_normalize = normalize; + return *this; + } + + /// Sets the outlier tolerance. + Transform& outliers(double outliers) { + m_outliers = outliers; + return *this; + } + + /// Sets the sigma2 value for this transform. + Transform& sigma2(double sigma2) { + m_sigma2 = sigma2; + return *this; + } + + /// Sets the final tolerance. + Transform& tolerance(double tolerance) { + m_tolerance = tolerance; + return *this; + } + + /// Runs this transform for the provided matrices. + Result run(Matrix fixed, Matrix moving) { + auto tic = std::chrono::high_resolution_clock::now(); + Normalization normalization(fixed, moving); + if (m_normalize) { + fixed = normalization.fixed; + moving = normalization.moving; + } + + this->init(fixed, moving); + + Result result; + result.points = moving; + if (m_sigma2 == 0.0) { + result.sigma2 = cpd::default_sigma2(fixed, moving); + } else if (m_normalize) { + result.sigma2 = m_sigma2 / normalization.scale; + } else { + result.sigma2 = m_sigma2; + } + + size_t iter = 0; + double ntol = m_tolerance + 10.0; + double l = 0.; + while (iter < m_max_iterations && ntol > m_tolerance && + result.sigma2 > 10 * std::numeric_limits::epsilon()) { + Probabilities probabilities = m_gauss_transform->compute( + fixed, result.points, result.sigma2, m_outliers); + this->modify_probabilities(probabilities); + + ntol = std::abs((probabilities.l - l) / probabilities.l); + l = probabilities.l; + + result = + this->compute_one(fixed, moving, probabilities, result.sigma2); + ++iter; + } + + if (m_normalize) { + result.denormalize(normalization); + } + if (m_correspondence) { + Probabilities probabilities = m_gauss_transform->compute( + fixed, result.points, m_sigma2, m_outliers); + result.correspondence = probabilities.correspondence; + assert(result.correspondence.rows() > 0); + } + auto toc = std::chrono::high_resolution_clock::now(); + result.runtime = + std::chrono::duration_cast(toc - tic); + result.iterations = iter; + return result; + } + + /// Initialize this transform for the provided matrices. + /// + /// This is called before beginning each run, but after normalization. In + /// general, transforms shouldn't need to be initialized, but the nonrigid + /// does. + virtual void init(const Matrix& fixed, const Matrix& moving) {} + + /// Modifies `Probabilities` in some way. + /// + /// Some types of transform need to tweak the probabilities before moving on + /// with an interation. The default behavior is to do nothing. + virtual void modify_probabilities(Probabilities& probabilities) const {} + + /// Computes one iteration of the transform. + virtual Result compute_one(const Matrix& fixed, const Matrix& moving, + const Probabilities& probabilities, + double sigma2) const = 0; + +private: + bool m_correspondence; + std::unique_ptr m_gauss_transform; + size_t m_max_iterations; + bool m_normalize; + double m_outliers; + double m_sigma2; + double m_tolerance; +}; +} diff --git a/include/cpd/utils.hpp b/include/cpd/utils.hpp index 47062820..ab69b65d 100644 --- a/include/cpd/utils.hpp +++ b/include/cpd/utils.hpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,9 +16,12 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. /// \file -/// Utility functions. +/// +/// The always-present, always-ambiguous utils file. -#include "cpd/matrix.hpp" +#pragma once + +#include namespace cpd { @@ -27,4 +30,7 @@ Matrix matrix_from_path(const std::string& path); /// Computes the default sigma2 for the given matrices. double default_sigma2(const Matrix& fixed, const Matrix& moving); + +/// Computes the affinity matrix between the two matrices. +Matrix affinity(const Matrix& fixed, const Matrix& moving, double beta); } diff --git a/include/cpd/vendor/spdlog/async_logger.h b/include/cpd/vendor/spdlog/async_logger.h deleted file mode 100644 index 1c42fd9c..00000000 --- a/include/cpd/vendor/spdlog/async_logger.h +++ /dev/null @@ -1,77 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// Very fast asynchronous logger (millions of logs per second on an average desktop) -// Uses pre allocated lockfree queue for maximum throughput even under large number of threads. -// Creates a single back thread to pop messages from the queue and log them. -// -// Upon each log write the logger: -// 1. Checks if its log level is enough to log the message -// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue) -// 3. will throw spdlog_ex upon log exceptions -// Upon destruction, logs all remaining messages in the queue before destructing.. - -#include -#include - -#include -#include -#include -#include - -namespace spdlog -{ - -namespace details -{ -class async_log_helper; -} - -class async_logger :public logger -{ -public: - template - async_logger(const std::string& name, - const It& begin, - const It& end, - size_t queue_size, - const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, - const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), - const std::function& worker_teardown_cb = nullptr); - - async_logger(const std::string& logger_name, - sinks_init_list sinks, - size_t queue_size, - const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, - const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), - const std::function& worker_teardown_cb = nullptr); - - async_logger(const std::string& logger_name, - sink_ptr single_sink, - size_t queue_size, - const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, - const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), - const std::function& worker_teardown_cb = nullptr); - - //Wait for the queue to be empty, and flush synchronously - //Warning: this can potentialy last forever as we wait it to complete - void flush() override; -protected: - void _sink_it(details::log_msg& msg) override; - void _set_formatter(spdlog::formatter_ptr msg_formatter) override; - void _set_pattern(const std::string& pattern) override; - -private: - std::unique_ptr _async_log_helper; -}; -} - - -#include diff --git a/include/cpd/vendor/spdlog/common.h b/include/cpd/vendor/spdlog/common.h deleted file mode 100644 index 490deec7..00000000 --- a/include/cpd/vendor/spdlog/common.h +++ /dev/null @@ -1,143 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) -#include -#include -#endif - -#include - -//visual studio upto 2013 does not support noexcept nor constexpr -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#define SPDLOG_NOEXCEPT throw() -#define SPDLOG_CONSTEXPR -#else -#define SPDLOG_NOEXCEPT noexcept -#define SPDLOG_CONSTEXPR constexpr -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define DEPRECATED __declspec(deprecated) -#else -#define DEPRECATED -#endif - - -#include - -namespace spdlog -{ - -class formatter; - -namespace sinks -{ -class sink; -} - -using log_clock = std::chrono::system_clock; -using sink_ptr = std::shared_ptr < sinks::sink >; -using sinks_init_list = std::initializer_list < sink_ptr >; -using formatter_ptr = std::shared_ptr; -#if defined(SPDLOG_NO_ATOMIC_LEVELS) -using level_t = details::null_atomic_int; -#else -using level_t = std::atomic_int; -#endif - -using log_err_handler = std::function; - -//Log level enum -namespace level -{ -typedef enum -{ - trace = 0, - debug = 1, - info = 2, - warn = 3, - err = 4, - critical = 5, - off = 6 -} level_enum; - -static const char* level_names[] { "trace", "debug", "info", "warning", "error", "critical", "off" }; - -static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" }; - -inline const char* to_str(spdlog::level::level_enum l) -{ - return level_names[l]; -} - -inline const char* to_short_str(spdlog::level::level_enum l) -{ - return short_level_names[l]; -} -} //level - - -// -// Async overflow policy - block by default. -// -enum class async_overflow_policy -{ - block_retry, // Block / yield / sleep until message can be enqueued - discard_log_msg // Discard the message it enqueue fails -}; - - -// -// Log exception -// -namespace details -{ -namespace os -{ -std::string errno_str(int err_num); -} -} -class spdlog_ex: public std::exception -{ -public: - spdlog_ex(const std::string& msg):_msg(msg) - {} - spdlog_ex(const std::string& msg, int last_errno) - { - _msg = msg + ": " + details::os::errno_str(last_errno); - } - const char* what() const SPDLOG_NOEXCEPT override - { - return _msg.c_str(); - } -private: - std::string _msg; - -}; - -// -// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) -// -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) -using filename_t = std::wstring; -#else -using filename_t = std::string; -#endif - - -} //spdlog diff --git a/include/cpd/vendor/spdlog/details/async_log_helper.h b/include/cpd/vendor/spdlog/details/async_log_helper.h deleted file mode 100644 index 7e9b3eb1..00000000 --- a/include/cpd/vendor/spdlog/details/async_log_helper.h +++ /dev/null @@ -1,390 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -// async log helper : -// Process logs asynchronously using a back thread. -// -// If the internal queue of log messages reaches its max size, -// then the client call will block until there is more room. -// -// If the back thread throws during logging, a spdlog::spdlog_ex exception -// will be thrown in client's thread when tries to log the next message - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace spdlog -{ -namespace details -{ - -class async_log_helper -{ - // Async msg to move to/from the queue - // Movable only. should never be copied - enum class async_msg_type - { - log, - flush, - terminate - }; - struct async_msg - { - std::string logger_name; - level::level_enum level; - log_clock::time_point time; - size_t thread_id; - std::string txt; - async_msg_type msg_type; - - async_msg() = default; - ~async_msg() = default; - - -async_msg(async_msg&& other) SPDLOG_NOEXCEPT: - logger_name(std::move(other.logger_name)), - level(std::move(other.level)), - time(std::move(other.time)), - txt(std::move(other.txt)), - msg_type(std::move(other.msg_type)) - {} - - async_msg(async_msg_type m_type) :msg_type(m_type) - {}; - - async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT - { - logger_name = std::move(other.logger_name); - level = other.level; - time = std::move(other.time); - thread_id = other.thread_id; - txt = std::move(other.txt); - msg_type = other.msg_type; - return *this; - } - - // never copy or assign. should only be moved.. - async_msg(const async_msg&) = delete; - async_msg& operator=(async_msg& other) = delete; - - // construct from log_msg - async_msg(const details::log_msg& m) : - level(m.level), - time(m.time), - thread_id(m.thread_id), - txt(m.raw.data(), m.raw.size()), - msg_type(async_msg_type::log) - { -#ifndef SPDLOG_NO_NAME - logger_name = *m.logger_name; -#endif - } - - - // copy into log_msg - void fill_log_msg(log_msg &msg) - { - msg.logger_name = &logger_name; - msg.level = level; - msg.time = time; - msg.thread_id = thread_id; - msg.raw << txt; - } - }; - -public: - - using item_type = async_msg; - using q_type = details::mpmc_bounded_queue; - - using clock = std::chrono::steady_clock; - - - async_log_helper(formatter_ptr formatter, - const std::vector& sinks, - size_t queue_size, - const log_err_handler err_handler, - const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, - const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), - const std::function& worker_teardown_cb = nullptr); - - void log(const details::log_msg& msg); - - // stop logging and join the back thread - ~async_log_helper(); - - void set_formatter(formatter_ptr); - - void flush(bool wait_for_q); - - -private: - formatter_ptr _formatter; - std::vector> _sinks; - - // queue of messages to log - q_type _q; - - log_err_handler _err_handler; - - bool _flush_requested; - - bool _terminate_requested; - - - // overflow policy - const async_overflow_policy _overflow_policy; - - // worker thread warmup callback - one can set thread priority, affinity, etc - const std::function _worker_warmup_cb; - - // auto periodic sink flush parameter - const std::chrono::milliseconds _flush_interval_ms; - - // worker thread teardown callback - const std::function _worker_teardown_cb; - - // worker thread - std::thread _worker_thread; - - void push_msg(async_msg&& new_msg); - - // worker thread main loop - void worker_loop(); - - // pop next message from the queue and process it. will set the last_pop to the pop time - // return false if termination of the queue is required - bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush); - - void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush); - - // sleep,yield or return immediatly using the time passed since last message as a hint - static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time); - - // wait until the queue is empty - void wait_empty_q(); - -}; -} -} - -/////////////////////////////////////////////////////////////////////////////// -// async_sink class implementation -/////////////////////////////////////////////////////////////////////////////// -inline spdlog::details::async_log_helper::async_log_helper( - formatter_ptr formatter, - const std::vector& sinks, - size_t queue_size, - log_err_handler err_handler, - const async_overflow_policy overflow_policy, - const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms, - const std::function& worker_teardown_cb): - _formatter(formatter), - _sinks(sinks), - _q(queue_size), - _err_handler(err_handler), - _flush_requested(false), - _terminate_requested(false), - _overflow_policy(overflow_policy), - _worker_warmup_cb(worker_warmup_cb), - _flush_interval_ms(flush_interval_ms), - _worker_teardown_cb(worker_teardown_cb), - _worker_thread(&async_log_helper::worker_loop, this) -{} - -// Send to the worker thread termination message(level=off) -// and wait for it to finish gracefully -inline spdlog::details::async_log_helper::~async_log_helper() -{ - try - { - push_msg(async_msg(async_msg_type::terminate)); - _worker_thread.join(); - } - catch (...) // don't crash in destructor - {} -} - - -//Try to push and block until succeeded (if the policy is not to discard when the queue is full) -inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) -{ - push_msg(async_msg(msg)); - - -} - -inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg) -{ - if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg) - { - auto last_op_time = details::os::now(); - auto now = last_op_time; - do - { - now = details::os::now(); - sleep_or_yield(now, last_op_time); - } - while (!_q.enqueue(std::move(new_msg))); - } - -} - -// optionally wait for the queue be empty and request flush from the sinks -inline void spdlog::details::async_log_helper::flush(bool wait_for_q) -{ - push_msg(async_msg(async_msg_type::flush)); - if(wait_for_q) - wait_empty_q(); //return only make after the above flush message was processed -} - -inline void spdlog::details::async_log_helper::worker_loop() -{ - try - { - if (_worker_warmup_cb) _worker_warmup_cb(); - auto last_pop = details::os::now(); - auto last_flush = last_pop; - while(process_next_msg(last_pop, last_flush)); - if (_worker_teardown_cb) _worker_teardown_cb(); - } - catch (const std::exception &ex) - { - _err_handler(ex.what()); - } - catch (...) - { - _err_handler("Unknown exception"); - } -} - -// process next message in the queue -// return true if this thread should still be active (while no terminate msg was received) -inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush) -{ - - async_msg incoming_async_msg; - - - if (_q.dequeue(incoming_async_msg)) - { - last_pop = details::os::now(); - switch (incoming_async_msg.msg_type) - { - case async_msg_type::flush: - _flush_requested = true; - break; - - case async_msg_type::terminate: - _flush_requested = true; - _terminate_requested = true; - break; - - default: - log_msg incoming_log_msg; - incoming_async_msg.fill_log_msg(incoming_log_msg); - _formatter->format(incoming_log_msg); - for (auto &s : _sinks) - { - if(s->should_log( incoming_log_msg.level)) - { - s->log(incoming_log_msg); - } - } - } - return true; - } - - // Handle empty queue.. - // This is the only place where the queue can terminate or flush to avoid losing messages already in the queue - else - { - auto now = details::os::now(); - handle_flush_interval(now, last_flush); - sleep_or_yield(now, last_pop); - return !_terminate_requested; - } -} - -// flush all sinks if _flush_interval_ms has expired -inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush) -{ - auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms); - if (should_flush) - { - for (auto &s : _sinks) - s->flush(); - now = last_flush = details::os::now(); - _flush_requested = false; - } -} - -inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) -{ - _formatter = msg_formatter; -} - - -// spin, yield or sleep. use the time passed since last message as a hint -inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time) -{ - using namespace std::this_thread; - using std::chrono::milliseconds; - using std::chrono::microseconds; - - auto time_since_op = now - last_op_time; - - // spin upto 50 micros - if (time_since_op <= microseconds(50)) - return; - - // yield upto 150 micros - if (time_since_op <= microseconds(100)) - return yield(); - - - // sleep for 20 ms upto 200 ms - if (time_since_op <= milliseconds(200)) - return sleep_for(milliseconds(20)); - - // sleep for 200 ms - return sleep_for(milliseconds(200)); -} - -// wait for the queue to be empty -inline void spdlog::details::async_log_helper::wait_empty_q() -{ - auto last_op = details::os::now(); - while (_q.approx_size() > 0) - { - sleep_or_yield(details::os::now(), last_op); - } - -} - - - - - - - - diff --git a/include/cpd/vendor/spdlog/details/async_logger_impl.h b/include/cpd/vendor/spdlog/details/async_logger_impl.h deleted file mode 100644 index 736d2e31..00000000 --- a/include/cpd/vendor/spdlog/details/async_logger_impl.h +++ /dev/null @@ -1,89 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// Async Logger implementation -// Use an async_sink (queue per logger) to perform the logging in a worker thread - -#include -#include - -#include -#include -#include -#include - -template -inline spdlog::async_logger::async_logger(const std::string& logger_name, - const It& begin, - const It& end, - size_t queue_size, - const async_overflow_policy overflow_policy, - const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms, - const std::function& worker_teardown_cb) : - logger(logger_name, begin, end), - _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) -{ -} - -inline spdlog::async_logger::async_logger(const std::string& logger_name, - sinks_init_list sinks, - size_t queue_size, - const async_overflow_policy overflow_policy, - const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms, - const std::function& worker_teardown_cb) : - async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} - -inline spdlog::async_logger::async_logger(const std::string& logger_name, - sink_ptr single_sink, - size_t queue_size, - const async_overflow_policy overflow_policy, - const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms, - const std::function& worker_teardown_cb) : - async_logger(logger_name, -{ - single_sink -}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} - - -inline void spdlog::async_logger::flush() -{ - _async_log_helper->flush(true); -} - -inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) -{ - _formatter = msg_formatter; - _async_log_helper->set_formatter(_formatter); -} - -inline void spdlog::async_logger::_set_pattern(const std::string& pattern) -{ - _formatter = std::make_shared(pattern); - _async_log_helper->set_formatter(_formatter); -} - - -inline void spdlog::async_logger::_sink_it(details::log_msg& msg) -{ - try - { - _async_log_helper->log(msg); - if (_should_flush_on(msg)) - _async_log_helper->flush(false); // do async flush - } - catch (const std::exception &ex) - { - _err_handler(ex.what()); - } - catch (...) - { - _err_handler("Unknown exception"); - } -} diff --git a/include/cpd/vendor/spdlog/details/file_helper.h b/include/cpd/vendor/spdlog/details/file_helper.h deleted file mode 100644 index 2e6ce9d2..00000000 --- a/include/cpd/vendor/spdlog/details/file_helper.h +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// Helper class for file sink -// When failing to open a file, retry several times(5) with small delay between the tries(10 ms) -// Can be set to auto flush on every line -// Throw spdlog_ex exception on errors - -#include -#include - -#include -#include -#include -#include -#include - -namespace spdlog -{ -namespace details -{ - -class file_helper -{ - -public: - const int open_tries = 5; - const int open_interval = 10; - - explicit file_helper() : - _fd(nullptr) - {} - - file_helper(const file_helper&) = delete; - file_helper& operator=(const file_helper&) = delete; - - ~file_helper() - { - close(); - } - - - void open(const filename_t& fname, bool truncate = false) - { - - close(); - auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); - _filename = fname; - for (int tries = 0; tries < open_tries; ++tries) - { - if (!os::fopen_s(&_fd, fname, mode)) - return; - - std::this_thread::sleep_for(std::chrono::milliseconds(open_interval)); - } - - throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); - } - - void reopen(bool truncate) - { - if (_filename.empty()) - throw spdlog_ex("Failed re opening file - was not opened before"); - open(_filename, truncate); - - } - - void flush() - { - std::fflush(_fd); - } - - void close() - { - if (_fd) - { - std::fclose(_fd); - _fd = nullptr; - } - } - - void write(const log_msg& msg) - { - - size_t msg_size = msg.formatted.size(); - auto data = msg.formatted.data(); - if (std::fwrite(data, 1, msg_size, _fd) != msg_size) - throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); - } - - size_t size() - { - if (!_fd) - throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); - return os::filesize(_fd); - } - - const filename_t& filename() const - { - return _filename; - } - - static bool file_exists(const filename_t& name) - { - - return os::file_exists(name); - } - -private: - FILE* _fd; - filename_t _filename; -}; -} -} diff --git a/include/cpd/vendor/spdlog/details/log_msg.h b/include/cpd/vendor/spdlog/details/log_msg.h deleted file mode 100644 index ecdc73d7..00000000 --- a/include/cpd/vendor/spdlog/details/log_msg.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include - - -#include -#include - -namespace spdlog -{ -namespace details -{ -struct log_msg -{ - log_msg() = default; - log_msg(const std::string *loggers_name, level::level_enum lvl) : logger_name(loggers_name), level(lvl) - { -#ifndef SPDLOG_NO_DATETIME - time = os::now(); -#endif - -#ifndef SPDLOG_NO_THREAD_ID - thread_id = os::thread_id(); -#endif - } - - log_msg(const log_msg& other) = delete; - log_msg& operator=(log_msg&& other) = delete; - log_msg(log_msg&& other) = delete; - - - const std::string *logger_name; - level::level_enum level; - log_clock::time_point time; - size_t thread_id; - fmt::MemoryWriter raw; - fmt::MemoryWriter formatted; -}; -} -} diff --git a/include/cpd/vendor/spdlog/details/logger_impl.h b/include/cpd/vendor/spdlog/details/logger_impl.h deleted file mode 100644 index a337b359..00000000 --- a/include/cpd/vendor/spdlog/details/logger_impl.h +++ /dev/null @@ -1,293 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include - -#include -#include - - -// create logger with given name, sinks and the default pattern formatter -// all other ctors will call this one -template -inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end): - _name(logger_name), - _sinks(begin, end), - _formatter(std::make_shared("%+")) -{ - _level = level::info; - _flush_level = level::off; - _last_err_time = 0; - _err_handler = [this](const std::string &msg) - { - this->_default_err_handler(msg); - }; -} - -// ctor with sinks as init list -inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list): - logger(logger_name, sinks_list.begin(), sinks_list.end()) -{} - - -// ctor with single sink -inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink): - logger(logger_name, -{ - single_sink -}) -{} - - -inline spdlog::logger::~logger() = default; - - -inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter) -{ - _set_formatter(msg_formatter); -} - -inline void spdlog::logger::set_pattern(const std::string& pattern) -{ - _set_pattern(pattern); -} - - -template -inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args) -{ - if (!should_log(lvl)) return; - - try - { - details::log_msg log_msg(&_name, lvl); - log_msg.raw.write(fmt, args...); - _sink_it(log_msg); - } - catch (const std::exception &ex) - { - _err_handler(ex.what()); - } - catch (...) - { - _err_handler("Unknown exception"); - } -} - -template -inline void spdlog::logger::log(level::level_enum lvl, const char* msg) -{ - if (!should_log(lvl)) return; - try - { - details::log_msg log_msg(&_name, lvl); - log_msg.raw << msg; - _sink_it(log_msg); - } - catch (const std::exception &ex) - { - _err_handler(ex.what()); - } - catch (...) - { - _err_handler("Unknown exception"); - } - -} - -template -inline void spdlog::logger::log(level::level_enum lvl, const T& msg) -{ - if (!should_log(lvl)) return; - try - { - details::log_msg log_msg(&_name, lvl); - log_msg.raw << msg; - _sink_it(log_msg); - } - catch (const std::exception &ex) - { - _err_handler(ex.what()); - } - catch (...) - { - _err_handler("Unknown exception"); - } -} - - -template -inline void spdlog::logger::trace(const char* fmt, const Args&... args) -{ - log(level::trace, fmt, args...); -} - -template -inline void spdlog::logger::debug(const char* fmt, const Args&... args) -{ - log(level::debug, fmt, args...); -} - -template -inline void spdlog::logger::info(const char* fmt, const Args&... args) -{ - log(level::info, fmt, args...); -} - - -template -inline void spdlog::logger::warn(const char* fmt, const Args&... args) -{ - log(level::warn, fmt, args...); -} - -template -inline void spdlog::logger::error(const char* fmt, const Args&... args) -{ - log(level::err, fmt, args...); -} - -template -inline void spdlog::logger::critical(const char* fmt, const Args&... args) -{ - log(level::critical, fmt, args...); -} - - -template -inline void spdlog::logger::trace(const T& msg) -{ - log(level::trace, msg); -} - -template -inline void spdlog::logger::debug(const T& msg) -{ - log(level::debug, msg); -} - - -template -inline void spdlog::logger::info(const T& msg) -{ - log(level::info, msg); -} - - -template -inline void spdlog::logger::warn(const T& msg) -{ - log(level::warn, msg); -} - -template -inline void spdlog::logger::error(const T& msg) -{ - log(level::err, msg); -} - -template -inline void spdlog::logger::critical(const T& msg) -{ - log(level::critical, msg); -} - - - - -// -// name and level -// -inline const std::string& spdlog::logger::name() const -{ - return _name; -} - -inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) -{ - _level.store(log_level); -} - -inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) -{ - _err_handler = err_handler; -} - -inline spdlog::log_err_handler spdlog::logger::error_handler() -{ - return _err_handler; -} - - -inline void spdlog::logger::flush_on(level::level_enum log_level) -{ - _flush_level.store(log_level); -} - -inline spdlog::level::level_enum spdlog::logger::level() const -{ - return static_cast(_level.load(std::memory_order_relaxed)); -} - -inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const -{ - return msg_level >= _level.load(std::memory_order_relaxed); -} - -// -// protected virtual called at end of each user log call (if enabled) by the line_logger -// -inline void spdlog::logger::_sink_it(details::log_msg& msg) -{ - _formatter->format(msg); - for (auto &sink : _sinks) - { - if( sink->should_log( msg.level)) - { - sink->log(msg); - } - } - - if(_should_flush_on(msg)) - flush(); -} - -inline void spdlog::logger::_set_pattern(const std::string& pattern) -{ - _formatter = std::make_shared(pattern); -} -inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) -{ - _formatter = msg_formatter; -} - -inline void spdlog::logger::flush() -{ - for (auto& sink : _sinks) - sink->flush(); -} - -inline void spdlog::logger::_default_err_handler(const std::string &msg) -{ - auto now = time(nullptr); - if (now - _last_err_time < 60) - return; - auto tm_time = details::os::localtime(now); - char date_buf[100]; - std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); - details::log_msg err_msg; - err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol); - sinks::stderr_sink_mt::instance()->log(err_msg); - _last_err_time = now; -} - -inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg) -{ - const auto flush_level = _flush_level.load(std::memory_order_relaxed); - return (msg.level >= flush_level) && (msg.level != level::off); -} diff --git a/include/cpd/vendor/spdlog/details/mpmc_bounded_q.h b/include/cpd/vendor/spdlog/details/mpmc_bounded_q.h deleted file mode 100644 index 3a46e8eb..00000000 --- a/include/cpd/vendor/spdlog/details/mpmc_bounded_q.h +++ /dev/null @@ -1,172 +0,0 @@ -/* -A modified version of Bounded MPMC queue by Dmitry Vyukov. - -Original code from: -http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue - -licensed by Dmitry Vyukov under the terms below: - -Simplified BSD license - -Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this list of -conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list -of conditions and the following disclaimer in the documentation and/or other materials -provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT -SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those of the authors and -should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov. -*/ - -/* -The code in its current form adds the license below: - -Copyright(c) 2015 Gabi Melman. -Distributed under the MIT License (http://opensource.org/licenses/MIT) - -*/ - -#pragma once - -#include - -#include -#include - -namespace spdlog -{ -namespace details -{ - -template -class mpmc_bounded_queue -{ -public: - - using item_type = T; - mpmc_bounded_queue(size_t buffer_size) - :max_size_(buffer_size), - buffer_(new cell_t [buffer_size]), - buffer_mask_(buffer_size - 1) - { - //queue size must be power of two - if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) - throw spdlog_ex("async logger queue size must be power of two"); - - for (size_t i = 0; i != buffer_size; i += 1) - buffer_[i].sequence_.store(i, std::memory_order_relaxed); - enqueue_pos_.store(0, std::memory_order_relaxed); - dequeue_pos_.store(0, std::memory_order_relaxed); - } - - ~mpmc_bounded_queue() - { - delete [] buffer_; - } - - - bool enqueue(T&& data) - { - cell_t* cell; - size_t pos = enqueue_pos_.load(std::memory_order_relaxed); - for (;;) - { - cell = &buffer_[pos & buffer_mask_]; - size_t seq = cell->sequence_.load(std::memory_order_acquire); - intptr_t dif = (intptr_t)seq - (intptr_t)pos; - if (dif == 0) - { - if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) - break; - } - else if (dif < 0) - { - return false; - } - else - { - pos = enqueue_pos_.load(std::memory_order_relaxed); - } - } - cell->data_ = std::move(data); - cell->sequence_.store(pos + 1, std::memory_order_release); - return true; - } - - bool dequeue(T& data) - { - cell_t* cell; - size_t pos = dequeue_pos_.load(std::memory_order_relaxed); - for (;;) - { - cell = &buffer_[pos & buffer_mask_]; - size_t seq = - cell->sequence_.load(std::memory_order_acquire); - intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1); - if (dif == 0) - { - if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) - break; - } - else if (dif < 0) - return false; - else - pos = dequeue_pos_.load(std::memory_order_relaxed); - } - data = std::move(cell->data_); - cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release); - return true; - } - - size_t approx_size() - { - size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed); - size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed); - if (last_pos <= first_pos) - return 0; - auto size = last_pos - first_pos; - return size < max_size_ ? size : max_size_; - } - -private: - struct cell_t - { - std::atomic sequence_; - T data_; - }; - - size_t const max_size_; - - static size_t const cacheline_size = 64; - typedef char cacheline_pad_t [cacheline_size]; - - cacheline_pad_t pad0_; - cell_t* const buffer_; - size_t const buffer_mask_; - cacheline_pad_t pad1_; - std::atomic enqueue_pos_; - cacheline_pad_t pad2_; - std::atomic dequeue_pos_; - cacheline_pad_t pad3_; - - mpmc_bounded_queue(mpmc_bounded_queue const&) = delete; - void operator= (mpmc_bounded_queue const&) = delete; -}; - -} // ns details -} // ns spdlog diff --git a/include/cpd/vendor/spdlog/details/null_mutex.h b/include/cpd/vendor/spdlog/details/null_mutex.h deleted file mode 100644 index 67b0aeee..00000000 --- a/include/cpd/vendor/spdlog/details/null_mutex.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -// null, no cost dummy "mutex" and dummy "atomic" int - -namespace spdlog -{ -namespace details -{ -struct null_mutex -{ - void lock() {} - void unlock() {} - bool try_lock() - { - return true; - } -}; - -struct null_atomic_int -{ - int value; - null_atomic_int() = default; - - null_atomic_int(int val):value(val) - {} - - int load(std::memory_order) const - { - return value; - } - - void store(int val) - { - value = val; - } -}; - -} -} diff --git a/include/cpd/vendor/spdlog/details/os.h b/include/cpd/vendor/spdlog/details/os.h deleted file mode 100644 index ed4f45cd..00000000 --- a/include/cpd/vendor/spdlog/details/os.h +++ /dev/null @@ -1,360 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include - - -#ifdef _WIN32 - -#ifndef NOMINMAX -#define NOMINMAX //prevent windows redefining min/max -#endif - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include - -#ifdef __MINGW32__ -#include -#endif - -#include - -#elif __linux__ - -#include //Use gettid() syscall under linux to get thread id -#include -#include - -#elif __FreeBSD__ -#include //Use thr_self() syscall under FreeBSD to get thread id - -#else -#include - -#endif - -namespace spdlog -{ -namespace details -{ -namespace os -{ - -inline spdlog::log_clock::time_point now() -{ - -#if defined __linux__ && defined SPDLOG_CLOCK_COARSE - timespec ts; - ::clock_gettime(CLOCK_REALTIME_COARSE, &ts); - return std::chrono::time_point( - std::chrono::duration_cast( - std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); - - -#else - return log_clock::now(); -#endif - -} -inline std::tm localtime(const std::time_t &time_tt) -{ - -#ifdef _WIN32 - std::tm tm; - localtime_s(&tm, &time_tt); -#else - std::tm tm; - localtime_r(&time_tt, &tm); -#endif - return tm; -} - -inline std::tm localtime() -{ - std::time_t now_t = time(nullptr); - return localtime(now_t); -} - - -inline std::tm gmtime(const std::time_t &time_tt) -{ - -#ifdef _WIN32 - std::tm tm; - gmtime_s(&tm, &time_tt); -#else - std::tm tm; - gmtime_r(&time_tt, &tm); -#endif - return tm; -} - -inline std::tm gmtime() -{ - std::time_t now_t = time(nullptr); - return gmtime(now_t); -} -inline bool operator==(const std::tm& tm1, const std::tm& tm2) -{ - return (tm1.tm_sec == tm2.tm_sec && - tm1.tm_min == tm2.tm_min && - tm1.tm_hour == tm2.tm_hour && - tm1.tm_mday == tm2.tm_mday && - tm1.tm_mon == tm2.tm_mon && - tm1.tm_year == tm2.tm_year && - tm1.tm_isdst == tm2.tm_isdst); -} - -inline bool operator!=(const std::tm& tm1, const std::tm& tm2) -{ - return !(tm1 == tm2); -} - -// eol definition -#if !defined (SPDLOG_EOL) -#ifdef _WIN32 -#define SPDLOG_EOL "\r\n" -#else -#define SPDLOG_EOL "\n" -#endif -#endif - -SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; -SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1; - - - -//fopen_s on non windows for writing -inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode) -{ -#ifdef _WIN32 -#ifdef SPDLOG_WCHAR_FILENAMES - *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); -#else - *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); -#endif - return *fp == nullptr; -#else - *fp = fopen((filename.c_str()), mode.c_str()); - return *fp == nullptr; -#endif -} - -inline int remove(const filename_t &filename) -{ -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return _wremove(filename.c_str()); -#else - return std::remove(filename.c_str()); -#endif -} - -inline int rename(const filename_t& filename1, const filename_t& filename2) -{ -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return _wrename(filename1.c_str(), filename2.c_str()); -#else - return std::rename(filename1.c_str(), filename2.c_str()); -#endif -} - - -//Return if file exists -inline bool file_exists(const filename_t& filename) -{ -#ifdef _WIN32 -#ifdef SPDLOG_WCHAR_FILENAMES - auto attribs = GetFileAttributesW(filename.c_str()); -#else - auto attribs = GetFileAttributesA(filename.c_str()); -#endif - return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); -#else //common linux/unix all have the stat system call - struct stat buffer; - return (stat (filename.c_str(), &buffer) == 0); -#endif -} - - - - -//Return file size according to open FILE* object -inline size_t filesize(FILE *f) -{ - if (f == nullptr) - throw spdlog_ex("Failed getting file size. fd is null"); -#ifdef _WIN32 - int fd = _fileno(f); -#if _WIN64 //64 bits - struct _stat64 st; - if (_fstat64(fd, &st) == 0) - return st.st_size; - -#else //windows 32 bits - struct _stat st; - if (_fstat(fd, &st) == 0) - return st.st_size; -#endif - -#else // unix - int fd = fileno(f); - //64 bits(but not in osx, where fstat64 is deprecated) -#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) - struct stat64 st; - if (fstat64(fd, &st) == 0) - return st.st_size; -#else // unix 32 bits or osx - struct stat st; - if (fstat(fd, &st) == 0) - return st.st_size; -#endif -#endif - throw spdlog_ex("Failed getting file size from fd", errno); -} - - - - -//Return utc offset in minutes or throw spdlog_ex on failure -inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) -{ - -#ifdef _WIN32 -#if _WIN32_WINNT < _WIN32_WINNT_WS08 - TIME_ZONE_INFORMATION tzinfo; - auto rv = GetTimeZoneInformation(&tzinfo); -#else - DYNAMIC_TIME_ZONE_INFORMATION tzinfo; - auto rv = GetDynamicTimeZoneInformation(&tzinfo); -#endif - if (rv == TIME_ZONE_ID_INVALID) - throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); - - int offset = -tzinfo.Bias; - if (tm.tm_isdst) - offset -= tzinfo.DaylightBias; - else - offset -= tzinfo.StandardBias; - return offset; -#else - -#if defined(sun) || defined(__sun) - // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris - struct helper - { - static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime()) - { - int local_year = localtm.tm_year + (1900 - 1); - int gmt_year = gmtm.tm_year + (1900 - 1); - - long int days = ( - // difference in day of year - localtm.tm_yday - gmtm.tm_yday - - // + intervening leap days - + ((local_year >> 2) - (gmt_year >> 2)) - - (local_year / 100 - gmt_year / 100) - + ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) - - // + difference in years * 365 */ - + (long int)(local_year - gmt_year) * 365 - ); - - long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); - long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); - long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); - - return secs; - } - }; - - long int offset_seconds = helper::calculate_gmt_offset(tm); -#else - long int offset_seconds = tm.tm_gmtoff; -#endif - - return static_cast(offset_seconds / 60); -#endif -} - -//Return current thread id as size_t -//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013) -inline size_t thread_id() -{ -#ifdef _WIN32 - return static_cast(::GetCurrentThreadId()); -#elif __linux__ -# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) -# define SYS_gettid __NR_gettid -# endif - return static_cast(syscall(SYS_gettid)); -#elif __FreeBSD__ - long tid; - thr_self(&tid); - return static_cast(tid); -#else //Default to standard C++11 (OSX and other Unix) - return static_cast(std::hash()(std::this_thread::get_id())); -#endif - - -} - - -// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) -#define SPDLOG_FILENAME_T(s) L ## s -inline std::string filename_to_str(const filename_t& filename) -{ - std::wstring_convert, wchar_t> c; - return c.to_bytes(filename); -} -#else -#define SPDLOG_FILENAME_T(s) s -inline std::string filename_to_str(const filename_t& filename) -{ - return filename; -} -#endif - - -// Return errno string (thread safe) -inline std::string errno_str(int err_num) -{ - char buf[256]; - SPDLOG_CONSTEXPR auto buf_size = sizeof(buf); - -#ifdef _WIN32 - if(strerror_s(buf, buf_size, err_num) == 0) - return std::string(buf); - else - return "Unkown error"; - -#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || \ - ((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version - - if (strerror_r(err_num, buf, buf_size) == 0) - return std::string(buf); - else - return "Unkown error"; - -#else // gnu version (might not use the given buf, so its retval pointer must be used) - return std::string(strerror_r(err_num, buf, buf_size)); -#endif -} - -} //os -} //details -} //spdlog diff --git a/include/cpd/vendor/spdlog/details/pattern_formatter_impl.h b/include/cpd/vendor/spdlog/details/pattern_formatter_impl.h deleted file mode 100644 index 73c0db3b..00000000 --- a/include/cpd/vendor/spdlog/details/pattern_formatter_impl.h +++ /dev/null @@ -1,636 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace spdlog -{ -namespace details -{ -class flag_formatter -{ -public: - virtual ~flag_formatter() - {} - virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0; -}; - -/////////////////////////////////////////////////////////////////////// -// name & level pattern appenders -/////////////////////////////////////////////////////////////////////// -namespace -{ -class name_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << *msg.logger_name; - } -}; -} - -// log level appender -class level_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << level::to_str(msg.level); - } -}; - -// short log level appender -class short_level_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << level::to_short_str(msg.level); - } -}; - -/////////////////////////////////////////////////////////////////////// -// Date time pattern appenders -/////////////////////////////////////////////////////////////////////// - -static const char* ampm(const tm& t) -{ - return t.tm_hour >= 12 ? "PM" : "AM"; -} - -static int to12h(const tm& t) -{ - return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; -} - -//Abbreviated weekday name -static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; -class a_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << days[tm_time.tm_wday]; - } -}; - -//Full weekday name -static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; -class A_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << full_days[tm_time.tm_wday]; - } -}; - -//Abbreviated month -static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; -class b_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << months[tm_time.tm_mon]; - } -}; - -//Full month name -static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; -class B_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << full_months[tm_time.tm_mon]; - } -}; - - -//write 2 ints seperated by sep with padding of 2 -static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep) -{ - w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0'); - return w; -} - -//write 3 ints seperated by sep with padding of 2 -static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep) -{ - w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0'); - return w; -} - - -//Date and time representation (Thu Aug 23 15:35:46 2014) -class c_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' '; - pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900; - } -}; - - -// year - 2 digit -class C_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0'); - } -}; - - - -// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 -class D_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/'); - } -}; - - -// year - 4 digit -class Y_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << tm_time.tm_year + 1900; - } -}; - -// month 1-12 -class m_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0'); - } -}; - -// day of month 1-31 -class d_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0'); - } -}; - -// hours in 24 format 0-23 -class H_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0'); - } -}; - -// hours in 12 format 1-12 -class I_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << fmt::pad(to12h(tm_time), 2, '0'); - } -}; - -// minutes 0-59 -class M_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << fmt::pad(tm_time.tm_min, 2, '0'); - } -}; - -// seconds 0-59 -class S_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0'); - } -}; - -// milliseconds -class e_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - auto duration = msg.time.time_since_epoch(); - auto millis = std::chrono::duration_cast(duration).count() % 1000; - msg.formatted << fmt::pad(static_cast(millis), 3, '0'); - } -}; - -// microseconds -class f_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - auto duration = msg.time.time_since_epoch(); - auto micros = std::chrono::duration_cast(duration).count() % 1000000; - msg.formatted << fmt::pad(static_cast(micros), 6, '0'); - } -}; - -// nanoseconds -class F_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - auto duration = msg.time.time_since_epoch(); - auto ns = std::chrono::duration_cast(duration).count() % 1000000000; - msg.formatted << fmt::pad(static_cast(ns), 9, '0'); - } -}; - -// AM/PM -class p_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - msg.formatted << ampm(tm_time); - } -}; - - -// 12 hour clock 02:55:02 pm -class r_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time); - } -}; - -// 24-hour HH:MM time, equivalent to %H:%M -class R_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':'); - } -}; - -// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S -class T_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { - pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':'); - } -}; - - -// ISO 8601 offset from UTC in timezone (+-HH:MM) -class z_formatter:public flag_formatter -{ -public: - const std::chrono::seconds cache_refresh = std::chrono::seconds(5); - - z_formatter():_last_update(std::chrono::seconds(0)) - {} - z_formatter(const z_formatter&) = delete; - z_formatter& operator=(const z_formatter&) = delete; - - void format(details::log_msg& msg, const std::tm& tm_time) override - { -#ifdef _WIN32 - int total_minutes = get_cached_offset(msg, tm_time); -#else - // No need to chache under gcc, - // it is very fast (already stored in tm.tm_gmtoff) - int total_minutes = os::utc_minutes_offset(tm_time); -#endif - bool is_negative = total_minutes < 0; - char sign; - if (is_negative) - { - total_minutes = -total_minutes; - sign = '-'; - } - else - { - sign = '+'; - } - - int h = total_minutes / 60; - int m = total_minutes % 60; - msg.formatted << sign; - pad_n_join(msg.formatted, h, m, ':'); - } -private: - log_clock::time_point _last_update; - int _offset_minutes; - std::mutex _mutex; - - int get_cached_offset(const log_msg& msg, const std::tm& tm_time) - { - using namespace std::chrono; - std::lock_guard l(_mutex); - if (msg.time - _last_update >= cache_refresh) - { - _offset_minutes = os::utc_minutes_offset(tm_time); - _last_update = msg.time; - } - return _offset_minutes; - } -}; - - - -//Thread id -class t_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << msg.thread_id; - } -}; - - -class v_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); - } -}; - -class ch_formatter:public flag_formatter -{ -public: - explicit ch_formatter(char ch): _ch(ch) - {} - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << _ch; - } -private: - char _ch; -}; - - -//aggregate user chars to display as is -class aggregate_formatter:public flag_formatter -{ -public: - aggregate_formatter() - {} - void add_ch(char ch) - { - _str += ch; - } - void format(details::log_msg& msg, const std::tm&) override - { - msg.formatted << _str; - } -private: - std::string _str; -}; - -// Full info formatter -// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v -class full_formatter:public flag_formatter -{ - void format(details::log_msg& msg, const std::tm& tm_time) override - { -#ifndef SPDLOG_NO_DATETIME - auto duration = msg.time.time_since_epoch(); - auto millis = std::chrono::duration_cast(duration).count() % 1000; - - /* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads), - msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ", - tm_time.tm_year + 1900, - tm_time.tm_mon + 1, - tm_time.tm_mday, - tm_time.tm_hour, - tm_time.tm_min, - tm_time.tm_sec, - static_cast(millis), - msg.logger_name, - level::to_str(msg.level), - msg.raw.str());*/ - - - // Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads) - msg.formatted << '[' << static_cast(tm_time.tm_year + 1900) << '-' - << fmt::pad(static_cast(tm_time.tm_mon + 1), 2, '0') << '-' - << fmt::pad(static_cast(tm_time.tm_mday), 2, '0') << ' ' - << fmt::pad(static_cast(tm_time.tm_hour), 2, '0') << ':' - << fmt::pad(static_cast(tm_time.tm_min), 2, '0') << ':' - << fmt::pad(static_cast(tm_time.tm_sec), 2, '0') << '.' - << fmt::pad(static_cast(millis), 3, '0') << "] "; - - //no datetime needed -#else - (void)tm_time; -#endif - -#ifndef SPDLOG_NO_NAME - msg.formatted << '[' << *msg.logger_name << "] "; -#endif - - msg.formatted << '[' << level::to_str(msg.level) << "] "; - msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); - } -}; - -} -} -/////////////////////////////////////////////////////////////////////////////// -// pattern_formatter inline impl -/////////////////////////////////////////////////////////////////////////////// -inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern) -{ - compile_pattern(pattern); -} - -inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern) -{ - auto end = pattern.end(); - std::unique_ptr user_chars; - for (auto it = pattern.begin(); it != end; ++it) - { - if (*it == '%') - { - if (user_chars) //append user chars found so far - _formatters.push_back(std::move(user_chars)); - - if (++it != end) - handle_flag(*it); - else - break; - } - else // chars not following the % sign should be displayed as is - { - if (!user_chars) - user_chars = std::unique_ptr(new details::aggregate_formatter()); - user_chars->add_ch(*it); - } - } - if (user_chars) //append raw chars found so far - { - _formatters.push_back(std::move(user_chars)); - } - -} -inline void spdlog::pattern_formatter::handle_flag(char flag) -{ - switch (flag) - { - // logger name - case 'n': - _formatters.push_back(std::unique_ptr(new details::name_formatter())); - break; - - case 'l': - _formatters.push_back(std::unique_ptr(new details::level_formatter())); - break; - - case 'L': - _formatters.push_back(std::unique_ptr(new details::short_level_formatter())); - break; - - case('t'): - _formatters.push_back(std::unique_ptr(new details::t_formatter())); - break; - - case('v'): - _formatters.push_back(std::unique_ptr(new details::v_formatter())); - break; - - case('a'): - _formatters.push_back(std::unique_ptr(new details::a_formatter())); - break; - - case('A'): - _formatters.push_back(std::unique_ptr(new details::A_formatter())); - break; - - case('b'): - case('h'): - _formatters.push_back(std::unique_ptr(new details::b_formatter())); - break; - - case('B'): - _formatters.push_back(std::unique_ptr(new details::B_formatter())); - break; - case('c'): - _formatters.push_back(std::unique_ptr(new details::c_formatter())); - break; - - case('C'): - _formatters.push_back(std::unique_ptr(new details::C_formatter())); - break; - - case('Y'): - _formatters.push_back(std::unique_ptr(new details::Y_formatter())); - break; - - case('D'): - case('x'): - - _formatters.push_back(std::unique_ptr(new details::D_formatter())); - break; - - case('m'): - _formatters.push_back(std::unique_ptr(new details::m_formatter())); - break; - - case('d'): - _formatters.push_back(std::unique_ptr(new details::d_formatter())); - break; - - case('H'): - _formatters.push_back(std::unique_ptr(new details::H_formatter())); - break; - - case('I'): - _formatters.push_back(std::unique_ptr(new details::I_formatter())); - break; - - case('M'): - _formatters.push_back(std::unique_ptr(new details::M_formatter())); - break; - - case('S'): - _formatters.push_back(std::unique_ptr(new details::S_formatter())); - break; - - case('e'): - _formatters.push_back(std::unique_ptr(new details::e_formatter())); - break; - - case('f'): - _formatters.push_back(std::unique_ptr(new details::f_formatter())); - break; - case('F'): - _formatters.push_back(std::unique_ptr(new details::F_formatter())); - break; - - case('p'): - _formatters.push_back(std::unique_ptr(new details::p_formatter())); - break; - - case('r'): - _formatters.push_back(std::unique_ptr(new details::r_formatter())); - break; - - case('R'): - _formatters.push_back(std::unique_ptr(new details::R_formatter())); - break; - - case('T'): - case('X'): - _formatters.push_back(std::unique_ptr(new details::T_formatter())); - break; - - case('z'): - _formatters.push_back(std::unique_ptr(new details::z_formatter())); - break; - - case ('+'): - _formatters.push_back(std::unique_ptr(new details::full_formatter())); - break; - - default: //Unkown flag appears as is - _formatters.push_back(std::unique_ptr(new details::ch_formatter('%'))); - _formatters.push_back(std::unique_ptr(new details::ch_formatter(flag))); - break; - } -} - - -inline void spdlog::pattern_formatter::format(details::log_msg& msg) -{ - -#ifndef SPDLOG_NO_DATETIME - auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time)); -#else - std::tm tm_time; -#endif - for (auto &f : _formatters) - { - f->format(msg, tm_time); - } - //write eol - msg.formatted.write(details::os::eol, details::os::eol_size); -} diff --git a/include/cpd/vendor/spdlog/details/registry.h b/include/cpd/vendor/spdlog/details/registry.h deleted file mode 100644 index ee14adfd..00000000 --- a/include/cpd/vendor/spdlog/details/registry.h +++ /dev/null @@ -1,185 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// Loggers registy of unique name->logger pointer -// An attempt to create a logger with an already existing name will be ignored -// If user requests a non existing logger, nullptr will be returned -// This class is thread safe - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace spdlog -{ -namespace details -{ -template class registry_t -{ -public: - - void register_logger(std::shared_ptr logger) - { - std::lock_guard lock(_mutex); - auto logger_name = logger->name(); - throw_if_exists(logger_name); - _loggers[logger_name] = logger; - } - - - std::shared_ptr get(const std::string& logger_name) - { - std::lock_guard lock(_mutex); - auto found = _loggers.find(logger_name); - return found == _loggers.end() ? nullptr : found->second; - } - - template - std::shared_ptr create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end) - { - std::lock_guard lock(_mutex); - throw_if_exists(logger_name); - std::shared_ptr new_logger; - if (_async_mode) - new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb); - else - new_logger = std::make_shared(logger_name, sinks_begin, sinks_end); - - if (_formatter) - new_logger->set_formatter(_formatter); - - if (_err_handler) - new_logger->set_error_handler(_err_handler); - - new_logger->set_level(_level); - - - //Add to registry - _loggers[logger_name] = new_logger; - return new_logger; - } - - void apply_all(std::function)> fun) - { - std::lock_guard lock(_mutex); - for (auto &l : _loggers) - fun(l.second); - } - - void drop(const std::string& logger_name) - { - std::lock_guard lock(_mutex); - _loggers.erase(logger_name); - } - - void drop_all() - { - std::lock_guard lock(_mutex); - _loggers.clear(); - } - std::shared_ptr create(const std::string& logger_name, sinks_init_list sinks) - { - return create(logger_name, sinks.begin(), sinks.end()); - } - - std::shared_ptr create(const std::string& logger_name, sink_ptr sink) - { - return create(logger_name, { sink }); - } - - - void formatter(formatter_ptr f) - { - std::lock_guard lock(_mutex); - _formatter = f; - for (auto& l : _loggers) - l.second->set_formatter(_formatter); - } - - void set_pattern(const std::string& pattern) - { - std::lock_guard lock(_mutex); - _formatter = std::make_shared(pattern); - for (auto& l : _loggers) - l.second->set_formatter(_formatter); - } - - void set_level(level::level_enum log_level) - { - std::lock_guard lock(_mutex); - for (auto& l : _loggers) - l.second->set_level(log_level); - _level = log_level; - } - - void set_error_handler(log_err_handler handler) - { - for (auto& l : _loggers) - l.second->set_error_handler(handler); - _err_handler = handler; - } - - void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) - { - std::lock_guard lock(_mutex); - _async_mode = true; - _async_q_size = q_size; - _overflow_policy = overflow_policy; - _worker_warmup_cb = worker_warmup_cb; - _flush_interval_ms = flush_interval_ms; - _worker_teardown_cb = worker_teardown_cb; - } - - void set_sync_mode() - { - std::lock_guard lock(_mutex); - _async_mode = false; - } - - static registry_t& instance() - { - static registry_t s_instance; - return s_instance; - } - -private: - registry_t() {} - registry_t(const registry_t&) = delete; - registry_t& operator=(const registry_t&) = delete; - - void throw_if_exists(const std::string &logger_name) - { - if (_loggers.find(logger_name) != _loggers.end()) - throw spdlog_ex("logger with name '" + logger_name + "' already exists"); - } - Mutex _mutex; - std::unordered_map > _loggers; - formatter_ptr _formatter; - level::level_enum _level = level::info; - log_err_handler _err_handler; - bool _async_mode = false; - size_t _async_q_size = 0; - async_overflow_policy _overflow_policy = async_overflow_policy::block_retry; - std::function _worker_warmup_cb = nullptr; - std::chrono::milliseconds _flush_interval_ms; - std::function _worker_teardown_cb = nullptr; -}; -#ifdef SPDLOG_NO_REGISTRY_MUTEX -typedef registry_t registry; -#else -typedef registry_t registry; -#endif -} -} diff --git a/include/cpd/vendor/spdlog/details/spdlog_impl.h b/include/cpd/vendor/spdlog/details/spdlog_impl.h deleted file mode 100644 index bc283c83..00000000 --- a/include/cpd/vendor/spdlog/details/spdlog_impl.h +++ /dev/null @@ -1,182 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// -// Global registry functions -// -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -inline void spdlog::register_logger(std::shared_ptr logger) -{ - return details::registry::instance().register_logger(logger); -} - -inline std::shared_ptr spdlog::get(const std::string& name) -{ - return details::registry::instance().get(name); -} - -inline void spdlog::drop(const std::string &name) -{ - details::registry::instance().drop(name); -} - -// Create multi/single threaded simple file logger -inline std::shared_ptr spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate) -{ - return create(logger_name, filename, truncate); -} - -inline std::shared_ptr spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate) -{ - return create(logger_name, filename, truncate); -} - -// Create multi/single threaded rotating file logger -inline std::shared_ptr spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files) -{ - return create(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files); -} - -inline std::shared_ptr spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files) -{ - return create(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files); -} - -// Create file logger which creates new file at midnight): -inline std::shared_ptr spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute) -{ - return create(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute); -} - -inline std::shared_ptr spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute) -{ - return create(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute); -} - -// Create stdout/stderr loggers (with optinal color support) -inline std::shared_ptr create_console_logger(const std::string& logger_name, spdlog::sink_ptr sink, bool color) -{ - if (color) //use color wrapper sink - sink = std::make_shared(sink); - return spdlog::details::registry::instance().create(logger_name, sink); -} - -inline std::shared_ptr spdlog::stdout_logger_mt(const std::string& logger_name, bool color) -{ - return create_console_logger(logger_name, sinks::stdout_sink_mt::instance(), color); -} - -inline std::shared_ptr spdlog::stdout_logger_st(const std::string& logger_name, bool color) -{ - return create_console_logger(logger_name, sinks::stdout_sink_st::instance(), color); -} - -inline std::shared_ptr spdlog::stderr_logger_mt(const std::string& logger_name, bool color) -{ - return create_console_logger(logger_name, sinks::stderr_sink_mt::instance(), color); -} - -inline std::shared_ptr spdlog::stderr_logger_st(const std::string& logger_name, bool color) -{ - return create_console_logger(logger_name, sinks::stderr_sink_st::instance(), color); -} - -#ifdef SPDLOG_ENABLE_SYSLOG -// Create syslog logger -inline std::shared_ptr spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option) -{ - return create(logger_name, syslog_ident, syslog_option); -} -#endif - -#if defined(__ANDROID__) -inline std::shared_ptr spdlog::android_logger(const std::string& logger_name, const std::string& tag) -{ - return create(logger_name, tag); -} -#endif - -// Create and register a logger a single sink -inline std::shared_ptr spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink) -{ - return details::registry::instance().create(logger_name, sink); -} - -//Create logger with multiple sinks - -inline std::shared_ptr spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks) -{ - return details::registry::instance().create(logger_name, sinks); -} - - -template -inline std::shared_ptr spdlog::create(const std::string& logger_name, Args... args) -{ - sink_ptr sink = std::make_shared(args...); - return details::registry::instance().create(logger_name, { sink }); -} - - -template -inline std::shared_ptr spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end) -{ - return details::registry::instance().create(logger_name, sinks_begin, sinks_end); -} - -inline void spdlog::set_formatter(spdlog::formatter_ptr f) -{ - details::registry::instance().formatter(f); -} - -inline void spdlog::set_pattern(const std::string& format_string) -{ - return details::registry::instance().set_pattern(format_string); -} - -inline void spdlog::set_level(level::level_enum log_level) -{ - return details::registry::instance().set_level(log_level); -} - -inline void spdlog::set_error_handler(log_err_handler handler) -{ - return details::registry::instance().set_error_handler(handler); -} - - -inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) -{ - details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb); -} - -inline void spdlog::set_sync_mode() -{ - details::registry::instance().set_sync_mode(); -} - -inline void spdlog::apply_all(std::function)> fun) -{ - details::registry::instance().apply_all(fun); -} - -inline void spdlog::drop_all() -{ - details::registry::instance().drop_all(); -} diff --git a/include/cpd/vendor/spdlog/fmt/bundled/format.cc b/include/cpd/vendor/spdlog/fmt/bundled/format.cc deleted file mode 100644 index 0f7e0aa2..00000000 --- a/include/cpd/vendor/spdlog/fmt/bundled/format.cc +++ /dev/null @@ -1,557 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// Commented out by spdlog to use header only -// #include "fmt/format.h" -// #include "fmt/printf.h" - -#include - -#include -#include -#include -#include -#include -#include // for std::ptrdiff_t - -#if defined(_WIN32) && defined(__MINGW32__) -# include -#endif - -#if FMT_USE_WINDOWS_H -# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) -# include -# else -# define NOMINMAX -# include -# undef NOMINMAX -# endif -#endif - -using fmt::internal::Arg; - -#if FMT_EXCEPTIONS -# define FMT_TRY try -# define FMT_CATCH(x) catch (x) -#else -# define FMT_TRY if (true) -# define FMT_CATCH(x) if (false) -#endif - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4702) // unreachable code -// Disable deprecation warning for strerror. The latter is not called but -// MSVC fails to detect it. -# pragma warning(disable: 4996) -#endif - -// Dummy implementations of strerror_r and strerror_s called if corresponding -// system functions are not available. -static inline fmt::internal::Null<> strerror_r(int, char *, ...) { - return fmt::internal::Null<>(); -} -static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { - return fmt::internal::Null<>(); -} - -namespace fmt { - -FMT_FUNC internal::RuntimeError::~RuntimeError() throw() {} -FMT_FUNC FormatError::~FormatError() throw() {} -FMT_FUNC SystemError::~SystemError() throw() {} - -namespace { - -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - -#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) -# define FMT_SWPRINTF snwprintf -#else -# define FMT_SWPRINTF swprintf -#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) - -const char RESET_COLOR[] = "\x1b[0m"; - -typedef void (*FormatFunc)(Writer &, int, StringRef); - -// Portable thread-safe version of strerror. -// Sets buffer to point to a string describing the error code. -// This can be either a pointer to a string stored in buffer, -// or a pointer to some static immutable string. -// Returns one of the following values: -// 0 - success -// ERANGE - buffer is not large enough to store the error message -// other - failure -// Buffer should be at least of size 1. -int safe_strerror( - int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { - FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); - - class StrError { - private: - int error_code_; - char *&buffer_; - std::size_t buffer_size_; - - // A noop assignment operator to avoid bogus warnings. - void operator=(const StrError &) {} - - // Handle the result of XSI-compliant version of strerror_r. - int handle(int result) { - // glibc versions before 2.13 return result in errno. - return result == -1 ? errno : result; - } - - // Handle the result of GNU-specific version of strerror_r. - int handle(char *message) { - // If the buffer is full then the message is probably truncated. - if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) - return ERANGE; - buffer_ = message; - return 0; - } - - // Handle the case when strerror_r is not available. - int handle(internal::Null<>) { - return fallback(strerror_s(buffer_, buffer_size_, error_code_)); - } - - // Fallback to strerror_s when strerror_r is not available. - int fallback(int result) { - // If the buffer is full then the message is probably truncated. - return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? - ERANGE : result; - } - - // Fallback to strerror if strerror_r and strerror_s are not available. - int fallback(internal::Null<>) { - errno = 0; - buffer_ = strerror(error_code_); - return errno; - } - - public: - StrError(int err_code, char *&buf, std::size_t buf_size) - : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} - - int run() { - strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r. - return handle(strerror_r(error_code_, buffer_, buffer_size_)); - } - }; - return StrError(error_code, buffer, buffer_size).run(); -} - -void format_error_code(Writer &out, int error_code, - StringRef message) FMT_NOEXCEPT { - // Report error code making sure that the output fits into - // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential - // bad_alloc. - out.clear(); - static const char SEP[] = ": "; - static const char ERROR_STR[] = "error "; - // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. - std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; - typedef internal::IntTraits::MainType MainType; - MainType abs_value = static_cast(error_code); - if (internal::is_negative(error_code)) { - abs_value = 0 - abs_value; - ++error_code_size; - } - error_code_size += internal::count_digits(abs_value); - if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) - out << message << SEP; - out << ERROR_STR << error_code; - assert(out.size() <= internal::INLINE_BUFFER_SIZE); -} - -void report_error(FormatFunc func, int error_code, - StringRef message) FMT_NOEXCEPT { - MemoryWriter full_message; - func(full_message, error_code, message); - // Use Writer::data instead of Writer::c_str to avoid potential memory - // allocation. - std::fwrite(full_message.data(), full_message.size(), 1, stderr); - std::fputc('\n', stderr); -} -} // namespace - -namespace internal { - -// This method is used to preserve binary compatibility with fmt 3.0. -// It can be removed in 4.0. -FMT_FUNC void format_system_error( - Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { - fmt::format_system_error(out, error_code, message); -} -} // namespace internal - -FMT_FUNC void SystemError::init( - int err_code, CStringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - format_system_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -template -int internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, value) : - FMT_SNPRINTF(buffer, size, format, precision, value); - } - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, width, value) : - FMT_SNPRINTF(buffer, size, format, width, precision, value); -} - -template -int internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SWPRINTF(buffer, size, format, value) : - FMT_SWPRINTF(buffer, size, format, precision, value); - } - return precision < 0 ? - FMT_SWPRINTF(buffer, size, format, width, value) : - FMT_SWPRINTF(buffer, size, format, width, precision, value); -} - -template -const char internal::BasicData::DIGITS[] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - -#define FMT_POWERS_OF_10(factor) \ - factor * 10, \ - factor * 100, \ - factor * 1000, \ - factor * 10000, \ - factor * 100000, \ - factor * 1000000, \ - factor * 10000000, \ - factor * 100000000, \ - factor * 1000000000 - -template -const uint32_t internal::BasicData::POWERS_OF_10_32[] = { - 0, FMT_POWERS_OF_10(1) -}; - -template -const uint64_t internal::BasicData::POWERS_OF_10_64[] = { - 0, - FMT_POWERS_OF_10(1), - FMT_POWERS_OF_10(ULongLong(1000000000)), - // Multiply several constants instead of using a single long long constant - // to avoid warnings about C++98 not supporting long long. - ULongLong(1000000000) * ULongLong(1000000000) * 10 -}; - -FMT_FUNC void internal::report_unknown_type(char code, const char *type) { - (void)type; - if (std::isprint(static_cast(code))) { - FMT_THROW(FormatError( - format("unknown format code '{}' for {}", code, type))); - } - FMT_THROW(FormatError( - format("unknown format code '\\x{:02x}' for {}", - static_cast(code), type))); -} - -#if FMT_USE_WINDOWS_H - -FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) { - static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; - if (s.size() > INT_MAX) - FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); - int s_size = static_cast(s.size()); - int length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0); - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); - buffer_.resize(length + 1); - length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); - buffer_[length] = 0; -} - -FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) { - if (int error_code = convert(s)) { - FMT_THROW(WindowsError(error_code, - "cannot convert string from UTF-16 to UTF-8")); - } -} - -FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) { - if (s.size() > INT_MAX) - return ERROR_INVALID_PARAMETER; - int s_size = static_cast(s.size()); - int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0); - if (length == 0) - return GetLastError(); - buffer_.resize(length + 1); - length = WideCharToMultiByte( - CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0); - if (length == 0) - return GetLastError(); - buffer_[length] = 0; - return 0; -} - -FMT_FUNC void WindowsError::init( - int err_code, CStringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_windows_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -FMT_FUNC void internal::format_windows_error( - Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { - FMT_TRY { - MemoryBuffer buffer; - buffer.resize(INLINE_BUFFER_SIZE); - for (;;) { - wchar_t *system_message = &buffer[0]; - int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - 0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - system_message, static_cast(buffer.size()), 0); - if (result != 0) { - UTF16ToUTF8 utf8_message; - if (utf8_message.convert(system_message) == ERROR_SUCCESS) { - out << message << ": " << utf8_message; - return; - } - break; - } - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - break; // Can't get error message, report error code instead. - buffer.resize(buffer.size() * 2); - } - } FMT_CATCH(...) {} - fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. -} - -#endif // FMT_USE_WINDOWS_H - -FMT_FUNC void format_system_error( - Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { - FMT_TRY { - internal::MemoryBuffer buffer; - buffer.resize(internal::INLINE_BUFFER_SIZE); - for (;;) { - char *system_message = &buffer[0]; - int result = safe_strerror(error_code, system_message, buffer.size()); - if (result == 0) { - out << message << ": " << system_message; - return; - } - if (result != ERANGE) - break; // Can't get error message, report error code instead. - buffer.resize(buffer.size() * 2); - } - } FMT_CATCH(...) {} - fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. -} - -template -void internal::ArgMap::init(const ArgList &args) { - if (!map_.empty()) - return; - typedef internal::NamedArg NamedArg; - const NamedArg *named_arg = 0; - bool use_values = - args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; - if (use_values) { - for (unsigned i = 0;/*nothing*/; ++i) { - internal::Arg::Type arg_type = args.type(i); - switch (arg_type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast(args.values_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); - break; - default: - /*nothing*/; - } - } - return; - } - for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { - internal::Arg::Type arg_type = args.type(i); - if (arg_type == internal::Arg::NAMED_ARG) { - named_arg = static_cast(args.args_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); - } - } - for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { - switch (args.args_[i].type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast(args.args_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); - break; - default: - /*nothing*/; - } - } -} - -template -void internal::FixedBuffer::grow(std::size_t) { - FMT_THROW(std::runtime_error("buffer overflow")); -} - -FMT_FUNC Arg internal::FormatterBase::do_get_arg( - unsigned arg_index, const char *&error) { - Arg arg = args_[arg_index]; - switch (arg.type) { - case Arg::NONE: - error = "argument index out of range"; - break; - case Arg::NAMED_ARG: - arg = *static_cast(arg.pointer); - break; - default: - /*nothing*/; - } - return arg; -} - -FMT_FUNC void report_system_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - // 'fmt::' is for bcc32. - report_error(format_system_error, error_code, message); -} - -#if FMT_USE_WINDOWS_H -FMT_FUNC void report_windows_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - // 'fmt::' is for bcc32. - report_error(internal::format_windows_error, error_code, message); -} -#endif - -FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - std::fwrite(w.data(), 1, w.size(), f); -} - -FMT_FUNC void print(CStringRef format_str, ArgList args) { - print(stdout, format_str, args); -} - -FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) { - char escape[] = "\x1b[30m"; - escape[3] = static_cast('0' + c); - std::fputs(escape, stdout); - print(format, args); - std::fputs(RESET_COLOR, stdout); -} - -template -void printf(BasicWriter &w, BasicCStringRef format, ArgList args); - -FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) { - MemoryWriter w; - printf(w, format, args); - std::size_t size = w.size(); - return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); -} - -#ifndef FMT_HEADER_ONLY - -template struct internal::BasicData; - -// Explicit instantiations for char. - -template void internal::FixedBuffer::grow(std::size_t); - -template void internal::ArgMap::init(const ArgList &args); - -template void PrintfFormatter::format(CStringRef format); - -template int internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, double value); - -template int internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, long double value); - -// Explicit instantiations for wchar_t. - -template void internal::FixedBuffer::grow(std::size_t); - -template void internal::ArgMap::init(const ArgList &args); - -template void PrintfFormatter::format(WCStringRef format); - -template int internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, double value); - -template int internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, long double value); - -#endif // FMT_HEADER_ONLY - -} // namespace fmt - -#ifdef _MSC_VER -# pragma warning(pop) -#endif diff --git a/include/cpd/vendor/spdlog/fmt/bundled/format.h b/include/cpd/vendor/spdlog/fmt/bundled/format.h deleted file mode 100644 index 294a686f..00000000 --- a/include/cpd/vendor/spdlog/fmt/bundled/format.h +++ /dev/null @@ -1,4455 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FMT_FORMAT_H_ -#define FMT_FORMAT_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _SECURE_SCL -# define FMT_SECURE_SCL _SECURE_SCL -#else -# define FMT_SECURE_SCL 0 -#endif - -#if FMT_SECURE_SCL -# include -#endif - -#ifdef _MSC_VER -# define FMT_MSC_VER _MSC_VER -#else -# define FMT_MSC_VER 0 -#endif - -#if FMT_MSC_VER && FMT_MSC_VER <= 1500 -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -typedef __int64 intmax_t; -#else -#include -#endif - -#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) -# ifdef FMT_EXPORT -# define FMT_API __declspec(dllexport) -# elif defined(FMT_SHARED) -# define FMT_API __declspec(dllimport) -# endif -#endif -#ifndef FMT_API -# define FMT_API -#endif - -#ifdef __GNUC__ -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FMT_GCC_EXTENSION __extension__ -# if FMT_GCC_VERSION >= 406 -# pragma GCC diagnostic push -// Disable the warning about "long long" which is sometimes reported even -// when using __extension__. -# pragma GCC diagnostic ignored "-Wlong-long" -// Disable the warning about declaration shadowing because it affects too -// many valid cases. -# pragma GCC diagnostic ignored "-Wshadow" -// Disable the warning about implicit conversions that may change the sign of -// an integer; silencing it otherwise would require many explicit casts. -# pragma GCC diagnostic ignored "-Wsign-conversion" -# endif -# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ -# define FMT_HAS_GXX_CXX11 1 -# endif -#else -# define FMT_GCC_EXTENSION -#endif - -#if defined(__INTEL_COMPILER) -# define FMT_ICC_VERSION __INTEL_COMPILER -#elif defined(__ICL) -# define FMT_ICC_VERSION __ICL -#endif - -#if defined(__clang__) && !defined(FMT_ICC_VERSION) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -# pragma clang diagnostic ignored "-Wpadded" -#endif - -#ifdef __GNUC_LIBSTD__ -# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) -#endif - -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) -#else -# define FMT_HAS_FEATURE(x) 0 -#endif - -#ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define FMT_HAS_BUILTIN(x) 0 -#endif - -#ifdef __has_cpp_attribute -# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -#else -# define FMT_HAS_CPP_ATTRIBUTE(x) 0 -#endif - -#ifndef FMT_USE_VARIADIC_TEMPLATES -// Variadic templates are available in GCC since version 4.4 -// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ -// since version 2013. -# define FMT_USE_VARIADIC_TEMPLATES \ - (FMT_HAS_FEATURE(cxx_variadic_templates) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800) -#endif - -#ifndef FMT_USE_RVALUE_REFERENCES -// Don't use rvalue references when compiling with clang and an old libstdc++ -// as the latter doesn't provide std::move. -# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 -# define FMT_USE_RVALUE_REFERENCES 0 -# else -# define FMT_USE_RVALUE_REFERENCES \ - (FMT_HAS_FEATURE(cxx_rvalue_references) || \ - (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600) -# endif -#endif - -#if FMT_USE_RVALUE_REFERENCES -# include // for std::move -#endif - -// Check if exceptions are disabled. -#if defined(__GNUC__) && !defined(__EXCEPTIONS) -# define FMT_EXCEPTIONS 0 -#endif -#if FMT_MSC_VER && !_HAS_EXCEPTIONS -# define FMT_EXCEPTIONS 0 -#endif -#ifndef FMT_EXCEPTIONS -# define FMT_EXCEPTIONS 1 -#endif - -#ifndef FMT_THROW -# if FMT_EXCEPTIONS -# define FMT_THROW(x) throw x -# else -# define FMT_THROW(x) assert(false) -# endif -#endif - -// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). -#ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 -#endif - -#ifndef FMT_NOEXCEPT -# if FMT_EXCEPTIONS -# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ - FMT_MSC_VER >= 1900 -# define FMT_NOEXCEPT noexcept -# else -# define FMT_NOEXCEPT throw() -# endif -# else -# define FMT_NOEXCEPT -# endif -#endif - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for a class -#ifndef FMT_USE_DELETED_FUNCTIONS -# define FMT_USE_DELETED_FUNCTIONS 0 -#endif - -#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800 -# define FMT_DELETED_OR_UNDEFINED = delete -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - TypeName& operator=(const TypeName&) = delete -#else -# define FMT_DELETED_OR_UNDEFINED -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - TypeName& operator=(const TypeName&) -#endif - -#ifndef FMT_USE_USER_DEFINED_LITERALS -// All compilers which support UDLs also support variadic templates. This -// makes the fmt::literals implementation easier. However, an explicit check -// for variadic templates is added here just in case. -// For Intel's compiler both it and the system gcc/msc must support UDLs. -# define FMT_USE_USER_DEFINED_LITERALS \ - FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \ - (FMT_HAS_FEATURE(cxx_user_literals) || \ - (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \ - (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) -#endif - -#ifndef FMT_ASSERT -# define FMT_ASSERT(condition, message) assert((condition) && message) -#endif - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -#endif - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -#endif - -// Some compilers masquerade as both MSVC and GCC-likes or -// otherwise support __builtin_clz and __builtin_clzll, so -// only define FMT_BUILTIN_CLZ using the MSVC intrinsics -// if the clz and clzll builtins are not available. -#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) -# include // _BitScanReverse, _BitScanReverse64 - -namespace fmt -{ -namespace internal -{ -# pragma intrinsic(_BitScanReverse) -inline uint32_t clz(uint32_t x) -{ - unsigned long r = 0; - _BitScanReverse(&r, x); - - assert(x != 0); - // Static analysis complains about using uninitialized data - // "r", but the only way that can happen is if "x" is 0, - // which the callers guarantee to not happen. -# pragma warning(suppress: 6102) - return 31 - r; -} -# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) - -# ifdef _WIN64 -# pragma intrinsic(_BitScanReverse64) -# endif - -inline uint32_t clzll(uint64_t x) -{ - unsigned long r = 0; -# ifdef _WIN64 - _BitScanReverse64(&r, x); -# else - // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) - return 63 - (r + 32); - - // Scan the low 32 bits. - _BitScanReverse(&r, static_cast(x)); -# endif - - assert(x != 0); - // Static analysis complains about using uninitialized data - // "r", but the only way that can happen is if "x" is 0, - // which the callers guarantee to not happen. -# pragma warning(suppress: 6102) - return 63 - r; -} -# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) -} -} -#endif - -namespace fmt -{ -namespace internal -{ -struct DummyInt -{ - int data[2]; - operator int() const - { - return 0; - } -}; -typedef std::numeric_limits FPUtil; - -// Dummy implementations of system functions such as signbit and ecvt called -// if the latter are not available. -inline DummyInt signbit(...) -{ - return DummyInt(); -} -inline DummyInt _ecvt_s(...) -{ - return DummyInt(); -} -inline DummyInt isinf(...) -{ - return DummyInt(); -} -inline DummyInt _finite(...) -{ - return DummyInt(); -} -inline DummyInt isnan(...) -{ - return DummyInt(); -} -inline DummyInt _isnan(...) -{ - return DummyInt(); -} - -// A helper function to suppress bogus "conditional expression is constant" -// warnings. -template -inline T const_check(T value) -{ - return value; -} -} -} // namespace fmt - -namespace std -{ -// Standard permits specialization of std::numeric_limits. This specialization -// is used to resolve ambiguity between isinf and std::isinf in glibc: -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891 -// and the same for isnan and signbit. -template <> -class numeric_limits : - public std::numeric_limits -{ -public: - // Portable version of isinf. - template - static bool isinfinity(T x) - { - using namespace fmt::internal; - // The resolution "priority" is: - // isinf macro > std::isinf > ::isinf > fmt::internal::isinf - if (const_check(sizeof(isinf(x)) == sizeof(bool) || - sizeof(isinf(x)) == sizeof(int))) - { - return isinf(x) != 0; - } - return !_finite(static_cast(x)); - } - - // Portable version of isnan. - template - static bool isnotanumber(T x) - { - using namespace fmt::internal; - if (const_check(sizeof(isnan(x)) == sizeof(bool) || - sizeof(isnan(x)) == sizeof(int))) - { - return isnan(x) != 0; - } - return _isnan(static_cast(x)) != 0; - } - - // Portable version of signbit. - static bool isnegative(double x) - { - using namespace fmt::internal; - if (const_check(sizeof(signbit(x)) == sizeof(int))) - return signbit(x) != 0; - if (x < 0) return true; - if (!isnotanumber(x)) return false; - int dec = 0, sign = 0; - char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. - _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign); - return sign != 0; - } -}; -} // namespace std - -namespace fmt -{ - -// Fix the warning about long long on older versions of GCC -// that don't support the diagnostic pragma. -FMT_GCC_EXTENSION typedef long long LongLong; -FMT_GCC_EXTENSION typedef unsigned long long ULongLong; - -#if FMT_USE_RVALUE_REFERENCES -using std::move; -#endif - -template -class BasicWriter; - -typedef BasicWriter Writer; -typedef BasicWriter WWriter; - -template -class ArgFormatter; - -template -class BasicPrintfArgFormatter; - -template > -class BasicFormatter; - -/** - \rst - A string reference. It can be constructed from a C string or ``std::string``. - - You can use one of the following typedefs for common character types: - - +------------+-------------------------+ - | Type | Definition | - +============+=========================+ - | StringRef | BasicStringRef | - +------------+-------------------------+ - | WStringRef | BasicStringRef | - +------------+-------------------------+ - - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: - - template - std::string format(StringRef format_str, const Args & ... args); - - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class BasicStringRef -{ -private: - const Char *data_; - std::size_t size_; - -public: - /** Constructs a string reference object from a C string and a size. */ - BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} - - /** - \rst - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - \endrst - */ - BasicStringRef(const Char *s) - : data_(s), size_(std::char_traits::length(s)) {} - - /** - \rst - Constructs a string reference from an ``std::string`` object. - \endrst - */ - BasicStringRef(const std::basic_string &s) - : data_(s.c_str()), size_(s.size()) {} - - /** - \rst - Converts a string reference to an ``std::string`` object. - \endrst - */ - std::basic_string to_string() const - { - return std::basic_string(data_, size_); - } - - /** Returns a pointer to the string data. */ - const Char *data() const - { - return data_; - } - - /** Returns the string size. */ - std::size_t size() const - { - return size_; - } - - // Lexicographically compare this string reference to other. - int compare(BasicStringRef other) const - { - std::size_t size = size_ < other.size_ ? size_ : other.size_; - int result = std::char_traits::compare(data_, other.data_, size); - if (result == 0) - result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); - return result; - } - - friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) - { - return lhs.compare(rhs) == 0; - } - friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) - { - return lhs.compare(rhs) != 0; - } - friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) - { - return lhs.compare(rhs) < 0; - } - friend bool operator<=(BasicStringRef lhs, BasicStringRef rhs) - { - return lhs.compare(rhs) <= 0; - } - friend bool operator>(BasicStringRef lhs, BasicStringRef rhs) - { - return lhs.compare(rhs) > 0; - } - friend bool operator>=(BasicStringRef lhs, BasicStringRef rhs) - { - return lhs.compare(rhs) >= 0; - } -}; - -typedef BasicStringRef StringRef; -typedef BasicStringRef WStringRef; - -/** - \rst - A reference to a null terminated string. It can be constructed from a C - string or ``std::string``. - - You can use one of the following typedefs for common character types: - - +-------------+--------------------------+ - | Type | Definition | - +=============+==========================+ - | CStringRef | BasicCStringRef | - +-------------+--------------------------+ - | WCStringRef | BasicCStringRef | - +-------------+--------------------------+ - - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: - - template - std::string format(CStringRef format_str, const Args & ... args); - - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class BasicCStringRef -{ -private: - const Char *data_; - -public: - /** Constructs a string reference object from a C string. */ - BasicCStringRef(const Char *s) : data_(s) {} - - /** - \rst - Constructs a string reference from an ``std::string`` object. - \endrst - */ - BasicCStringRef(const std::basic_string &s) : data_(s.c_str()) {} - - /** Returns the pointer to a C string. */ - const Char *c_str() const - { - return data_; - } -}; - -typedef BasicCStringRef CStringRef; -typedef BasicCStringRef WCStringRef; - -/** A formatting error such as invalid format string. */ -class FormatError : public std::runtime_error -{ -public: - explicit FormatError(CStringRef message) - : std::runtime_error(message.c_str()) {} - ~FormatError() throw(); -}; - -namespace internal -{ - -// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. -template -struct MakeUnsigned -{ - typedef T Type; -}; - -#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ - template <> \ - struct MakeUnsigned { typedef U Type; } - -FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); -FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); -FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); -FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); - -// Casts nonnegative integer to unsigned. -template -inline typename MakeUnsigned::Type to_unsigned(Int value) -{ - FMT_ASSERT(value >= 0, "negative value"); - return static_cast::Type>(value); -} - -// The number of characters to store in the MemoryBuffer object itself -// to avoid dynamic memory allocation. -enum { INLINE_BUFFER_SIZE = 500 }; - -#if FMT_SECURE_SCL -// Use checked iterator to avoid warnings on MSVC. -template -inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) -{ - return stdext::checked_array_iterator(ptr, size); -} -#else -template -inline T *make_ptr(T *ptr, std::size_t) -{ - return ptr; -} -#endif -} // namespace internal - -/** - \rst - A buffer supporting a subset of ``std::vector``'s operations. - \endrst - */ -template -class Buffer -{ -private: - FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); - -protected: - T *ptr_; - std::size_t size_; - std::size_t capacity_; - - Buffer(T *ptr = 0, std::size_t capacity = 0) - : ptr_(ptr), size_(0), capacity_(capacity) {} - - /** - \rst - Increases the buffer capacity to hold at least *size* elements updating - ``ptr_`` and ``capacity_``. - \endrst - */ - virtual void grow(std::size_t size) = 0; - -public: - virtual ~Buffer() {} - - /** Returns the size of this buffer. */ - std::size_t size() const - { - return size_; - } - - /** Returns the capacity of this buffer. */ - std::size_t capacity() const - { - return capacity_; - } - - /** - Resizes the buffer. If T is a POD type new elements may not be initialized. - */ - void resize(std::size_t new_size) - { - if (new_size > capacity_) - grow(new_size); - size_ = new_size; - } - - /** - \rst - Reserves space to store at least *capacity* elements. - \endrst - */ - void reserve(std::size_t capacity) - { - if (capacity > capacity_) - grow(capacity); - } - - void clear() FMT_NOEXCEPT { size_ = 0; } - - void push_back(const T &value) - { - if (size_ == capacity_) - grow(size_ + 1); - ptr_[size_++] = value; - } - - /** Appends data to the end of the buffer. */ - template - void append(const U *begin, const U *end); - - T &operator[](std::size_t index) - { - return ptr_[index]; - } - const T &operator[](std::size_t index) const - { - return ptr_[index]; - } -}; - -template -template -void Buffer::append(const U *begin, const U *end) -{ - std::size_t new_size = size_ + internal::to_unsigned(end - begin); - if (new_size > capacity_) - grow(new_size); - std::uninitialized_copy(begin, end, - internal::make_ptr(ptr_, capacity_) + size_); - size_ = new_size; -} - -namespace internal -{ - -// A memory buffer for trivially copyable/constructible types with the first -// SIZE elements stored in the object itself. -template > -class MemoryBuffer : private Allocator, public Buffer -{ -private: - T data_[SIZE]; - - // Deallocate memory allocated by the buffer. - void deallocate() - { - if (this->ptr_ != data_) Allocator::deallocate(this->ptr_, this->capacity_); - } - -protected: - void grow(std::size_t size); - -public: - explicit MemoryBuffer(const Allocator &alloc = Allocator()) - : Allocator(alloc), Buffer(data_, SIZE) {} - ~MemoryBuffer() - { - deallocate(); - } - -#if FMT_USE_RVALUE_REFERENCES -private: - // Move data from other to this buffer. - void move(MemoryBuffer &other) - { - Allocator &this_alloc = *this, &other_alloc = other; - this_alloc = std::move(other_alloc); - this->size_ = other.size_; - this->capacity_ = other.capacity_; - if (other.ptr_ == other.data_) - { - this->ptr_ = data_; - std::uninitialized_copy(other.data_, other.data_ + this->size_, - make_ptr(data_, this->capacity_)); - } - else - { - this->ptr_ = other.ptr_; - // Set pointer to the inline array so that delete is not called - // when deallocating. - other.ptr_ = other.data_; - } - } - -public: - MemoryBuffer(MemoryBuffer &&other) - { - move(other); - } - - MemoryBuffer &operator=(MemoryBuffer &&other) - { - assert(this != &other); - deallocate(); - move(other); - return *this; - } -#endif - - // Returns a copy of the allocator associated with this buffer. - Allocator get_allocator() const - { - return *this; - } -}; - -template -void MemoryBuffer::grow(std::size_t size) -{ - std::size_t new_capacity = this->capacity_ + this->capacity_ / 2; - if (size > new_capacity) - new_capacity = size; - T *new_ptr = this->allocate(new_capacity); - // The following code doesn't throw, so the raw pointer above doesn't leak. - std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_, - make_ptr(new_ptr, new_capacity)); - std::size_t old_capacity = this->capacity_; - T *old_ptr = this->ptr_; - this->capacity_ = new_capacity; - this->ptr_ = new_ptr; - // deallocate may throw (at least in principle), but it doesn't matter since - // the buffer already uses the new storage and will deallocate it in case - // of exception. - if (old_ptr != data_) - Allocator::deallocate(old_ptr, old_capacity); -} - -// A fixed-size buffer. -template -class FixedBuffer : public fmt::Buffer -{ -public: - FixedBuffer(Char *array, std::size_t size) : fmt::Buffer(array, size) {} - -protected: - FMT_API void grow(std::size_t size); -}; - -template -class BasicCharTraits -{ -public: -#if FMT_SECURE_SCL - typedef stdext::checked_array_iterator CharPtr; -#else - typedef Char *CharPtr; -#endif - static Char cast(int value) - { - return static_cast(value); - } -}; - -template -class CharTraits; - -template <> -class CharTraits : public BasicCharTraits -{ -private: - // Conversion from wchar_t to char is not allowed. - static char convert(wchar_t); - -public: - static char convert(char value) - { - return value; - } - - // Formats a floating-point number. - template - FMT_API static int format_float(char *buffer, std::size_t size, - const char *format, unsigned width, int precision, T value); -}; - -template <> -class CharTraits : public BasicCharTraits -{ -public: - static wchar_t convert(char value) - { - return value; - } - static wchar_t convert(wchar_t value) - { - return value; - } - - template - FMT_API static int format_float(wchar_t *buffer, std::size_t size, - const wchar_t *format, unsigned width, int precision, T value); -}; - -// Checks if a number is negative - used to avoid warnings. -template -struct SignChecker -{ - template - static bool is_negative(T value) - { - return value < 0; - } -}; - -template <> -struct SignChecker -{ - template - static bool is_negative(T) - { - return false; - } -}; - -// Returns true if value is negative, false otherwise. -// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. -template -inline bool is_negative(T value) -{ - return SignChecker::is_signed>::is_negative(value); -} - -// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. -template -struct TypeSelector -{ - typedef uint32_t Type; -}; - -template <> -struct TypeSelector -{ - typedef uint64_t Type; -}; - -template -struct IntTraits -{ - // Smallest of uint32_t and uint64_t that is large enough to represent - // all values of T. - typedef typename - TypeSelector::digits <= 32>::Type MainType; -}; - -FMT_API void report_unknown_type(char code, const char *type); - -// Static data is placed in this class template to allow header-only -// configuration. -template -struct FMT_API BasicData -{ - static const uint32_t POWERS_OF_10_32[]; - static const uint64_t POWERS_OF_10_64[]; - static const char DIGITS[]; -}; - -#ifndef FMT_USE_EXTERN_TEMPLATES -// Clang doesn't have a feature check for extern templates so we check -// for variadic templates which were introduced in the same version. -# define FMT_USE_EXTERN_TEMPLATES (__clang__ && FMT_USE_VARIADIC_TEMPLATES) -#endif - -#if FMT_USE_EXTERN_TEMPLATES && !defined(FMT_HEADER_ONLY) -extern template struct BasicData; -#endif - -typedef BasicData<> Data; - -#ifdef FMT_BUILTIN_CLZLL -// Returns the number of decimal digits in n. Leading zeros are not counted -// except for n == 0 in which case count_digits returns 1. -inline unsigned count_digits(uint64_t n) -{ - // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 - // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. - int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; - return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1; -} -#else -// Fallback version of count_digits used when __builtin_clz is not available. -inline unsigned count_digits(uint64_t n) -{ - unsigned count = 1; - for (;;) - { - // Integer division is slow so do it for a group of four digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - if (n < 10) return count; - if (n < 100) return count + 1; - if (n < 1000) return count + 2; - if (n < 10000) return count + 3; - n /= 10000u; - count += 4; - } -} -#endif - -#ifdef FMT_BUILTIN_CLZ -// Optional version of count_digits for better performance on 32-bit platforms. -inline unsigned count_digits(uint32_t n) -{ - int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; - return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1; -} -#endif - -// A functor that doesn't add a thousands separator. -struct NoThousandsSep -{ - template - void operator()(Char *) {} -}; - -// A functor that adds a thousands separator. -class ThousandsSep -{ -private: - fmt::StringRef sep_; - - // Index of a decimal digit with the least significant digit having index 0. - unsigned digit_index_; - -public: - explicit ThousandsSep(fmt::StringRef sep) : sep_(sep), digit_index_(0) {} - - template - void operator()(Char *&buffer) - { - if (++digit_index_ % 3 != 0) - return; - buffer -= sep_.size(); - std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(), - internal::make_ptr(buffer, sep_.size())); - } -}; - -// Formats a decimal unsigned integer value writing into buffer. -// thousands_sep is a functor that is called after writing each char to -// add a thousands separator if necessary. -template -inline void format_decimal(Char *buffer, UInt value, unsigned num_digits, - ThousandsSep thousands_sep) -{ - buffer += num_digits; - while (value >= 100) - { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = static_cast((value % 100) * 2); - value /= 100; - *--buffer = Data::DIGITS[index + 1]; - thousands_sep(buffer); - *--buffer = Data::DIGITS[index]; - thousands_sep(buffer); - } - if (value < 10) - { - *--buffer = static_cast('0' + value); - return; - } - unsigned index = static_cast(value * 2); - *--buffer = Data::DIGITS[index + 1]; - thousands_sep(buffer); - *--buffer = Data::DIGITS[index]; -} - -template -inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) -{ - return format_decimal(buffer, value, num_digits, NoThousandsSep()); -} - -#ifndef _WIN32 -# define FMT_USE_WINDOWS_H 0 -#elif !defined(FMT_USE_WINDOWS_H) -# define FMT_USE_WINDOWS_H 1 -#endif - -// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h. -// All the functionality that relies on it will be disabled too. -#if FMT_USE_WINDOWS_H -// A converter from UTF-8 to UTF-16. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF8ToUTF16 -{ -private: - MemoryBuffer buffer_; - -public: - FMT_API explicit UTF8ToUTF16(StringRef s); - operator WStringRef() const - { - return WStringRef(&buffer_[0], size()); - } - size_t size() const - { - return buffer_.size() - 1; - } - const wchar_t *c_str() const - { - return &buffer_[0]; - } - std::wstring str() const - { - return std::wstring(&buffer_[0], size()); - } -}; - -// A converter from UTF-16 to UTF-8. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF16ToUTF8 -{ -private: - MemoryBuffer buffer_; - -public: - UTF16ToUTF8() {} - FMT_API explicit UTF16ToUTF8(WStringRef s); - operator StringRef() const - { - return StringRef(&buffer_[0], size()); - } - size_t size() const - { - return buffer_.size() - 1; - } - const char *c_str() const - { - return &buffer_[0]; - } - std::string str() const - { - return std::string(&buffer_[0], size()); - } - - // Performs conversion returning a system error code instead of - // throwing exception on conversion error. This method may still throw - // in case of memory allocation error. - FMT_API int convert(WStringRef s); -}; - -FMT_API void format_windows_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; -#endif - -// A formatting argument value. -struct Value -{ - template - struct StringValue - { - const Char *value; - std::size_t size; - }; - - typedef void (*FormatFunc)( - void *formatter, const void *arg, void *format_str_ptr); - - struct CustomValue - { - const void *value; - FormatFunc format; - }; - - union - { - int int_value; - unsigned uint_value; - LongLong long_long_value; - ULongLong ulong_long_value; - double double_value; - long double long_double_value; - const void *pointer; - StringValue string; - StringValue sstring; - StringValue ustring; - StringValue wstring; - CustomValue custom; - }; - - enum Type - { - NONE, NAMED_ARG, - // Integer types should go first, - INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, - // followed by floating-point types. - DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, - CSTRING, STRING, WSTRING, POINTER, CUSTOM - }; -}; - -// A formatting argument. It is a trivially copyable/constructible type to -// allow storage in internal::MemoryBuffer. -struct Arg : Value -{ - Type type; -}; - -template -struct NamedArg; - -template -struct Null {}; - -// A helper class template to enable or disable overloads taking wide -// characters and strings in MakeValue. -template -struct WCharHelper -{ - typedef Null Supported; - typedef T Unsupported; -}; - -template -struct WCharHelper -{ - typedef T Supported; - typedef Null Unsupported; -}; - -typedef char Yes[1]; -typedef char No[2]; - -template -T &get(); - -// These are non-members to workaround an overload resolution bug in bcc32. -Yes &convert(fmt::ULongLong); -No &convert(...); - -template -struct ConvertToIntImpl -{ - enum { value = ENABLE_CONVERSION }; -}; - -template -struct ConvertToIntImpl2 -{ - enum { value = false }; -}; - -template -struct ConvertToIntImpl2 -{ - enum - { - // Don't convert numeric types. - value = ConvertToIntImpl::is_specialized>::value - }; -}; - -template -struct ConvertToInt -{ - enum { enable_conversion = sizeof(convert(get())) == sizeof(Yes) }; - enum { value = ConvertToIntImpl2::value }; -}; - -#define FMT_DISABLE_CONVERSION_TO_INT(Type) \ - template <> \ - struct ConvertToInt { enum { value = 0 }; } - -// Silence warnings about convering float to int. -FMT_DISABLE_CONVERSION_TO_INT(float); -FMT_DISABLE_CONVERSION_TO_INT(double); -FMT_DISABLE_CONVERSION_TO_INT(long double); - -template -struct EnableIf {}; - -template -struct EnableIf -{ - typedef T type; -}; - -template -struct Conditional -{ - typedef T type; -}; - -template -struct Conditional -{ - typedef F type; -}; - -// For bcc32 which doesn't understand ! in template arguments. -template -struct Not -{ - enum { value = 0 }; -}; - -template <> -struct Not -{ - enum { value = 1 }; -}; - -template -struct False -{ - enum { value = 0 }; -}; - -template struct LConvCheck -{ - LConvCheck(int) {} -}; - -// Returns the thousands separator for the current locale. -// We check if ``lconv`` contains ``thousands_sep`` because on Android -// ``lconv`` is stubbed as an empty struct. -template -inline StringRef thousands_sep( - LConv *lc, LConvCheck = 0) -{ - return lc->thousands_sep; -} - -inline fmt::StringRef thousands_sep(...) -{ - return ""; -} - -#define FMT_CONCAT(a, b) a##b - -#if FMT_GCC_VERSION >= 407 -# define FMT_UNUSED __attribute__((unused)) -#else -# define FMT_UNUSED -#endif - -#ifndef FMT_USE_STATIC_ASSERT -# define FMT_USE_STATIC_ASSERT 0 -#endif - -#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \ - (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600 -# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message) -#else -# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b) -# define FMT_STATIC_ASSERT(cond, message) \ - typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED -#endif - -template -void format_arg(Formatter &, const Char *, const T &) -{ - FMT_STATIC_ASSERT(False::value, - "Cannot format argument. To enable the use of ostream " - "operator<< include fmt/ostream.h. Otherwise provide " - "an overload of format_arg."); -} - -// Makes an Arg object from any type. -template -class MakeValue : public Arg -{ -public: - typedef typename Formatter::Char Char; - -private: - // The following two methods are private to disallow formatting of - // arbitrary pointers. If you want to output a pointer cast it to - // "void *" or "const void *". In particular, this forbids formatting - // of "[const] volatile char *" which is printed as bool by iostreams. - // Do not implement! - template - MakeValue(const T *value); - template - MakeValue(T *value); - - // The following methods are private to disallow formatting of wide - // characters and strings into narrow strings as in - // fmt::format("{}", L"test"); - // To fix this, use a wide format string: fmt::format(L"{}", L"test"). -#if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED) - MakeValue(typename WCharHelper::Unsupported); -#endif - MakeValue(typename WCharHelper::Unsupported); - MakeValue(typename WCharHelper::Unsupported); - MakeValue(typename WCharHelper::Unsupported); - MakeValue(typename WCharHelper::Unsupported); - - void set_string(StringRef str) - { - string.value = str.data(); - string.size = str.size(); - } - - void set_string(WStringRef str) - { - wstring.value = str.data(); - wstring.size = str.size(); - } - - // Formats an argument of a custom type, such as a user-defined class. - template - static void format_custom_arg( - void *formatter, const void *arg, void *format_str_ptr) - { - format_arg(*static_cast(formatter), - *static_cast(format_str_ptr), - *static_cast(arg)); - } - -public: - MakeValue() {} - -#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \ - MakeValue(Type value) { field = rhs; } \ - static uint64_t type(Type) { return Arg::TYPE; } - -#define FMT_MAKE_VALUE(Type, field, TYPE) \ - FMT_MAKE_VALUE_(Type, field, TYPE, value) - - FMT_MAKE_VALUE(bool, int_value, BOOL) - FMT_MAKE_VALUE(short, int_value, INT) - FMT_MAKE_VALUE(unsigned short, uint_value, UINT) - FMT_MAKE_VALUE(int, int_value, INT) - FMT_MAKE_VALUE(unsigned, uint_value, UINT) - - MakeValue(long value) - { - // To minimize the number of types we need to deal with, long is - // translated either to int or to long long depending on its size. - if (const_check(sizeof(long) == sizeof(int))) - int_value = static_cast(value); - else - long_long_value = value; - } - static uint64_t type(long) - { - return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; - } - - MakeValue(unsigned long value) - { - if (const_check(sizeof(unsigned long) == sizeof(unsigned))) - uint_value = static_cast(value); - else - ulong_long_value = value; - } - static uint64_t type(unsigned long) - { - return sizeof(unsigned long) == sizeof(unsigned) ? - Arg::UINT : Arg::ULONG_LONG; - } - - FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) - FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) - FMT_MAKE_VALUE(float, double_value, DOUBLE) - FMT_MAKE_VALUE(double, double_value, DOUBLE) - FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) - FMT_MAKE_VALUE(signed char, int_value, INT) - FMT_MAKE_VALUE(unsigned char, uint_value, UINT) - FMT_MAKE_VALUE(char, int_value, CHAR) - -#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) - MakeValue(typename WCharHelper::Supported value) - { - int_value = value; - } - static uint64_t type(wchar_t) - { - return Arg::CHAR; - } -#endif - -#define FMT_MAKE_STR_VALUE(Type, TYPE) \ - MakeValue(Type value) { set_string(value); } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_VALUE(char *, string.value, CSTRING) - FMT_MAKE_VALUE(const char *, string.value, CSTRING) - FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING) - FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) - FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING) - FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) - FMT_MAKE_STR_VALUE(const std::string &, STRING) - FMT_MAKE_STR_VALUE(StringRef, STRING) - FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str()) - -#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ - MakeValue(typename WCharHelper::Supported value) { \ - set_string(value); \ - } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) - FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING) - FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING) - FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING) - - FMT_MAKE_VALUE(void *, pointer, POINTER) - FMT_MAKE_VALUE(const void *, pointer, POINTER) - - template - MakeValue(const T &value, - typename EnableIf::value>::value, int>::type = 0) - { - custom.value = &value; - custom.format = &format_custom_arg; - } - - template - MakeValue(const T &value, - typename EnableIf::value, int>::type = 0) - { - int_value = value; - } - - template - static uint64_t type(const T &) - { - return ConvertToInt::value ? Arg::INT : Arg::CUSTOM; - } - - // Additional template param `Char_` is needed here because make_type always - // uses char. - template - MakeValue(const NamedArg &value) - { - pointer = &value; - } - - template - static uint64_t type(const NamedArg &) - { - return Arg::NAMED_ARG; - } -}; - -template -class MakeArg : public Arg -{ -public: - MakeArg() - { - type = Arg::NONE; - } - - template - MakeArg(const T &value) - : Arg(MakeValue(value)) - { - type = static_cast(MakeValue::type(value)); - } -}; - -template -struct NamedArg : Arg -{ - BasicStringRef name; - - template - NamedArg(BasicStringRef argname, const T &value) - : Arg(MakeArg< BasicFormatter >(value)), name(argname) {} -}; - -class RuntimeError : public std::runtime_error -{ -protected: - RuntimeError() : std::runtime_error("") {} - ~RuntimeError() throw(); -}; - -template -class ArgMap; -} // namespace internal - -/** An argument list. */ -class ArgList -{ -private: - // To reduce compiled code size per formatting function call, types of first - // MAX_PACKED_ARGS arguments are passed in the types_ field. - uint64_t types_; - union - { - // If the number of arguments is less than MAX_PACKED_ARGS, the argument - // values are stored in values_, otherwise they are stored in args_. - // This is done to reduce compiled code size as storing larger objects - // may require more code (at least on x86-64) even if the same amount of - // data is actually copied to stack. It saves ~10% on the bloat test. - const internal::Value *values_; - const internal::Arg *args_; - }; - - internal::Arg::Type type(unsigned index) const - { - unsigned shift = index * 4; - uint64_t mask = 0xf; - return static_cast( - (types_ & (mask << shift)) >> shift); - } - - template - friend class internal::ArgMap; - -public: - // Maximum number of arguments with packed types. - enum { MAX_PACKED_ARGS = 16 }; - - ArgList() : types_(0) {} - - ArgList(ULongLong types, const internal::Value *values) - : types_(types), values_(values) {} - ArgList(ULongLong types, const internal::Arg *args) - : types_(types), args_(args) {} - - /** Returns the argument at specified index. */ - internal::Arg operator[](unsigned index) const - { - using internal::Arg; - Arg arg; - bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE; - if (index < MAX_PACKED_ARGS) - { - Arg::Type arg_type = type(index); - internal::Value &val = arg; - if (arg_type != Arg::NONE) - val = use_values ? values_[index] : args_[index]; - arg.type = arg_type; - return arg; - } - if (use_values) - { - // The index is greater than the number of arguments that can be stored - // in values, so return a "none" argument. - arg.type = Arg::NONE; - return arg; - } - for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) - { - if (args_[i].type == Arg::NONE) - return args_[i]; - } - return args_[index]; - } -}; - -#define FMT_DISPATCH(call) static_cast(this)->call - -/** - \rst - An argument visitor based on the `curiously recurring template pattern - `_. - - To use `~fmt::ArgVisitor` define a subclass that implements some or all of the - visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, - for example, `~fmt::ArgVisitor::visit_int()`. - Pass the subclass as the *Impl* template parameter. Then calling - `~fmt::ArgVisitor::visit` for some argument will dispatch to a visit method - specific to the argument type. For example, if the argument type is - ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass - will be called. If the subclass doesn't contain a method with this signature, - then a corresponding method of `~fmt::ArgVisitor` will be called. - - **Example**:: - - class MyArgVisitor : public fmt::ArgVisitor { - public: - void visit_int(int value) { fmt::print("{}", value); } - void visit_double(double value) { fmt::print("{}", value ); } - }; - \endrst - */ -template -class ArgVisitor -{ -private: - typedef internal::Arg Arg; - -public: - void report_unhandled_arg() {} - - Result visit_unhandled_arg() - { - FMT_DISPATCH(report_unhandled_arg()); - return Result(); - } - - /** Visits an ``int`` argument. **/ - Result visit_int(int value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - - /** Visits a ``long long`` argument. **/ - Result visit_long_long(LongLong value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - - /** Visits an ``unsigned`` argument. **/ - Result visit_uint(unsigned value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - - /** Visits an ``unsigned long long`` argument. **/ - Result visit_ulong_long(ULongLong value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - - /** Visits a ``bool`` argument. **/ - Result visit_bool(bool value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - - /** Visits a ``char`` or ``wchar_t`` argument. **/ - Result visit_char(int value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - - /** Visits an argument of any integral type. **/ - template - Result visit_any_int(T) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - /** Visits a ``double`` argument. **/ - Result visit_double(double value) - { - return FMT_DISPATCH(visit_any_double(value)); - } - - /** Visits a ``long double`` argument. **/ - Result visit_long_double(long double value) - { - return FMT_DISPATCH(visit_any_double(value)); - } - - /** Visits a ``double`` or ``long double`` argument. **/ - template - Result visit_any_double(T) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - /** Visits a null-terminated C string (``const char *``) argument. **/ - Result visit_cstring(const char *) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - /** Visits a string argument. **/ - Result visit_string(Arg::StringValue) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - /** Visits a wide string argument. **/ - Result visit_wstring(Arg::StringValue) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - /** Visits a pointer argument. **/ - Result visit_pointer(const void *) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - /** Visits an argument of a custom (user-defined) type. **/ - Result visit_custom(Arg::CustomValue) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - /** - \rst - Visits an argument dispatching to the appropriate visit method based on - the argument type. For example, if the argument type is ``double`` then - the `~fmt::ArgVisitor::visit_double()` method of the *Impl* class will be - called. - \endrst - */ - Result visit(const Arg &arg) - { - switch (arg.type) - { - case Arg::NONE: - case Arg::NAMED_ARG: - FMT_ASSERT(false, "invalid argument type"); - break; - case Arg::INT: - return FMT_DISPATCH(visit_int(arg.int_value)); - case Arg::UINT: - return FMT_DISPATCH(visit_uint(arg.uint_value)); - case Arg::LONG_LONG: - return FMT_DISPATCH(visit_long_long(arg.long_long_value)); - case Arg::ULONG_LONG: - return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); - case Arg::BOOL: - return FMT_DISPATCH(visit_bool(arg.int_value != 0)); - case Arg::CHAR: - return FMT_DISPATCH(visit_char(arg.int_value)); - case Arg::DOUBLE: - return FMT_DISPATCH(visit_double(arg.double_value)); - case Arg::LONG_DOUBLE: - return FMT_DISPATCH(visit_long_double(arg.long_double_value)); - case Arg::CSTRING: - return FMT_DISPATCH(visit_cstring(arg.string.value)); - case Arg::STRING: - return FMT_DISPATCH(visit_string(arg.string)); - case Arg::WSTRING: - return FMT_DISPATCH(visit_wstring(arg.wstring)); - case Arg::POINTER: - return FMT_DISPATCH(visit_pointer(arg.pointer)); - case Arg::CUSTOM: - return FMT_DISPATCH(visit_custom(arg.custom)); - } - return Result(); - } -}; - -enum Alignment -{ - ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC -}; - -// Flags. -enum -{ - SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, - CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. -}; - -// An empty format specifier. -struct EmptySpec {}; - -// A type specifier. -template -struct TypeSpec : EmptySpec -{ - Alignment align() const - { - return ALIGN_DEFAULT; - } - unsigned width() const - { - return 0; - } - int precision() const - { - return -1; - } - bool flag(unsigned) const - { - return false; - } - char type() const - { - return TYPE; - } - char fill() const - { - return ' '; - } -}; - -// A width specifier. -struct WidthSpec -{ - unsigned width_; - // Fill is always wchar_t and cast to char if necessary to avoid having - // two specialization of WidthSpec and its subclasses. - wchar_t fill_; - - WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} - - unsigned width() const - { - return width_; - } - wchar_t fill() const - { - return fill_; - } -}; - -// An alignment specifier. -struct AlignSpec : WidthSpec -{ - Alignment align_; - - AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) - : WidthSpec(width, fill), align_(align) {} - - Alignment align() const - { - return align_; - } - - int precision() const - { - return -1; - } -}; - -// An alignment and type specifier. -template -struct AlignTypeSpec : AlignSpec -{ - AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} - - bool flag(unsigned) const - { - return false; - } - char type() const - { - return TYPE; - } -}; - -// A full format specifier. -struct FormatSpec : AlignSpec -{ - unsigned flags_; - int precision_; - char type_; - - FormatSpec( - unsigned width = 0, char type = 0, wchar_t fill = ' ') - : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} - - bool flag(unsigned f) const - { - return (flags_ & f) != 0; - } - int precision() const - { - return precision_; - } - char type() const - { - return type_; - } -}; - -// An integer format specifier. -template , typename Char = char> -class IntFormatSpec : public SpecT -{ -private: - T value_; - -public: - IntFormatSpec(T val, const SpecT &spec = SpecT()) - : SpecT(spec), value_(val) {} - - T value() const - { - return value_; - } -}; - -// A string format specifier. -template -class StrFormatSpec : public AlignSpec -{ -private: - const Char *str_; - -public: - template - StrFormatSpec(const Char *str, unsigned width, FillChar fill) - : AlignSpec(width, fill), str_(str) - { - internal::CharTraits::convert(FillChar()); - } - - const Char *str() const - { - return str_; - } -}; - -/** - Returns an integer format specifier to format the value in base 2. - */ -IntFormatSpec > bin(int value); - -/** - Returns an integer format specifier to format the value in base 8. - */ -IntFormatSpec > oct(int value); - -/** - Returns an integer format specifier to format the value in base 16 using - lower-case letters for the digits above 9. - */ -IntFormatSpec > hex(int value); - -/** - Returns an integer formatter format specifier to format in base 16 using - upper-case letters for the digits above 9. - */ -IntFormatSpec > hexu(int value); - -/** - \rst - Returns an integer format specifier to pad the formatted argument with the - fill character to the specified width using the default (right) numeric - alignment. - - **Example**:: - - MemoryWriter out; - out << pad(hex(0xcafe), 8, '0'); - // out.str() == "0000cafe" - - \endrst - */ -template -IntFormatSpec, Char> pad( - int value, unsigned width, Char fill = ' '); - -#define FMT_DEFINE_INT_FORMATTERS(TYPE) \ -inline IntFormatSpec > bin(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'b'>()); \ -} \ - \ -inline IntFormatSpec > oct(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'o'>()); \ -} \ - \ -inline IntFormatSpec > hex(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'x'>()); \ -} \ - \ -inline IntFormatSpec > hexu(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'X'>()); \ -} \ - \ -template \ -inline IntFormatSpec > pad( \ - IntFormatSpec > f, unsigned width) { \ - return IntFormatSpec >( \ - f.value(), AlignTypeSpec(width, ' ')); \ -} \ - \ -/* For compatibility with older compilers we provide two overloads for pad, */ \ -/* one that takes a fill character and one that doesn't. In the future this */ \ -/* can be replaced with one overload making the template argument Char */ \ -/* default to char (C++11). */ \ -template \ -inline IntFormatSpec, Char> pad( \ - IntFormatSpec, Char> f, \ - unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - f.value(), AlignTypeSpec(width, fill)); \ -} \ - \ -inline IntFormatSpec > pad( \ - TYPE value, unsigned width) { \ - return IntFormatSpec >( \ - value, AlignTypeSpec<0>(width, ' ')); \ -} \ - \ -template \ -inline IntFormatSpec, Char> pad( \ - TYPE value, unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - value, AlignTypeSpec<0>(width, fill)); \ -} - -FMT_DEFINE_INT_FORMATTERS(int) -FMT_DEFINE_INT_FORMATTERS(long) -FMT_DEFINE_INT_FORMATTERS(unsigned) -FMT_DEFINE_INT_FORMATTERS(unsigned long) -FMT_DEFINE_INT_FORMATTERS(LongLong) -FMT_DEFINE_INT_FORMATTERS(ULongLong) - -/** - \rst - Returns a string formatter that pads the formatted argument with the fill - character to the specified width using the default (left) string alignment. - - **Example**:: - - std::string s = str(MemoryWriter() << pad("abc", 8)); - // s == "abc " - - \endrst - */ -template -inline StrFormatSpec pad( - const Char *str, unsigned width, Char fill = ' ') -{ - return StrFormatSpec(str, width, fill); -} - -inline StrFormatSpec pad( - const wchar_t *str, unsigned width, char fill = ' ') -{ - return StrFormatSpec(str, width, fill); -} - -namespace internal -{ - -template -class ArgMap -{ -private: - typedef std::vector< - std::pair, internal::Arg> > MapType; - typedef typename MapType::value_type Pair; - - MapType map_; - -public: - FMT_API void init(const ArgList &args); - - const internal::Arg* find(const fmt::BasicStringRef &name) const - { - // The list is unsorted, so just return the first matching name. - for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); - it != end; ++it) - { - if (it->first == name) - return &it->second; - } - return 0; - } -}; - -template -class ArgFormatterBase : public ArgVisitor -{ -private: - BasicWriter &writer_; - FormatSpec &spec_; - - FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase); - - void write_pointer(const void *p) - { - spec_.flags_ = HASH_FLAG; - spec_.type_ = 'x'; - writer_.write_int(reinterpret_cast(p), spec_); - } - -protected: - BasicWriter &writer() - { - return writer_; - } - FormatSpec &spec() - { - return spec_; - } - - void write(bool value) - { - const char *str_value = value ? "true" : "false"; - Arg::StringValue str = { str_value, std::strlen(str_value) }; - writer_.write_str(str, spec_); - } - - void write(const char *value) - { - Arg::StringValue str = {value, value != 0 ? std::strlen(value) : 0}; - writer_.write_str(str, spec_); - } - -public: - ArgFormatterBase(BasicWriter &w, FormatSpec &s) - : writer_(w), spec_(s) {} - - template - void visit_any_int(T value) - { - writer_.write_int(value, spec_); - } - - template - void visit_any_double(T value) - { - writer_.write_double(value, spec_); - } - - void visit_bool(bool value) - { - if (spec_.type_) - return visit_any_int(value); - write(value); - } - - void visit_char(int value) - { - if (spec_.type_ && spec_.type_ != 'c') - { - spec_.flags_ |= CHAR_FLAG; - writer_.write_int(value, spec_); - return; - } - if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) - FMT_THROW(FormatError("invalid format specifier for char")); - typedef typename BasicWriter::CharPtr CharPtr; - Char fill = internal::CharTraits::cast(spec_.fill()); - CharPtr out = CharPtr(); - const unsigned CHAR_WIDTH = 1; - if (spec_.width_ > CHAR_WIDTH) - { - out = writer_.grow_buffer(spec_.width_); - if (spec_.align_ == ALIGN_RIGHT) - { - std::uninitialized_fill_n(out, spec_.width_ - CHAR_WIDTH, fill); - out += spec_.width_ - CHAR_WIDTH; - } - else if (spec_.align_ == ALIGN_CENTER) - { - out = writer_.fill_padding(out, spec_.width_, - internal::const_check(CHAR_WIDTH), fill); - } - else - { - std::uninitialized_fill_n(out + CHAR_WIDTH, - spec_.width_ - CHAR_WIDTH, fill); - } - } - else - { - out = writer_.grow_buffer(CHAR_WIDTH); - } - *out = internal::CharTraits::cast(value); - } - - void visit_cstring(const char *value) - { - if (spec_.type_ == 'p') - return write_pointer(value); - write(value); - } - - void visit_string(Arg::StringValue value) - { - writer_.write_str(value, spec_); - } - - using ArgVisitor::visit_wstring; - - void visit_wstring(Arg::StringValue value) - { - writer_.write_str(value, spec_); - } - - void visit_pointer(const void *value) - { - if (spec_.type_ && spec_.type_ != 'p') - report_unknown_type(spec_.type_, "pointer"); - write_pointer(value); - } -}; - -class FormatterBase -{ -private: - ArgList args_; - int next_arg_index_; - - // Returns the argument with specified index. - FMT_API Arg do_get_arg(unsigned arg_index, const char *&error); - -protected: - const ArgList &args() const - { - return args_; - } - - explicit FormatterBase(const ArgList &args) - { - args_ = args; - next_arg_index_ = 0; - } - - // Returns the next argument. - Arg next_arg(const char *&error) - { - if (next_arg_index_ >= 0) - return do_get_arg(internal::to_unsigned(next_arg_index_++), error); - error = "cannot switch from manual to automatic argument indexing"; - return Arg(); - } - - // Checks if manual indexing is used and returns the argument with - // specified index. - Arg get_arg(unsigned arg_index, const char *&error) - { - return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg(); - } - - bool check_no_auto_index(const char *&error) - { - if (next_arg_index_ > 0) - { - error = "cannot switch from automatic to manual argument indexing"; - return false; - } - next_arg_index_ = -1; - return true; - } - - template - void write(BasicWriter &w, const Char *start, const Char *end) - { - if (start != end) - w << BasicStringRef(start, internal::to_unsigned(end - start)); - } -}; -} // namespace internal - -/** - \rst - An argument formatter based on the `curiously recurring template pattern - `_. - - To use `~fmt::BasicArgFormatter` define a subclass that implements some or - all of the visit methods with the same signatures as the methods in - `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. - Pass the subclass as the *Impl* template parameter. When a formatting - function processes an argument, it will dispatch to a visit method - specific to the argument type. For example, if the argument type is - ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass - will be called. If the subclass doesn't contain a method with this signature, - then a corresponding method of `~fmt::BasicArgFormatter` or its superclass - will be called. - \endrst - */ -template -class BasicArgFormatter : public internal::ArgFormatterBase -{ -private: - BasicFormatter &formatter_; - const Char *format_; - -public: - /** - \rst - Constructs an argument formatter object. - *formatter* is a reference to the main formatter object, *spec* contains - format specifier information for standard argument types, and *fmt* points - to the part of the format string being parsed for custom argument types. - \endrst - */ - BasicArgFormatter(BasicFormatter &formatter, - FormatSpec &spec, const Char *fmt) - : internal::ArgFormatterBase(formatter.writer(), spec), - formatter_(formatter), format_(fmt) {} - - /** Formats an argument of a custom (user-defined) type. */ - void visit_custom(internal::Arg::CustomValue c) - { - c.format(&formatter_, c.value, &format_); - } -}; - -/** The default argument formatter. */ -template -class ArgFormatter : public BasicArgFormatter, Char> -{ -public: - /** Constructs an argument formatter object. */ - ArgFormatter(BasicFormatter &formatter, - FormatSpec &spec, const Char *fmt) - : BasicArgFormatter, Char>(formatter, spec, fmt) {} -}; - -/** This template formats data and writes the output to a writer. */ -template -class BasicFormatter : private internal::FormatterBase -{ -public: - /** The character type for the output. */ - typedef CharType Char; - -private: - BasicWriter &writer_; - internal::ArgMap map_; - - FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); - - using internal::FormatterBase::get_arg; - - // Checks if manual indexing is used and returns the argument with - // specified name. - internal::Arg get_arg(BasicStringRef arg_name, const char *&error); - - // Parses argument index and returns corresponding argument. - internal::Arg parse_arg_index(const Char *&s); - - // Parses argument name and returns corresponding argument. - internal::Arg parse_arg_name(const Char *&s); - -public: - /** - \rst - Constructs a ``BasicFormatter`` object. References to the arguments and - the writer are stored in the formatter object so make sure they have - appropriate lifetimes. - \endrst - */ - BasicFormatter(const ArgList &args, BasicWriter &w) - : internal::FormatterBase(args), writer_(w) {} - - /** Returns a reference to the writer associated with this formatter. */ - BasicWriter &writer() - { - return writer_; - } - - /** Formats stored arguments and writes the output to the writer. */ - void format(BasicCStringRef format_str); - - // Formats a single argument and advances format_str, a format string pointer. - const Char *format(const Char *&format_str, const internal::Arg &arg); -}; - -// Generates a comma-separated list with results of applying f to -// numbers 0..n-1. -# define FMT_GEN(n, f) FMT_GEN##n(f) -# define FMT_GEN1(f) f(0) -# define FMT_GEN2(f) FMT_GEN1(f), f(1) -# define FMT_GEN3(f) FMT_GEN2(f), f(2) -# define FMT_GEN4(f) FMT_GEN3(f), f(3) -# define FMT_GEN5(f) FMT_GEN4(f), f(4) -# define FMT_GEN6(f) FMT_GEN5(f), f(5) -# define FMT_GEN7(f) FMT_GEN6(f), f(6) -# define FMT_GEN8(f) FMT_GEN7(f), f(7) -# define FMT_GEN9(f) FMT_GEN8(f), f(8) -# define FMT_GEN10(f) FMT_GEN9(f), f(9) -# define FMT_GEN11(f) FMT_GEN10(f), f(10) -# define FMT_GEN12(f) FMT_GEN11(f), f(11) -# define FMT_GEN13(f) FMT_GEN12(f), f(12) -# define FMT_GEN14(f) FMT_GEN13(f), f(13) -# define FMT_GEN15(f) FMT_GEN14(f), f(14) - -namespace internal -{ -inline uint64_t make_type() -{ - return 0; -} - -template -inline uint64_t make_type(const T &arg) -{ - return MakeValue< BasicFormatter >::type(arg); -} - -template - struct ArgArray; - -template -struct ArgArray -{ - typedef Value Type[N > 0 ? N : 1]; - -template -static Value make(const T &value) -{ -#ifdef __clang__ - Value result = MakeValue(value); - // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang: - // https://github.com/fmtlib/fmt/issues/276 - (void)result.custom.format; - return result; -#else - return MakeValue(value); -#endif -} - }; - -template -struct ArgArray -{ - typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE - - template - static Arg make(const T &value) - { - return MakeArg(value); - } -}; - -#if FMT_USE_VARIADIC_TEMPLATES -template -inline uint64_t make_type(const Arg &first, const Args & ... tail) -{ - return make_type(first) | (make_type(tail...) << 4); -} - -#else - -struct ArgType -{ - uint64_t type; - - ArgType() : type(0) {} - - template - ArgType(const T &arg) : type(make_type(arg)) {} -}; - -# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() - -inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) -{ - return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | - (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | - (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | - (t12.type << 48) | (t13.type << 52) | (t14.type << 56); -} -#endif -} // namespace internal - -# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n -# define FMT_MAKE_ARG_TYPE(n) T##n -# define FMT_MAKE_ARG(n) const T##n &v##n -# define FMT_ASSIGN_char(n) \ - arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) -# define FMT_ASSIGN_wchar_t(n) \ - arr[n] = fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) - -#if FMT_USE_VARIADIC_TEMPLATES -// Defines a variadic function returning void. -# define FMT_VARIADIC_VOID(func, arg_type) \ - template \ - void func(arg_type arg0, const Args & ... args) { \ - typedef fmt::internal::ArgArray ArgArray; \ - typename ArgArray::Type array{ \ - ArgArray::template make >(args)...}; \ - func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \ - } - -// Defines a variadic constructor. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ - typedef fmt::internal::ArgArray ArgArray; \ - typename ArgArray::Type array{ \ - ArgArray::template make >(args)...}; \ - func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \ - } - -#else - -# define FMT_MAKE_REF(n) \ - fmt::internal::MakeValue< fmt::BasicFormatter >(v##n) -# define FMT_MAKE_REF2(n) v##n - -// Defines a wrapper for a function taking one argument of type arg_type -// and n additional arguments of arbitrary types. -# define FMT_WRAP1(func, arg_type, n) \ - template \ - inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ - } - -// Emulates a variadic function returning void on a pre-C++11 compiler. -# define FMT_VARIADIC_VOID(func, arg_type) \ - inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ - FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ - FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ - FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ - FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ - FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) - -# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg0, arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ - } - -// Emulates a variadic constructor on a pre-C++11 compiler. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) -#endif - -// Generates a comma-separated list with results of applying f to pairs -// (argument, index). -#define FMT_FOR_EACH1(f, x0) f(x0, 0) -#define FMT_FOR_EACH2(f, x0, x1) \ - FMT_FOR_EACH1(f, x0), f(x1, 1) -#define FMT_FOR_EACH3(f, x0, x1, x2) \ - FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) -#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ - FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) -#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ - FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) -#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ - FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) -#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ - FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) -#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ - FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) -#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ - FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) -#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ - FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) - -/** - An error returned by an operating system or a language runtime, - for example a file opening error. -*/ -class SystemError : public internal::RuntimeError -{ -private: - void init(int err_code, CStringRef format_str, ArgList args); - -protected: - int error_code_; - - typedef char Char; // For FMT_VARIADIC_CTOR. - - SystemError() {} - -public: - /** - \rst - Constructs a :class:`fmt::SystemError` object with a description - formatted with `fmt::format_system_error`. *message* and additional - arguments passed into the constructor are formatted similarly to - `fmt::format`. - - **Example**:: - - // This throws a SystemError with the description - // cannot open file 'madeup': No such file or directory - // or similar (system message may vary). - const char *filename = "madeup"; - std::FILE *file = std::fopen(filename, "r"); - if (!file) - throw fmt::SystemError(errno, "cannot open file '{}'", filename); - \endrst - */ - SystemError(int error_code, CStringRef message) - { - init(error_code, message, ArgList()); - } - FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) - - ~SystemError() throw(); - - int error_code() const - { - return error_code_; - } -}; - -/** - \rst - Formats an error returned by an operating system or a language runtime, - for example a file opening error, and writes it to *out* in the following - form: - - .. parsed-literal:: - **: ** - - where ** is the passed message and ** is - the system message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - If *error_code* is not a valid error code such as -1, the system message - may look like "Unknown error -1" and is platform-dependent. - \endrst - */ -FMT_API void format_system_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; - -/** - \rst - This template provides operations for formatting and writing data into - a character stream. The output is stored in a buffer provided by a subclass - such as :class:`fmt::BasicMemoryWriter`. - - You can use one of the following typedefs for common character types: - - +---------+----------------------+ - | Type | Definition | - +=========+======================+ - | Writer | BasicWriter | - +---------+----------------------+ - | WWriter | BasicWriter | - +---------+----------------------+ - - \endrst - */ -template -class BasicWriter -{ -private: - // Output buffer. - Buffer &buffer_; - - FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); - - typedef typename internal::CharTraits::CharPtr CharPtr; - -#if FMT_SECURE_SCL - // Returns pointer value. - static Char *get(CharPtr p) - { - return p.base(); - } -#else - static Char *get(Char *p) - { - return p; - } -#endif - - // Fills the padding around the content and returns the pointer to the - // content area. - static CharPtr fill_padding(CharPtr buffer, - unsigned total_size, std::size_t content_size, wchar_t fill); - - // Grows the buffer by n characters and returns a pointer to the newly - // allocated area. - CharPtr grow_buffer(std::size_t n) - { - std::size_t size = buffer_.size(); - buffer_.resize(size + n); - return internal::make_ptr(&buffer_[size], n); - } - - // Writes an unsigned decimal integer. - template - Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) - { - unsigned num_digits = internal::count_digits(value); - Char *ptr = get(grow_buffer(prefix_size + num_digits)); - internal::format_decimal(ptr + prefix_size, value, num_digits); - return ptr; - } - - // Writes a decimal integer. - template - void write_decimal(Int value) - { - typedef typename internal::IntTraits::MainType MainType; - MainType abs_value = static_cast(value); - if (internal::is_negative(value)) - { - abs_value = 0 - abs_value; - *write_unsigned_decimal(abs_value, 1) = '-'; - } - else - { - write_unsigned_decimal(abs_value, 0); - } - } - - // Prepare a buffer for integer formatting. - CharPtr prepare_int_buffer(unsigned num_digits, - const EmptySpec &, const char *prefix, unsigned prefix_size) - { - unsigned size = prefix_size + num_digits; - CharPtr p = grow_buffer(size); - std::uninitialized_copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } - - template - CharPtr prepare_int_buffer(unsigned num_digits, - const Spec &spec, const char *prefix, unsigned prefix_size); - - // Formats an integer. - template - void write_int(T value, Spec spec); - - // Formats a floating-point number (double or long double). - template - void write_double(T value, const FormatSpec &spec); - - // Writes a formatted string. - template - CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec); - - template - void write_str(const internal::Arg::StringValue &str, - const FormatSpec &spec); - - // This following methods are private to disallow writing wide characters - // and strings to a char stream. If you want to print a wide string as a - // pointer as std::ostream does, cast it to const void*. - // Do not implement! - void operator<<(typename internal::WCharHelper::Unsupported); - void operator<<( - typename internal::WCharHelper::Unsupported); - - // Appends floating-point length specifier to the format string. - // The second argument is only used for overload resolution. - void append_float_length(Char *&format_ptr, long double) - { - *format_ptr++ = 'L'; - } - - template - void append_float_length(Char *&, T) {} - - template - friend class internal::ArgFormatterBase; - - template - friend class BasicPrintfArgFormatter; - -protected: - /** - Constructs a ``BasicWriter`` object. - */ - explicit BasicWriter(Buffer &b) : buffer_(b) {} - -public: - /** - \rst - Destroys a ``BasicWriter`` object. - \endrst - */ - virtual ~BasicWriter() {} - - /** - Returns the total number of characters written. - */ - std::size_t size() const - { - return buffer_.size(); - } - - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const Char *data() const FMT_NOEXCEPT - { - return &buffer_[0]; - } - - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const Char *c_str() const - { - std::size_t size = buffer_.size(); - buffer_.reserve(size + 1); - buffer_[size] = '\0'; - return &buffer_[0]; - } - - /** - \rst - Returns the content of the output buffer as an `std::string`. - \endrst - */ - std::basic_string str() const - { - return std::basic_string(&buffer_[0], buffer_.size()); - } - - /** - \rst - Writes formatted data. - - *args* is an argument list representing arbitrary arguments. - - **Example**:: - - MemoryWriter out; - out.write("Current point:\n"); - out.write("({:+f}, {:+f})", -3.14, 3.14); - - This will write the following output to the ``out`` object: - - .. code-block:: none - - Current point: - (-3.140000, +3.140000) - - The output can be accessed using :func:`data()`, :func:`c_str` or - :func:`str` methods. - - See also :ref:`syntax`. - \endrst - */ - void write(BasicCStringRef format, ArgList args) - { - BasicFormatter(args, *this).format(format); - } - FMT_VARIADIC_VOID(write, BasicCStringRef) - - BasicWriter &operator<<(int value) - { - write_decimal(value); - return *this; - } - BasicWriter &operator<<(unsigned value) - { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(long value) - { - write_decimal(value); - return *this; - } - BasicWriter &operator<<(unsigned long value) - { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(LongLong value) - { - write_decimal(value); - return *this; - } - - /** - \rst - Formats *value* and writes it to the stream. - \endrst - */ - BasicWriter &operator<<(ULongLong value) - { - return *this << IntFormatSpec(value); - } - - BasicWriter &operator<<(double value) - { - write_double(value, FormatSpec()); - return *this; - } - - /** - \rst - Formats *value* using the general format for floating-point numbers - (``'g'``) and writes it to the stream. - \endrst - */ - BasicWriter &operator<<(long double value) - { - write_double(value, FormatSpec()); - return *this; - } - - /** - Writes a character to the stream. - */ - BasicWriter &operator<<(char value) - { - buffer_.push_back(value); - return *this; - } - - BasicWriter &operator<<( - typename internal::WCharHelper::Supported value) - { - buffer_.push_back(value); - return *this; - } - - /** - \rst - Writes *value* to the stream. - \endrst - */ - BasicWriter &operator<<(fmt::BasicStringRef value) - { - const Char *str = value.data(); - buffer_.append(str, str + value.size()); - return *this; - } - - BasicWriter &operator<<( - typename internal::WCharHelper::Supported value) - { - const char *str = value.data(); - buffer_.append(str, str + value.size()); - return *this; - } - - template - BasicWriter &operator<<(IntFormatSpec spec) - { - internal::CharTraits::convert(FillChar()); - write_int(spec.value(), spec); - return *this; - } - - template - BasicWriter &operator<<(const StrFormatSpec &spec) - { - const StrChar *s = spec.str(); - write_str(s, std::char_traits::length(s), spec); - return *this; - } - - void clear() FMT_NOEXCEPT { buffer_.clear(); } - - Buffer &buffer() FMT_NOEXCEPT { return buffer_; } -}; - -template -template -typename BasicWriter::CharPtr BasicWriter::write_str( - const StrChar *s, std::size_t size, const AlignSpec &spec) -{ - CharPtr out = CharPtr(); - if (spec.width() > size) - { - out = grow_buffer(spec.width()); - Char fill = internal::CharTraits::cast(spec.fill()); - if (spec.align() == ALIGN_RIGHT) - { - std::uninitialized_fill_n(out, spec.width() - size, fill); - out += spec.width() - size; - } - else if (spec.align() == ALIGN_CENTER) - { - out = fill_padding(out, spec.width(), size, fill); - } - else - { - std::uninitialized_fill_n(out + size, spec.width() - size, fill); - } - } - else - { - out = grow_buffer(size); - } - std::uninitialized_copy(s, s + size, out); - return out; -} - -template -template -void BasicWriter::write_str( - const internal::Arg::StringValue &s, const FormatSpec &spec) -{ - // Check if StrChar is convertible to Char. - internal::CharTraits::convert(StrChar()); - if (spec.type_ && spec.type_ != 's') - internal::report_unknown_type(spec.type_, "string"); - const StrChar *str_value = s.value; - std::size_t str_size = s.size; - if (str_size == 0) - { - if (!str_value) - { - FMT_THROW(FormatError("string pointer is null")); - } - } - std::size_t precision = static_cast(spec.precision_); - if (spec.precision_ >= 0 && precision < str_size) - str_size = precision; - write_str(str_value, str_size, spec); -} - -template -typename BasicWriter::CharPtr -BasicWriter::fill_padding( - CharPtr buffer, unsigned total_size, - std::size_t content_size, wchar_t fill) -{ - std::size_t padding = total_size - content_size; - std::size_t left_padding = padding / 2; - Char fill_char = internal::CharTraits::cast(fill); - std::uninitialized_fill_n(buffer, left_padding, fill_char); - buffer += left_padding; - CharPtr content = buffer; - std::uninitialized_fill_n(buffer + content_size, - padding - left_padding, fill_char); - return content; -} - -template -template -typename BasicWriter::CharPtr -BasicWriter::prepare_int_buffer( - unsigned num_digits, const Spec &spec, - const char *prefix, unsigned prefix_size) -{ - unsigned width = spec.width(); - Alignment align = spec.align(); - Char fill = internal::CharTraits::cast(spec.fill()); - if (spec.precision() > static_cast(num_digits)) - { - // Octal prefix '0' is counted as a digit, so ignore it if precision - // is specified. - if (prefix_size > 0 && prefix[prefix_size - 1] == '0') - --prefix_size; - unsigned number_size = - prefix_size + internal::to_unsigned(spec.precision()); - AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); - if (number_size >= width) - return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); - buffer_.reserve(width); - unsigned fill_size = width - number_size; - if (align != ALIGN_LEFT) - { - CharPtr p = grow_buffer(fill_size); - std::uninitialized_fill(p, p + fill_size, fill); - } - CharPtr result = prepare_int_buffer( - num_digits, subspec, prefix, prefix_size); - if (align == ALIGN_LEFT) - { - CharPtr p = grow_buffer(fill_size); - std::uninitialized_fill(p, p + fill_size, fill); - } - return result; - } - unsigned size = prefix_size + num_digits; - if (width <= size) - { - CharPtr p = grow_buffer(size); - std::uninitialized_copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } - CharPtr p = grow_buffer(width); - CharPtr end = p + width; - if (align == ALIGN_LEFT) - { - std::uninitialized_copy(prefix, prefix + prefix_size, p); - p += size; - std::uninitialized_fill(p, end, fill); - } - else if (align == ALIGN_CENTER) - { - p = fill_padding(p, width, size, fill); - std::uninitialized_copy(prefix, prefix + prefix_size, p); - p += size; - } - else - { - if (align == ALIGN_NUMERIC) - { - if (prefix_size != 0) - { - p = std::uninitialized_copy(prefix, prefix + prefix_size, p); - size -= prefix_size; - } - } - else - { - std::uninitialized_copy(prefix, prefix + prefix_size, end - size); - } - std::uninitialized_fill(p, end - size, fill); - p = end; - } - return p - 1; -} - -template -template -void BasicWriter::write_int(T value, Spec spec) -{ - unsigned prefix_size = 0; - typedef typename internal::IntTraits::MainType UnsignedType; - UnsignedType abs_value = static_cast(value); - char prefix[4] = ""; - if (internal::is_negative(value)) - { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } - else if (spec.flag(SIGN_FLAG)) - { - prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; - ++prefix_size; - } - switch (spec.type()) - { - case 0: - case 'd': - { - unsigned num_digits = internal::count_digits(abs_value); - CharPtr p = prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1; - internal::format_decimal(get(p), abs_value, 0); - break; - } - case 'x': - case 'X': - { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) - { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do - { - ++num_digits; - } - while ((n >>= 4) != 0); - Char *p = get(prepare_int_buffer( - num_digits, spec, prefix, prefix_size)); - n = abs_value; - const char *digits = spec.type() == 'x' ? - "0123456789abcdef" : "0123456789ABCDEF"; - do - { - *p-- = digits[n & 0xf]; - } - while ((n >>= 4) != 0); - break; - } - case 'b': - case 'B': - { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) - { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do - { - ++num_digits; - } - while ((n >>= 1) != 0); - Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do - { - *p-- = static_cast('0' + (n & 1)); - } - while ((n >>= 1) != 0); - break; - } - case 'o': - { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) - prefix[prefix_size++] = '0'; - unsigned num_digits = 0; - do - { - ++num_digits; - } - while ((n >>= 3) != 0); - Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do - { - *p-- = static_cast('0' + (n & 7)); - } - while ((n >>= 3) != 0); - break; - } - case 'n': - { - unsigned num_digits = internal::count_digits(abs_value); - fmt::StringRef sep = ""; -#ifndef ANDROID - sep = internal::thousands_sep(std::localeconv()); -#endif - unsigned size = static_cast( - num_digits + sep.size() * ((num_digits - 1) / 3)); - CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1; - internal::format_decimal(get(p), abs_value, 0, internal::ThousandsSep(sep)); - break; - } - default: - internal::report_unknown_type( - spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); - break; - } -} - -template -template -void BasicWriter::write_double(T value, const FormatSpec &spec) -{ - // Check type. - char type = spec.type(); - bool upper = false; - switch (type) - { - case 0: - type = 'g'; - break; - case 'e': - case 'f': - case 'g': - case 'a': - break; - case 'F': -#if FMT_MSC_VER - // MSVC's printf doesn't support 'F'. - type = 'f'; -#endif - // Fall through. - case 'E': - case 'G': - case 'A': - upper = true; - break; - default: - internal::report_unknown_type(type, "double"); - break; - } - - char sign = 0; - // Use isnegative instead of value < 0 because the latter is always - // false for NaN. - if (internal::FPUtil::isnegative(static_cast(value))) - { - sign = '-'; - value = -value; - } - else if (spec.flag(SIGN_FLAG)) - { - sign = spec.flag(PLUS_FLAG) ? '+' : ' '; - } - - if (internal::FPUtil::isnotanumber(value)) - { - // Format NaN ourselves because sprintf's output is not consistent - // across platforms. - std::size_t nan_size = 4; - const char *nan = upper ? " NAN" : " nan"; - if (!sign) - { - --nan_size; - ++nan; - } - CharPtr out = write_str(nan, nan_size, spec); - if (sign) - *out = sign; - return; - } - - if (internal::FPUtil::isinfinity(value)) - { - // Format infinity ourselves because sprintf's output is not consistent - // across platforms. - std::size_t inf_size = 4; - const char *inf = upper ? " INF" : " inf"; - if (!sign) - { - --inf_size; - ++inf; - } - CharPtr out = write_str(inf, inf_size, spec); - if (sign) - *out = sign; - return; - } - - std::size_t offset = buffer_.size(); - unsigned width = spec.width(); - if (sign) - { - buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u)); - if (width > 0) - --width; - ++offset; - } - - // Build format string. - enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg - Char format[MAX_FORMAT_SIZE]; - Char *format_ptr = format; - *format_ptr++ = '%'; - unsigned width_for_sprintf = width; - if (spec.flag(HASH_FLAG)) - *format_ptr++ = '#'; - if (spec.align() == ALIGN_CENTER) - { - width_for_sprintf = 0; - } - else - { - if (spec.align() == ALIGN_LEFT) - *format_ptr++ = '-'; - if (width != 0) - *format_ptr++ = '*'; - } - if (spec.precision() >= 0) - { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - - append_float_length(format_ptr, value); - *format_ptr++ = type; - *format_ptr = '\0'; - - // Format using snprintf. - Char fill = internal::CharTraits::cast(spec.fill()); - unsigned n = 0; - Char *start = 0; - for (;;) - { - std::size_t buffer_size = buffer_.capacity() - offset; -#if FMT_MSC_VER - // MSVC's vsnprintf_s doesn't work with zero size, so reserve - // space for at least one extra character to make the size non-zero. - // Note that the buffer's capacity will increase by more than 1. - if (buffer_size == 0) - { - buffer_.reserve(offset + 1); - buffer_size = buffer_.capacity() - offset; - } -#endif - start = &buffer_[offset]; - int result = internal::CharTraits::format_float( - start, buffer_size, format, width_for_sprintf, spec.precision(), value); - if (result >= 0) - { - n = internal::to_unsigned(result); - if (offset + n < buffer_.capacity()) - break; // The buffer is large enough - continue with formatting. - buffer_.reserve(offset + n + 1); - } - else - { - // If result is negative we ask to increase the capacity by at least 1, - // but as std::vector, the buffer grows exponentially. - buffer_.reserve(buffer_.capacity() + 1); - } - } - if (sign) - { - if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || - *start != ' ') - { - *(start - 1) = sign; - sign = 0; - } - else - { - *(start - 1) = fill; - } - ++n; - } - if (spec.align() == ALIGN_CENTER && spec.width() > n) - { - width = spec.width(); - CharPtr p = grow_buffer(width); - std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char)); - fill_padding(p, spec.width(), n, fill); - return; - } - if (spec.fill() != ' ' || sign) - { - while (*start == ' ') - *start++ = fill; - if (sign) - *(start - 1) = sign; - } - grow_buffer(n); -} - -/** - \rst - This class template provides operations for formatting and writing data - into a character stream. The output is stored in a memory buffer that grows - dynamically. - - You can use one of the following typedefs for common character types - and the standard allocator: - - +---------------+-----------------------------------------------------+ - | Type | Definition | - +===============+=====================================================+ - | MemoryWriter | BasicMemoryWriter> | - +---------------+-----------------------------------------------------+ - | WMemoryWriter | BasicMemoryWriter> | - +---------------+-----------------------------------------------------+ - - **Example**:: - - MemoryWriter out; - out << "The answer is " << 42 << "\n"; - out.write("({:+f}, {:+f})", -3.14, 3.14); - - This will write the following output to the ``out`` object: - - .. code-block:: none - - The answer is 42 - (-3.140000, +3.140000) - - The output can be converted to an ``std::string`` with ``out.str()`` or - accessed as a C string with ``out.c_str()``. - \endrst - */ -template > -class BasicMemoryWriter : public BasicWriter -{ -private: - internal::MemoryBuffer buffer_; - -public: - explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) - : BasicWriter(buffer_), buffer_(alloc) {} - -#if FMT_USE_RVALUE_REFERENCES - /** - \rst - Constructs a :class:`fmt::BasicMemoryWriter` object moving the content - of the other object to it. - \endrst - */ - BasicMemoryWriter(BasicMemoryWriter &&other) - : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) - { - } - - /** - \rst - Moves the content of the other ``BasicMemoryWriter`` object to this one. - \endrst - */ - BasicMemoryWriter &operator=(BasicMemoryWriter &&other) - { - buffer_ = std::move(other.buffer_); - return *this; - } -#endif -}; - -typedef BasicMemoryWriter MemoryWriter; -typedef BasicMemoryWriter WMemoryWriter; - -/** - \rst - This class template provides operations for formatting and writing data - into a fixed-size array. For writing into a dynamically growing buffer - use :class:`fmt::BasicMemoryWriter`. - - Any write method will throw ``std::runtime_error`` if the output doesn't fit - into the array. - - You can use one of the following typedefs for common character types: - - +--------------+---------------------------+ - | Type | Definition | - +==============+===========================+ - | ArrayWriter | BasicArrayWriter | - +--------------+---------------------------+ - | WArrayWriter | BasicArrayWriter | - +--------------+---------------------------+ - \endrst - */ -template -class BasicArrayWriter : public BasicWriter -{ -private: - internal::FixedBuffer buffer_; - -public: - /** - \rst - Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the - given size. - \endrst - */ - BasicArrayWriter(Char *array, std::size_t size) - : BasicWriter(buffer_), buffer_(array, size) {} - - /** - \rst - Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the - size known at compile time. - \endrst - */ - template - explicit BasicArrayWriter(Char (&array)[SIZE]) - : BasicWriter(buffer_), buffer_(array, SIZE) {} -}; - -typedef BasicArrayWriter ArrayWriter; -typedef BasicArrayWriter WArrayWriter; - -// Reports a system error without throwing an exception. -// Can be used to report errors from destructors. -FMT_API void report_system_error(int error_code, - StringRef message) FMT_NOEXCEPT; - -#if FMT_USE_WINDOWS_H - -/** A Windows error. */ -class WindowsError : public SystemError -{ -private: - FMT_API void init(int error_code, CStringRef format_str, ArgList args); - -public: - /** - \rst - Constructs a :class:`fmt::WindowsError` object with the description - of the form - - .. parsed-literal:: - **: ** - - where ** is the formatted message and ** is the - system message corresponding to the error code. - *error_code* is a Windows error code as given by ``GetLastError``. - If *error_code* is not a valid error code such as -1, the system message - will look like "error -1". - - **Example**:: - - // This throws a WindowsError with the description - // cannot open file 'madeup': The system cannot find the file specified. - // or similar (system message may vary). - const char *filename = "madeup"; - LPOFSTRUCT of = LPOFSTRUCT(); - HFILE file = OpenFile(filename, &of, OF_READ); - if (file == HFILE_ERROR) { - throw fmt::WindowsError(GetLastError(), - "cannot open file '{}'", filename); - } - \endrst - */ - WindowsError(int error_code, CStringRef message) - { - init(error_code, message, ArgList()); - } - FMT_VARIADIC_CTOR(WindowsError, init, int, CStringRef) -}; - -// Reports a Windows error without throwing an exception. -// Can be used to report errors from destructors. -FMT_API void report_windows_error(int error_code, - StringRef message) FMT_NOEXCEPT; - -#endif - -enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; - -/** - Formats a string and prints it to stdout using ANSI escape sequences - to specify color (experimental). - Example: - print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); - */ -FMT_API void print_colored(Color c, CStringRef format, ArgList args); - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = format("The answer is {}", 42); - \endrst -*/ -inline std::string format(CStringRef format_str, ArgList args) -{ - MemoryWriter w; - w.write(format_str, args); - return w.str(); -} - -inline std::wstring format(WCStringRef format_str, ArgList args) -{ - WMemoryWriter w; - w.write(format_str, args); - return w.str(); -} - -/** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - print(stderr, "Don't {}!", "panic"); - \endrst - */ -FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args); - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst - */ -FMT_API void print(CStringRef format_str, ArgList args); - -/** - Fast integer formatter. - */ -class FormatInt -{ -private: - // Buffer should be large enough to hold all digits (digits10 + 1), - // a sign and a null character. - enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; - mutable char buffer_[BUFFER_SIZE]; - char *str_; - - // Formats value in reverse and returns the number of digits. - char *format_decimal(ULongLong value) - { - char *buffer_end = buffer_ + BUFFER_SIZE - 1; - while (value >= 100) - { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = static_cast((value % 100) * 2); - value /= 100; - *--buffer_end = internal::Data::DIGITS[index + 1]; - *--buffer_end = internal::Data::DIGITS[index]; - } - if (value < 10) - { - *--buffer_end = static_cast('0' + value); - return buffer_end; - } - unsigned index = static_cast(value * 2); - *--buffer_end = internal::Data::DIGITS[index + 1]; - *--buffer_end = internal::Data::DIGITS[index]; - return buffer_end; - } - - void FormatSigned(LongLong value) - { - ULongLong abs_value = static_cast(value); - bool negative = value < 0; - if (negative) - abs_value = 0 - abs_value; - str_ = format_decimal(abs_value); - if (negative) - *--str_ = '-'; - } - -public: - explicit FormatInt(int value) - { - FormatSigned(value); - } - explicit FormatInt(long value) - { - FormatSigned(value); - } - explicit FormatInt(LongLong value) - { - FormatSigned(value); - } - explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} - explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} - explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} - - /** Returns the number of characters written to the output buffer. */ - std::size_t size() const - { - return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1); - } - - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const char *data() const - { - return str_; - } - - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const char *c_str() const - { - buffer_[BUFFER_SIZE - 1] = '\0'; - return str_; - } - - /** - \rst - Returns the content of the output buffer as an ``std::string``. - \endrst - */ - std::string str() const - { - return std::string(str_, size()); - } -}; - -// Formats a decimal integer value writing into buffer and returns -// a pointer to the end of the formatted string. This function doesn't -// write a terminating null character. -template -inline void format_decimal(char *&buffer, T value) -{ - typedef typename internal::IntTraits::MainType MainType; - MainType abs_value = static_cast(value); - if (internal::is_negative(value)) - { - *buffer++ = '-'; - abs_value = 0 - abs_value; - } - if (abs_value < 100) - { - if (abs_value < 10) - { - *buffer++ = static_cast('0' + abs_value); - return; - } - unsigned index = static_cast(abs_value * 2); - *buffer++ = internal::Data::DIGITS[index]; - *buffer++ = internal::Data::DIGITS[index + 1]; - return; - } - unsigned num_digits = internal::count_digits(abs_value); - internal::format_decimal(buffer, abs_value, num_digits); - buffer += num_digits; -} - -/** - \rst - Returns a named argument for formatting functions. - - **Example**:: - - print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); - - \endrst - */ -template -inline internal::NamedArg arg(StringRef name, const T &arg) -{ - return internal::NamedArg(name, arg); -} - -template -inline internal::NamedArg arg(WStringRef name, const T &arg) -{ - return internal::NamedArg(name, arg); -} - -// The following two functions are deleted intentionally to disable -// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``. -template -void arg(StringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; -template -void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; -} - -#if FMT_GCC_VERSION -// Use the system_header pragma to suppress warnings about variadic macros -// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't -// work. It is used at the end because we want to suppress as little warnings -// as possible. -# pragma GCC system_header -#endif - -// This is used to work around VC++ bugs in handling variadic macros. -#define FMT_EXPAND(args) args - -// Returns the number of arguments. -// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. -#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) -#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) -#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N -#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 - -#define FMT_FOR_EACH_(N, f, ...) \ - FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) -#define FMT_FOR_EACH(f, ...) \ - FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) - -#define FMT_ADD_ARG_NAME(type, index) type arg##index -#define FMT_GET_ARG_NAME(type, index) arg##index - -#if FMT_USE_VARIADIC_TEMPLATES -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - template \ - ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - const Args & ... args) { \ - typedef fmt::internal::ArgArray ArgArray; \ - typename ArgArray::Type array{ \ - ArgArray::template make >(args)...}; \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ - fmt::ArgList(fmt::internal::make_type(args...), array)); \ - } -#else -// Defines a wrapper for a function taking __VA_ARGS__ arguments -// and n additional arguments of arbitrary types. -# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ - template \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - FMT_GEN(n, FMT_MAKE_ARG)) { \ - fmt::internal::ArgArray::Type arr; \ - FMT_GEN(n, FMT_ASSIGN_##Char); \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \ - } - -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ - } \ - FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) -#endif // FMT_USE_VARIADIC_TEMPLATES - -/** - \rst - Defines a variadic function with the specified return type, function name - and argument types passed as variable arguments to this macro. - - **Example**:: - - void print_error(const char *file, int line, const char *format, - fmt::ArgList args) { - fmt::print("{}: {}: ", file, line); - fmt::print(format, args); - } - FMT_VARIADIC(void, print_error, const char *, int, const char *) - - ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that - don't implement variadic templates. You don't have to use this macro if - you don't need legacy compiler support and can use variadic templates - directly:: - - template - void print_error(const char *file, int line, const char *format, - const Args & ... args) { - fmt::print("{}: {}: ", file, line); - fmt::print(format, args...); - } - \endrst - */ -#define FMT_VARIADIC(ReturnType, func, ...) \ - FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) - -#define FMT_VARIADIC_W(ReturnType, func, ...) \ - FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) - -#define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) - -#define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L###id, id) - -/** - \rst - Convenient macro to capture the arguments' names and values into several - ``fmt::arg(name, value)``. - - **Example**:: - - int x = 1, y = 2; - print("point: ({x}, {y})", FMT_CAPTURE(x, y)); - // same as: - // print("point: ({x}, {y})", arg("x", x), arg("y", y)); - - \endrst - */ -#define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__) - -#define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__) - -namespace fmt -{ -FMT_VARIADIC(std::string, format, CStringRef) -FMT_VARIADIC_W(std::wstring, format, WCStringRef) -FMT_VARIADIC(void, print, CStringRef) -FMT_VARIADIC(void, print, std::FILE *, CStringRef) -FMT_VARIADIC(void, print_colored, Color, CStringRef) - -namespace internal -{ -template -inline bool is_name_start(Char c) -{ - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; -} - -// Parses an unsigned integer advancing s to the end of the parsed input. -// This function assumes that the first character of s is a digit. -template -unsigned parse_nonnegative_int(const Char *&s) -{ - assert('0' <= *s && *s <= '9'); - unsigned value = 0; - do - { - unsigned new_value = value * 10 + (*s++ - '0'); - // Check if value wrapped around. - if (new_value < value) - { - value = (std::numeric_limits::max)(); - break; - } - value = new_value; - } - while ('0' <= *s && *s <= '9'); - // Convert to unsigned to prevent a warning. - unsigned max_int = (std::numeric_limits::max)(); - if (value > max_int) - FMT_THROW(FormatError("number is too big")); - return value; -} - -inline void require_numeric_argument(const Arg &arg, char spec) -{ - if (arg.type > Arg::LAST_NUMERIC_TYPE) - { - std::string message = - fmt::format("format specifier '{}' requires numeric argument", spec); - FMT_THROW(fmt::FormatError(message)); - } -} - -template -void check_sign(const Char *&s, const Arg &arg) -{ - char sign = static_cast(*s); - require_numeric_argument(arg, sign); - if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) - { - FMT_THROW(FormatError(fmt::format( - "format specifier '{}' requires signed argument", sign))); - } - ++s; -} -} // namespace internal - -template -inline internal::Arg BasicFormatter::get_arg( - BasicStringRef arg_name, const char *&error) -{ - if (check_no_auto_index(error)) - { - map_.init(args()); - const internal::Arg *arg = map_.find(arg_name); - if (arg) - return *arg; - error = "argument not found"; - } - return internal::Arg(); -} - -template -inline internal::Arg BasicFormatter::parse_arg_index(const Char *&s) -{ - const char *error = 0; - internal::Arg arg = *s < '0' || *s > '9' ? - next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error); - if (error) - { - FMT_THROW(FormatError( - *s != '}' && *s != ':' ? "invalid format string" : error)); - } - return arg; -} - -template -inline internal::Arg BasicFormatter::parse_arg_name(const Char *&s) -{ - assert(internal::is_name_start(*s)); - const Char *start = s; - Char c; - do - { - c = *++s; - } - while (internal::is_name_start(c) || ('0' <= c && c <= '9')); - const char *error = 0; - internal::Arg arg = get_arg(BasicStringRef(start, s - start), error); - if (error) - FMT_THROW(FormatError(error)); - return arg; -} - -template -const Char *BasicFormatter::format( - const Char *&format_str, const internal::Arg &arg) -{ - using internal::Arg; - const Char *s = format_str; - FormatSpec spec; - if (*s == ':') - { - if (arg.type == Arg::CUSTOM) - { - arg.custom.format(this, arg.custom.value, &s); - return s; - } - ++s; - // Parse fill and alignment. - if (Char c = *s) - { - const Char *p = s + 1; - spec.align_ = ALIGN_DEFAULT; - do - { - switch (*p) - { - case '<': - spec.align_ = ALIGN_LEFT; - break; - case '>': - spec.align_ = ALIGN_RIGHT; - break; - case '=': - spec.align_ = ALIGN_NUMERIC; - break; - case '^': - spec.align_ = ALIGN_CENTER; - break; - } - if (spec.align_ != ALIGN_DEFAULT) - { - if (p != s) - { - if (c == '}') break; - if (c == '{') - FMT_THROW(FormatError("invalid fill character '{'")); - s += 2; - spec.fill_ = c; - } - else ++s; - if (spec.align_ == ALIGN_NUMERIC) - require_numeric_argument(arg, '='); - break; - } - } - while (--p >= s); - } - - // Parse sign. - switch (*s) - { - case '+': - check_sign(s, arg); - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '-': - check_sign(s, arg); - spec.flags_ |= MINUS_FLAG; - break; - case ' ': - check_sign(s, arg); - spec.flags_ |= SIGN_FLAG; - break; - } - - if (*s == '#') - { - require_numeric_argument(arg, '#'); - spec.flags_ |= HASH_FLAG; - ++s; - } - - // Parse zero flag. - if (*s == '0') - { - require_numeric_argument(arg, '0'); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; - ++s; - } - - // Parse width. - if ('0' <= *s && *s <= '9') - { - spec.width_ = internal::parse_nonnegative_int(s); - } - else if (*s == '{') - { - ++s; - Arg width_arg = internal::is_name_start(*s) ? - parse_arg_name(s) : parse_arg_index(s); - if (*s++ != '}') - FMT_THROW(FormatError("invalid format string")); - ULongLong value = 0; - switch (width_arg.type) - { - case Arg::INT: - if (width_arg.int_value < 0) - FMT_THROW(FormatError("negative width")); - value = width_arg.int_value; - break; - case Arg::UINT: - value = width_arg.uint_value; - break; - case Arg::LONG_LONG: - if (width_arg.long_long_value < 0) - FMT_THROW(FormatError("negative width")); - value = width_arg.long_long_value; - break; - case Arg::ULONG_LONG: - value = width_arg.ulong_long_value; - break; - default: - FMT_THROW(FormatError("width is not integer")); - } - if (value > (std::numeric_limits::max)()) - FMT_THROW(FormatError("number is too big")); - spec.width_ = static_cast(value); - } - - // Parse precision. - if (*s == '.') - { - ++s; - spec.precision_ = 0; - if ('0' <= *s && *s <= '9') - { - spec.precision_ = internal::parse_nonnegative_int(s); - } - else if (*s == '{') - { - ++s; - Arg precision_arg = internal::is_name_start(*s) ? - parse_arg_name(s) : parse_arg_index(s); - if (*s++ != '}') - FMT_THROW(FormatError("invalid format string")); - ULongLong value = 0; - switch (precision_arg.type) - { - case Arg::INT: - if (precision_arg.int_value < 0) - FMT_THROW(FormatError("negative precision")); - value = precision_arg.int_value; - break; - case Arg::UINT: - value = precision_arg.uint_value; - break; - case Arg::LONG_LONG: - if (precision_arg.long_long_value < 0) - FMT_THROW(FormatError("negative precision")); - value = precision_arg.long_long_value; - break; - case Arg::ULONG_LONG: - value = precision_arg.ulong_long_value; - break; - default: - FMT_THROW(FormatError("precision is not integer")); - } - if (value > (std::numeric_limits::max)()) - FMT_THROW(FormatError("number is too big")); - spec.precision_ = static_cast(value); - } - else - { - FMT_THROW(FormatError("missing precision specifier")); - } - if (arg.type <= Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) - { - FMT_THROW(FormatError( - fmt::format("precision not allowed in {} format specifier", - arg.type == Arg::POINTER ? "pointer" : "integer"))); - } - } - - // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); - } - - if (*s++ != '}') - FMT_THROW(FormatError("missing '}' in format string")); - - // Format argument. - ArgFormatter(*this, spec, s - 1).visit(arg); - return s; -} - -template -void BasicFormatter::format(BasicCStringRef format_str) -{ - const Char *s = format_str.c_str(); - const Char *start = s; - while (*s) - { - Char c = *s++; - if (c != '{' && c != '}') continue; - if (*s == c) - { - write(writer_, start, s); - start = ++s; - continue; - } - if (c == '}') - FMT_THROW(FormatError("unmatched '}' in format string")); - write(writer_, start, s - 1); - internal::Arg arg = internal::is_name_start(*s) ? - parse_arg_name(s) : parse_arg_index(s); - start = s = format(s, arg); - } - write(writer_, start, s); -} -} // namespace fmt - -#if FMT_USE_USER_DEFINED_LITERALS -namespace fmt -{ -namespace internal -{ - -template -struct UdlFormat -{ - const Char *str; - - template - auto operator()(Args && ... args) const - -> decltype(format(str, std::forward(args)...)) - { - return format(str, std::forward(args)...); - } -}; - -template -struct UdlArg -{ - const Char *str; - - template - NamedArg operator=(T &&value) const - { - return {str, std::forward(value)}; - } -}; - -} // namespace internal - -inline namespace literals -{ - -/** - \rst - C++11 literal equivalent of :func:`fmt::format`. - - **Example**:: - - using namespace fmt::literals; - std::string message = "The answer is {}"_format(42); - \endrst - */ -inline internal::UdlFormat -operator"" _format(const char *s, std::size_t) -{ - return {s}; -} -inline internal::UdlFormat -operator"" _format(const wchar_t *s, std::size_t) -{ - return {s}; -} - -/** - \rst - C++11 literal equivalent of :func:`fmt::arg`. - - **Example**:: - - using namespace fmt::literals; - print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); - \endrst - */ -inline internal::UdlArg -operator"" _a(const char *s, std::size_t) -{ - return {s}; -} -inline internal::UdlArg -operator"" _a(const wchar_t *s, std::size_t) -{ - return {s}; -} - -} // inline namespace literals -} // namespace fmt -#endif // FMT_USE_USER_DEFINED_LITERALS - -// Restore warnings. -#if FMT_GCC_VERSION >= 406 -# pragma GCC diagnostic pop -#endif - -#if defined(__clang__) && !defined(FMT_ICC_VERSION) -# pragma clang diagnostic pop -#endif - -#ifdef FMT_HEADER_ONLY -# define FMT_FUNC inline -# include "format.cc" -#else -# define FMT_FUNC -#endif - -#endif // FMT_FORMAT_H_ diff --git a/include/cpd/vendor/spdlog/fmt/bundled/ostream.cc b/include/cpd/vendor/spdlog/fmt/bundled/ostream.cc deleted file mode 100644 index 4d4a9a4d..00000000 --- a/include/cpd/vendor/spdlog/fmt/bundled/ostream.cc +++ /dev/null @@ -1,36 +0,0 @@ -/* - Formatting library for C++ - std::ostream support - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ - -// Commented out by spdlog to use header only -// #include "fmt/ostream.h" - -namespace fmt { - -namespace internal { -FMT_FUNC void write(std::ostream &os, Writer &w) { - const char *data = w.data(); - typedef internal::MakeUnsigned::Type UnsignedStreamSize; - UnsignedStreamSize size = w.size(); - UnsignedStreamSize max_size = - internal::to_unsigned((std::numeric_limits::max)()); - do { - UnsignedStreamSize n = size <= max_size ? size : max_size; - os.write(data, static_cast(n)); - data += n; - size -= n; - } while (size != 0); -} -} - -FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - internal::write(os, w); -} -} // namespace fmt diff --git a/include/cpd/vendor/spdlog/fmt/bundled/ostream.h b/include/cpd/vendor/spdlog/fmt/bundled/ostream.h deleted file mode 100644 index 37e08f3b..00000000 --- a/include/cpd/vendor/spdlog/fmt/bundled/ostream.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - Formatting library for C++ - std::ostream support - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ - -#ifndef FMT_OSTREAM_H_ -#define FMT_OSTREAM_H_ - -// Commented out by spdlog to use header only -// #include "fmt/format.h" -#include - -namespace fmt -{ - -namespace internal -{ - -template -class FormatBuf : public std::basic_streambuf -{ -private: - typedef typename std::basic_streambuf::int_type int_type; - typedef typename std::basic_streambuf::traits_type traits_type; - - Buffer &buffer_; - Char *start_; - -public: - FormatBuf(Buffer &buffer) : buffer_(buffer), start_(&buffer[0]) - { - this->setp(start_, start_ + buffer_.capacity()); - } - - int_type overflow(int_type ch = traits_type::eof()) - { - if (!traits_type::eq_int_type(ch, traits_type::eof())) - { - size_t buf_size = size(); - buffer_.resize(buf_size); - buffer_.reserve(buf_size * 2); - - start_ = &buffer_[0]; - start_[buf_size] = traits_type::to_char_type(ch); - this->setp(start_+ buf_size + 1, start_ + buf_size * 2); - } - return ch; - } - - size_t size() const - { - return to_unsigned(this->pptr() - start_); - } -}; - -Yes &convert(std::ostream &); - -struct DummyStream : std::ostream -{ - DummyStream(); // Suppress a bogus warning in MSVC. - // Hide all operator<< overloads from std::ostream. - void operator<<(Null<>); -}; - -No &operator<<(std::ostream &, int); - -template -struct ConvertToIntImpl -{ - // Convert to int only if T doesn't have an overloaded operator<<. - enum - { - value = sizeof(convert(get() << get())) == sizeof(No) - }; -}; - -// Write the content of w to os. -void write(std::ostream &os, Writer &w); -} // namespace internal - -// Formats a value. -template -void format_arg(BasicFormatter &f, - const Char *&format_str, const T &value) -{ - internal::MemoryBuffer buffer; - - internal::FormatBuf format_buf(buffer); - std::basic_ostream output(&format_buf); - output << value; - - BasicStringRef str(&buffer[0], format_buf.size()); - typedef internal::MakeArg< BasicFormatter > MakeArg; - format_str = f.format(format_str, MakeArg(str)); -} - -/** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - print(cerr, "Don't {}!", "panic"); - \endrst - */ -FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); -FMT_VARIADIC(void, print, std::ostream &, CStringRef) -} // namespace fmt - -#ifdef FMT_HEADER_ONLY -# include "ostream.cc" -#endif - -#endif // FMT_OSTREAM_H_ diff --git a/include/cpd/vendor/spdlog/fmt/bundled/printf.h b/include/cpd/vendor/spdlog/fmt/bundled/printf.h deleted file mode 100644 index 77e68403..00000000 --- a/include/cpd/vendor/spdlog/fmt/bundled/printf.h +++ /dev/null @@ -1,642 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ - -#ifndef FMT_PRINTF_H_ -#define FMT_PRINTF_H_ - -#include // std::fill_n -#include // std::numeric_limits - -#include "fmt/ostream.h" - -namespace fmt -{ -namespace internal -{ - -// Checks if a value fits in int - used to avoid warnings about comparing -// signed and unsigned integers. -template -struct IntChecker -{ - template - static bool fits_in_int(T value) - { - unsigned max = std::numeric_limits::max(); - return value <= max; - } - static bool fits_in_int(bool) - { - return true; - } -}; - -template <> -struct IntChecker -{ - template - static bool fits_in_int(T value) - { - return value >= std::numeric_limits::min() && - value <= std::numeric_limits::max(); - } - static bool fits_in_int(int) - { - return true; - } -}; - -class PrecisionHandler : public ArgVisitor -{ -public: - void report_unhandled_arg() - { - FMT_THROW(FormatError("precision is not integer")); - } - - template - int visit_any_int(T value) - { - if (!IntChecker::is_signed>::fits_in_int(value)) - FMT_THROW(FormatError("number is too big")); - return static_cast(value); - } -}; - -// IsZeroInt::visit(arg) returns true iff arg is a zero integer. -class IsZeroInt : public ArgVisitor -{ -public: - template - bool visit_any_int(T value) - { - return value == 0; - } -}; - -template -struct is_same -{ - enum { value = 0 }; -}; - -template -struct is_same -{ - enum { value = 1 }; -}; - -// An argument visitor that converts an integer argument to T for printf, -// if T is an integral type. If T is void, the argument is converted to -// corresponding signed or unsigned type depending on the type specifier: -// 'd' and 'i' - signed, other - unsigned) -template -class ArgConverter : public ArgVisitor, void> -{ -private: - internal::Arg &arg_; - wchar_t type_; - - FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); - -public: - ArgConverter(internal::Arg &arg, wchar_t type) - : arg_(arg), type_(type) {} - - void visit_bool(bool value) - { - if (type_ != 's') - visit_any_int(value); - } - - template - void visit_any_int(U value) - { - bool is_signed = type_ == 'd' || type_ == 'i'; - using internal::Arg; - typedef typename internal::Conditional< - is_same::value, U, T>::type TargetType; - if (sizeof(TargetType) <= sizeof(int)) - { - // Extra casts are used to silence warnings. - if (is_signed) - { - arg_.type = Arg::INT; - arg_.int_value = static_cast(static_cast(value)); - } - else - { - arg_.type = Arg::UINT; - typedef typename internal::MakeUnsigned::Type Unsigned; - arg_.uint_value = static_cast(static_cast(value)); - } - } - else - { - if (is_signed) - { - arg_.type = Arg::LONG_LONG; - // glibc's printf doesn't sign extend arguments of smaller types: - // std::printf("%lld", -42); // prints "4294967254" - // but we don't have to do the same because it's a UB. - arg_.long_long_value = static_cast(value); - } - else - { - arg_.type = Arg::ULONG_LONG; - arg_.ulong_long_value = - static_cast::Type>(value); - } - } - } -}; - -// Converts an integer argument to char for printf. -class CharConverter : public ArgVisitor -{ -private: - internal::Arg &arg_; - - FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); - -public: - explicit CharConverter(internal::Arg &arg) : arg_(arg) {} - - template - void visit_any_int(T value) - { - arg_.type = internal::Arg::CHAR; - arg_.int_value = static_cast(value); - } -}; - -// Checks if an argument is a valid printf width specifier and sets -// left alignment if it is negative. -class WidthHandler : public ArgVisitor -{ -private: - FormatSpec &spec_; - - FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); - -public: - explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} - - void report_unhandled_arg() - { - FMT_THROW(FormatError("width is not integer")); - } - - template - unsigned visit_any_int(T value) - { - typedef typename internal::IntTraits::MainType UnsignedType; - UnsignedType width = static_cast(value); - if (internal::is_negative(value)) - { - spec_.align_ = ALIGN_LEFT; - width = 0 - width; - } - unsigned int_max = std::numeric_limits::max(); - if (width > int_max) - FMT_THROW(FormatError("number is too big")); - return static_cast(width); - } -}; -} // namespace internal - -/** - \rst - A ``printf`` argument formatter based on the `curiously recurring template - pattern `_. - - To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some - or all of the visit methods with the same signatures as the methods in - `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. - Pass the subclass as the *Impl* template parameter. When a formatting - function processes an argument, it will dispatch to a visit method - specific to the argument type. For example, if the argument type is - ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass - will be called. If the subclass doesn't contain a method with this signature, - then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its - superclass will be called. - \endrst - */ -template -class BasicPrintfArgFormatter : public internal::ArgFormatterBase -{ -private: - void write_null_pointer() - { - this->spec().type_ = 0; - this->write("(nil)"); - } - - typedef internal::ArgFormatterBase Base; - -public: - /** - \rst - Constructs an argument formatter object. - *writer* is a reference to the output writer and *spec* contains format - specifier information for standard argument types. - \endrst - */ - BasicPrintfArgFormatter(BasicWriter &writer, FormatSpec &spec) - : internal::ArgFormatterBase(writer, spec) {} - - /** Formats an argument of type ``bool``. */ - void visit_bool(bool value) - { - FormatSpec &fmt_spec = this->spec(); - if (fmt_spec.type_ != 's') - return this->visit_any_int(value); - fmt_spec.type_ = 0; - this->write(value); - } - - /** Formats a character. */ - void visit_char(int value) - { - const FormatSpec &fmt_spec = this->spec(); - BasicWriter &w = this->writer(); - if (fmt_spec.type_ && fmt_spec.type_ != 'c') - w.write_int(value, fmt_spec); - typedef typename BasicWriter::CharPtr CharPtr; - CharPtr out = CharPtr(); - if (fmt_spec.width_ > 1) - { - Char fill = ' '; - out = w.grow_buffer(fmt_spec.width_); - if (fmt_spec.align_ != ALIGN_LEFT) - { - std::fill_n(out, fmt_spec.width_ - 1, fill); - out += fmt_spec.width_ - 1; - } - else - { - std::fill_n(out + 1, fmt_spec.width_ - 1, fill); - } - } - else - { - out = w.grow_buffer(1); - } - *out = static_cast(value); - } - - /** Formats a null-terminated C string. */ - void visit_cstring(const char *value) - { - if (value) - Base::visit_cstring(value); - else if (this->spec().type_ == 'p') - write_null_pointer(); - else - this->write("(null)"); - } - - /** Formats a pointer. */ - void visit_pointer(const void *value) - { - if (value) - return Base::visit_pointer(value); - this->spec().type_ = 0; - write_null_pointer(); - } - - /** Formats an argument of a custom (user-defined) type. */ - void visit_custom(internal::Arg::CustomValue c) - { - BasicFormatter formatter(ArgList(), this->writer()); - const Char format_str[] = {'}', 0}; - const Char *format = format_str; - c.format(&formatter, c.value, &format); - } -}; - -/** The default printf argument formatter. */ -template -class PrintfArgFormatter - : public BasicPrintfArgFormatter, Char> -{ -public: - /** Constructs an argument formatter object. */ - PrintfArgFormatter(BasicWriter &w, FormatSpec &s) - : BasicPrintfArgFormatter, Char>(w, s) {} -}; - -/** This template formats data and writes the output to a writer. */ -template > -class PrintfFormatter : private internal::FormatterBase -{ -private: - BasicWriter &writer_; - - void parse_flags(FormatSpec &spec, const Char *&s); - - // Returns the argument with specified index or, if arg_index is equal - // to the maximum unsigned value, the next argument. - internal::Arg get_arg( - const Char *s, - unsigned arg_index = (std::numeric_limits::max)()); - - // Parses argument index, flags and width and returns the argument index. - unsigned parse_header(const Char *&s, FormatSpec &spec); - -public: - /** - \rst - Constructs a ``PrintfFormatter`` object. References to the arguments and - the writer are stored in the formatter object so make sure they have - appropriate lifetimes. - \endrst - */ - explicit PrintfFormatter(const ArgList &args, BasicWriter &w) - : FormatterBase(args), writer_(w) {} - - /** Formats stored arguments and writes the output to the writer. */ - FMT_API void format(BasicCStringRef format_str); -}; - -template -void PrintfFormatter::parse_flags(FormatSpec &spec, const Char *&s) -{ - for (;;) - { - switch (*s++) - { - case '-': - spec.align_ = ALIGN_LEFT; - break; - case '+': - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '0': - spec.fill_ = '0'; - break; - case ' ': - spec.flags_ |= SIGN_FLAG; - break; - case '#': - spec.flags_ |= HASH_FLAG; - break; - default: - --s; - return; - } - } -} - -template -internal::Arg PrintfFormatter::get_arg(const Char *s, - unsigned arg_index) -{ - (void)s; - const char *error = 0; - internal::Arg arg = arg_index == std::numeric_limits::max() ? - next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); - if (error) - FMT_THROW(FormatError(!*s ? "invalid format string" : error)); - return arg; -} - -template -unsigned PrintfFormatter::parse_header( - const Char *&s, FormatSpec &spec) -{ - unsigned arg_index = std::numeric_limits::max(); - Char c = *s; - if (c >= '0' && c <= '9') - { - // Parse an argument index (if followed by '$') or a width possibly - // preceded with '0' flag(s). - unsigned value = internal::parse_nonnegative_int(s); - if (*s == '$') // value is an argument index - { - ++s; - arg_index = value; - } - else - { - if (c == '0') - spec.fill_ = '0'; - if (value != 0) - { - // Nonzero value means that we parsed width and don't need to - // parse it or flags again, so return now. - spec.width_ = value; - return arg_index; - } - } - } - parse_flags(spec, s); - // Parse width. - if (*s >= '0' && *s <= '9') - { - spec.width_ = internal::parse_nonnegative_int(s); - } - else if (*s == '*') - { - ++s; - spec.width_ = internal::WidthHandler(spec).visit(get_arg(s)); - } - return arg_index; -} - -template -void PrintfFormatter::format(BasicCStringRef format_str) -{ - const Char *start = format_str.c_str(); - const Char *s = start; - while (*s) - { - Char c = *s++; - if (c != '%') continue; - if (*s == c) - { - write(writer_, start, s); - start = ++s; - continue; - } - write(writer_, start, s - 1); - - FormatSpec spec; - spec.align_ = ALIGN_RIGHT; - - // Parse argument index, flags and width. - unsigned arg_index = parse_header(s, spec); - - // Parse precision. - if (*s == '.') - { - ++s; - if ('0' <= *s && *s <= '9') - { - spec.precision_ = static_cast(internal::parse_nonnegative_int(s)); - } - else if (*s == '*') - { - ++s; - spec.precision_ = internal::PrecisionHandler().visit(get_arg(s)); - } - } - - using internal::Arg; - Arg arg = get_arg(s, arg_index); - if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg)) - spec.flags_ &= ~internal::to_unsigned(HASH_FLAG); - if (spec.fill_ == '0') - { - if (arg.type <= Arg::LAST_NUMERIC_TYPE) - spec.align_ = ALIGN_NUMERIC; - else - spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. - } - - // Parse length and convert the argument to the required type. - using internal::ArgConverter; - switch (*s++) - { - case 'h': - if (*s == 'h') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'l': - if (*s == 'l') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'j': - ArgConverter(arg, *s).visit(arg); - break; - case 'z': - ArgConverter(arg, *s).visit(arg); - break; - case 't': - ArgConverter(arg, *s).visit(arg); - break; - case 'L': - // printf produces garbage when 'L' is omitted for long double, no - // need to do the same. - break; - default: - --s; - ArgConverter(arg, *s).visit(arg); - } - - // Parse type. - if (!*s) - FMT_THROW(FormatError("invalid format string")); - spec.type_ = static_cast(*s++); - if (arg.type <= Arg::LAST_INTEGER_TYPE) - { - // Normalize type. - switch (spec.type_) - { - case 'i': - case 'u': - spec.type_ = 'd'; - break; - case 'c': - // TODO: handle wchar_t - internal::CharConverter(arg).visit(arg); - break; - } - } - - start = s; - - // Format argument. - AF(writer_, spec).visit(arg); - } - write(writer_, start, s); -} - -template -void printf(BasicWriter &w, BasicCStringRef format, ArgList args) -{ - PrintfFormatter(args, w).format(format); -} - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = fmt::sprintf("The answer is %d", 42); - \endrst -*/ -inline std::string sprintf(CStringRef format, ArgList args) -{ - MemoryWriter w; - printf(w, format, args); - return w.str(); -} -FMT_VARIADIC(std::string, sprintf, CStringRef) - -inline std::wstring sprintf(WCStringRef format, ArgList args) -{ - WMemoryWriter w; - printf(w, format, args); - return w.str(); -} -FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) - -/** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - fmt::fprintf(stderr, "Don't %s!", "panic"); - \endrst - */ -FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args); -FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - fmt::printf("Elapsed time: %.2f seconds", 1.23); - \endrst - */ -inline int printf(CStringRef format, ArgList args) -{ - return fprintf(stdout, format, args); -} -FMT_VARIADIC(int, printf, CStringRef) - -/** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - fprintf(cerr, "Don't %s!", "panic"); - \endrst - */ -inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) -{ - MemoryWriter w; - printf(w, format_str, args); - internal::write(os, w); - return static_cast(w.size()); -} -FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) -} // namespace fmt - -#endif // FMT_PRINTF_H_ diff --git a/include/cpd/vendor/spdlog/fmt/fmt.h b/include/cpd/vendor/spdlog/fmt/fmt.h deleted file mode 100644 index dd035fd2..00000000 --- a/include/cpd/vendor/spdlog/fmt/fmt.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright(c) 2016 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// -// Include a bundled header-only copy of fmtlib or an external one. -// By default spdlog include its own copy. -// - -#if !defined(SPDLOG_FMT_EXTERNAL) - -#ifndef FMT_HEADER_ONLY -#define FMT_HEADER_ONLY -#endif -#ifndef FMT_USE_WINDOWS_H -#define FMT_USE_WINDOWS_H 0 -#endif -#include - -#else //external fmtlib - -#include - -#endif - diff --git a/include/cpd/vendor/spdlog/fmt/ostr.h b/include/cpd/vendor/spdlog/fmt/ostr.h deleted file mode 100644 index 7a651865..00000000 --- a/include/cpd/vendor/spdlog/fmt/ostr.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright(c) 2016 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// include external or bundled copy of fmtlib's ostream support -// -#if !defined(SPDLOG_FMT_EXTERNAL) -#include -#include -#else -#include -#endif - - diff --git a/include/cpd/vendor/spdlog/formatter.h b/include/cpd/vendor/spdlog/formatter.h deleted file mode 100644 index 0ffcec03..00000000 --- a/include/cpd/vendor/spdlog/formatter.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include - -#include -#include -#include - -namespace spdlog -{ -namespace details -{ -class flag_formatter; -} - -class formatter -{ -public: - virtual ~formatter() {} - virtual void format(details::log_msg& msg) = 0; -}; - -class pattern_formatter : public formatter -{ - -public: - explicit pattern_formatter(const std::string& pattern); - pattern_formatter(const pattern_formatter&) = delete; - pattern_formatter& operator=(const pattern_formatter&) = delete; - void format(details::log_msg& msg) override; -private: - const std::string _pattern; - std::vector> _formatters; - void handle_flag(char flag); - void compile_pattern(const std::string& pattern); -}; -} - -#include - diff --git a/include/cpd/vendor/spdlog/logger.h b/include/cpd/vendor/spdlog/logger.h deleted file mode 100644 index e998999e..00000000 --- a/include/cpd/vendor/spdlog/logger.h +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// Thread safe logger -// Has name, log level, vector of std::shared sink pointers and formatter -// Upon each log write the logger: -// 1. Checks if its log level is enough to log the message -// 2. Format the message using the formatter function -// 3. Pass the formatted message to its sinks to performa the actual logging - -#include -#include - -#include -#include -#include - -namespace spdlog -{ - -class logger -{ -public: - logger(const std::string& logger_name, sink_ptr single_sink); - logger(const std::string& name, sinks_init_list); - template - logger(const std::string& name, const It& begin, const It& end); - - virtual ~logger(); - logger(const logger&) = delete; - logger& operator=(const logger&) = delete; - - - template void log(level::level_enum lvl, const char* fmt, const Args&... args); - template void log(level::level_enum lvl, const char* msg); - template void trace(const char* fmt, const Args&... args); - template void debug(const char* fmt, const Args&... args); - template void info(const char* fmt, const Args&... args); - template void warn(const char* fmt, const Args&... args); - template void error(const char* fmt, const Args&... args); - template void critical(const char* fmt, const Args&... args); - - template void log(level::level_enum lvl, const T&); - template void trace(const T&); - template void debug(const T&); - template void info(const T&); - template void warn(const T&); - template void error(const T&); - template void critical(const T&); - - bool should_log(level::level_enum) const; - void set_level(level::level_enum); - level::level_enum level() const; - const std::string& name() const; - void set_pattern(const std::string&); - void set_formatter(formatter_ptr); - - // error handler - void set_error_handler(log_err_handler); - log_err_handler error_handler(); - - // automatically call flush() if message level >= log_level - void flush_on(level::level_enum log_level); - - virtual void flush(); - -protected: - virtual void _sink_it(details::log_msg&); - virtual void _set_pattern(const std::string&); - virtual void _set_formatter(formatter_ptr); - - // default error handler: print the error to stderr with the max rate of 1 message/minute - virtual void _default_err_handler(const std::string &msg); - - // return true if the given message level should trigger a flush - bool _should_flush_on(const details::log_msg&); - - const std::string _name; - std::vector _sinks; - formatter_ptr _formatter; - spdlog::level_t _level; - spdlog::level_t _flush_level; - log_err_handler _err_handler; - std::atomic _last_err_time; -}; -} - -#include - - diff --git a/include/cpd/vendor/spdlog/sinks/android_sink.h b/include/cpd/vendor/spdlog/sinks/android_sink.h deleted file mode 100644 index d8c97e03..00000000 --- a/include/cpd/vendor/spdlog/sinks/android_sink.h +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#if defined(__ANDROID__) - -#include - -#include -#include -#include - -namespace spdlog -{ -namespace sinks -{ - -/* -* Android sink (logging using __android_log_write) -* __android_log_write is thread-safe. No lock is needed. -*/ -class android_sink : public sink -{ -public: - explicit android_sink(const std::string& tag = "spdlog"): _tag(tag) {} - - void log(const details::log_msg& msg) override - { - const android_LogPriority priority = convert_to_android(msg.level); - // See system/core/liblog/logger_write.c for explanation of return value - const int ret = __android_log_write( - priority, _tag.c_str(), msg.formatted.c_str() - ); - if (ret < 0) - { - throw spdlog_ex("__android_log_write() failed", ret); - } - } - - void flush() override - { - } - -private: - static android_LogPriority convert_to_android(spdlog::level::level_enum level) - { - switch(level) - { - case spdlog::level::trace: - return ANDROID_LOG_VERBOSE; - case spdlog::level::debug: - return ANDROID_LOG_DEBUG; - case spdlog::level::info: - return ANDROID_LOG_INFO; - case spdlog::level::warn: - return ANDROID_LOG_WARN; - case spdlog::level::err: - return ANDROID_LOG_ERROR; - case spdlog::level::critical: - return ANDROID_LOG_FATAL; - default: - return ANDROID_LOG_DEFAULT; - } - } - - std::string _tag; -}; - -} -} - -#endif diff --git a/include/cpd/vendor/spdlog/sinks/ansicolor_sink.h b/include/cpd/vendor/spdlog/sinks/ansicolor_sink.h deleted file mode 100644 index a3b4292d..00000000 --- a/include/cpd/vendor/spdlog/sinks/ansicolor_sink.h +++ /dev/null @@ -1,116 +0,0 @@ -// -// Copyright(c) 2016 Kevin M. Godby (a modified version by spdlog). -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include - -#include -#include - -namespace spdlog -{ -namespace sinks -{ - -/** - * @brief The ansi_color_sink is a decorator around another sink and prefixes - * the output with an ANSI escape sequence color code depending on the severity - * of the message. - */ -class ansicolor_sink : public sink -{ -public: - ansicolor_sink(sink_ptr wrapped_sink); - virtual ~ansicolor_sink(); - - ansicolor_sink(const ansicolor_sink& other) = delete; - ansicolor_sink& operator=(const ansicolor_sink& other) = delete; - - virtual void log(const details::log_msg& msg) override; - virtual void flush() override; - - void set_color(level::level_enum level, const std::string& color); - - /// Formatting codes - const std::string reset = "\033[00m"; - const std::string bold = "\033[1m"; - const std::string dark = "\033[2m"; - const std::string underline = "\033[4m"; - const std::string blink = "\033[5m"; - const std::string reverse = "\033[7m"; - const std::string concealed = "\033[8m"; - - // Foreground colors - const std::string grey = "\033[30m"; - const std::string red = "\033[31m"; - const std::string green = "\033[32m"; - const std::string yellow = "\033[33m"; - const std::string blue = "\033[34m"; - const std::string magenta = "\033[35m"; - const std::string cyan = "\033[36m"; - const std::string white = "\033[37m"; - - /// Background colors - const std::string on_grey = "\033[40m"; - const std::string on_red = "\033[41m"; - const std::string on_green = "\033[42m"; - const std::string on_yellow = "\033[43m"; - const std::string on_blue = "\033[44m"; - const std::string on_magenta = "\033[45m"; - const std::string on_cyan = "\033[46m"; - const std::string on_white = "\033[47m"; - - -protected: - sink_ptr sink_; - std::map colors_; -}; - -inline ansicolor_sink::ansicolor_sink(sink_ptr wrapped_sink) : sink_(wrapped_sink) -{ - colors_[level::trace] = cyan; - colors_[level::debug] = cyan; - colors_[level::info] = bold; - colors_[level::warn] = yellow + bold; - colors_[level::err] = red + bold; - colors_[level::critical] = bold + on_red; - colors_[level::off] = reset; -} - -inline void ansicolor_sink::log(const details::log_msg& msg) -{ - // Wrap the originally formatted message in color codes - const std::string& prefix = colors_[msg.level]; - const std::string& s = msg.formatted.str(); - const std::string& suffix = reset; - details::log_msg m; - m.level = msg.level; - m.logger_name = msg.logger_name; - m.time = msg.time; - m.thread_id = msg.thread_id; - m.formatted << prefix << s << suffix; - sink_->log(m); -} - -inline void ansicolor_sink::flush() -{ - sink_->flush(); -} - -inline void ansicolor_sink::set_color(level::level_enum level, const std::string& color) -{ - colors_[level] = color; -} - -inline ansicolor_sink::~ansicolor_sink() -{ - flush(); -} - -} // namespace sinks -} // namespace spdlog - diff --git a/include/cpd/vendor/spdlog/sinks/base_sink.h b/include/cpd/vendor/spdlog/sinks/base_sink.h deleted file mode 100644 index 7f1a31db..00000000 --- a/include/cpd/vendor/spdlog/sinks/base_sink.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once -// -// base sink templated over a mutex (either dummy or realy) -// concrete implementation should only overrid the _sink_it method. -// all locking is taken care of here so no locking needed by the implementers.. -// - -#include -#include -#include -#include - -#include - -namespace spdlog -{ -namespace sinks -{ -template -class base_sink:public sink -{ -public: - base_sink():_mutex() {} - virtual ~base_sink() = default; - - base_sink(const base_sink&) = delete; - base_sink& operator=(const base_sink&) = delete; - - void log(const details::log_msg& msg) override - { - std::lock_guard lock(_mutex); - _sink_it(msg); - } - -protected: - virtual void _sink_it(const details::log_msg& msg) = 0; - Mutex _mutex; -}; -} -} diff --git a/include/cpd/vendor/spdlog/sinks/dist_sink.h b/include/cpd/vendor/spdlog/sinks/dist_sink.h deleted file mode 100644 index cef08bfb..00000000 --- a/include/cpd/vendor/spdlog/sinks/dist_sink.h +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright (c) 2015 David Schury, Gabi Melman -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include - -// Distribution sink (mux). Stores a vector of sinks which get called when log is called - -namespace spdlog -{ -namespace sinks -{ -template -class dist_sink: public base_sink -{ -public: - explicit dist_sink() :_sinks() {} - dist_sink(const dist_sink&) = delete; - dist_sink& operator=(const dist_sink&) = delete; - virtual ~dist_sink() = default; - -protected: - std::vector> _sinks; - - void _sink_it(const details::log_msg& msg) override - { - for (auto &sink : _sinks) - { - if( sink->should_log( msg.level)) - { - sink->log(msg); - } - } - } - -public: - void flush() override - { - std::lock_guard lock(base_sink::_mutex); - for (auto &sink : _sinks) - sink->flush(); - } - - void add_sink(std::shared_ptr sink) - { - std::lock_guard lock(base_sink::_mutex); - _sinks.push_back(sink); - } - - void remove_sink(std::shared_ptr sink) - { - std::lock_guard lock(base_sink::_mutex); - _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end()); - } -}; - -typedef dist_sink dist_sink_mt; -typedef dist_sink dist_sink_st; -} -} diff --git a/include/cpd/vendor/spdlog/sinks/file_sinks.h b/include/cpd/vendor/spdlog/sinks/file_sinks.h deleted file mode 100644 index fad21fe1..00000000 --- a/include/cpd/vendor/spdlog/sinks/file_sinks.h +++ /dev/null @@ -1,237 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace spdlog -{ -namespace sinks -{ -/* - * Trivial file sink with single file as target - */ -template -class simple_file_sink : public base_sink < Mutex > -{ -public: - explicit simple_file_sink(const filename_t &filename, bool truncate = false) - { - _file_helper.open(filename, truncate); - } - void flush() override - { - _file_helper.flush(); - } - -protected: - void _sink_it(const details::log_msg& msg) override - { - _file_helper.write(msg); - } -private: - details::file_helper _file_helper; -}; - -typedef simple_file_sink simple_file_sink_mt; -typedef simple_file_sink simple_file_sink_st; - -/* - * Rotating file sink based on size - */ -template -class rotating_file_sink : public base_sink < Mutex > -{ -public: - rotating_file_sink(const filename_t &base_filename, const filename_t &extension, - std::size_t max_size, std::size_t max_files ) : - _base_filename(base_filename), - _extension(extension), - _max_size(max_size), - _max_files(max_files), - _current_size(0), - _file_helper() - { - _file_helper.open(calc_filename(_base_filename, 0, _extension)); - _current_size = _file_helper.size(); //expensive. called only once - } - - void flush() override - { - _file_helper.flush(); - } - -protected: - void _sink_it(const details::log_msg& msg) override - { - _current_size += msg.formatted.size(); - if (_current_size > _max_size) - { - _rotate(); - _current_size = msg.formatted.size(); - } - _file_helper.write(msg); - } - -private: - static filename_t calc_filename(const filename_t& filename, std::size_t index, const filename_t& extension) - { - std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; - if (index) - w.write(SPDLOG_FILENAME_T("{}.{}.{}"), filename, index, extension); - else - w.write(SPDLOG_FILENAME_T("{}.{}"), filename, extension); - return w.str(); - } - - // Rotate files: - // log.txt -> log.1.txt - // log.1.txt -> log2.txt - // log.2.txt -> log3.txt - // log.3.txt -> delete - - void _rotate() - { - using details::os::filename_to_str; - _file_helper.close(); - for (auto i = _max_files; i > 0; --i) - { - filename_t src = calc_filename(_base_filename, i - 1, _extension); - filename_t target = calc_filename(_base_filename, i, _extension); - - if (details::file_helper::file_exists(target)) - { - if (details::os::remove(target) != 0) - { - throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno); - } - } - if (details::file_helper::file_exists(src) && details::os::rename(src, target)) - { - throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); - } - } - _file_helper.reopen(true); - } - filename_t _base_filename; - filename_t _extension; - std::size_t _max_size; - std::size_t _max_files; - std::size_t _current_size; - details::file_helper _file_helper; -}; - -typedef rotating_file_sink rotating_file_sink_mt; -typedef rotating_file_sinkrotating_file_sink_st; - -/* - * Default generator of daily log file names. - */ -struct default_daily_file_name_calculator -{ - // Create filename for the form basename.YYYY-MM-DD_hh-mm.extension - static filename_t calc_filename(const filename_t& basename, const filename_t& extension) - { - std::tm tm = spdlog::details::os::localtime(); - std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; - w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension); - return w.str(); - } -}; - -/* - * Generator of daily log file names in format basename.YYYY-MM-DD.extension - */ -struct dateonly_daily_file_name_calculator -{ - // Create filename for the form basename.YYYY-MM-DD.extension - static filename_t calc_filename(const filename_t& basename, const filename_t& extension) - { - std::tm tm = spdlog::details::os::localtime(); - std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; - w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension); - return w.str(); - } -}; - -/* - * Rotating file sink based on date. rotates at midnight - */ -template -class daily_file_sink :public base_sink < Mutex > -{ -public: - //create daily file sink which rotates on given time - daily_file_sink( - const filename_t& base_filename, - const filename_t& extension, - int rotation_hour, - int rotation_minute) : _base_filename(base_filename), - _extension(extension), - _rotation_h(rotation_hour), - _rotation_m(rotation_minute) - { - if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) - throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); - _rotation_tp = _next_rotation_tp(); - _file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension)); - } - - void flush() override - { - _file_helper.flush(); - } - -protected: - void _sink_it(const details::log_msg& msg) override - { - if (std::chrono::system_clock::now() >= _rotation_tp) - { - _file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension)); - _rotation_tp = _next_rotation_tp(); - } - _file_helper.write(msg); - } - -private: - std::chrono::system_clock::time_point _next_rotation_tp() - { - auto now = std::chrono::system_clock::now(); - time_t tnow = std::chrono::system_clock::to_time_t(now); - tm date = spdlog::details::os::localtime(tnow); - date.tm_hour = _rotation_h; - date.tm_min = _rotation_m; - date.tm_sec = 0; - auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date)); - if (rotation_time > now) - return rotation_time; - else - return std::chrono::system_clock::time_point(rotation_time + std::chrono::hours(24)); - } - - filename_t _base_filename; - filename_t _extension; - int _rotation_h; - int _rotation_m; - std::chrono::system_clock::time_point _rotation_tp; - details::file_helper _file_helper; -}; - -typedef daily_file_sink daily_file_sink_mt; -typedef daily_file_sink daily_file_sink_st; -} -} diff --git a/include/cpd/vendor/spdlog/sinks/msvc_sink.h b/include/cpd/vendor/spdlog/sinks/msvc_sink.h deleted file mode 100644 index 16342ca2..00000000 --- a/include/cpd/vendor/spdlog/sinks/msvc_sink.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright(c) 2016 Alexander Dalshov. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#if defined(_MSC_VER) - -#include -#include - -#include - -#include -#include - -namespace spdlog -{ -namespace sinks -{ -/* -* MSVC sink (logging using OutputDebugStringA) -*/ -template -class msvc_sink : public base_sink < Mutex > -{ -public: - explicit msvc_sink() - { - } - - void flush() override - { - } - -protected: - void _sink_it(const details::log_msg& msg) override - { - OutputDebugStringA(msg.formatted.c_str()); - } -}; - -typedef msvc_sink msvc_sink_mt; -typedef msvc_sink msvc_sink_st; - -} -} - -#endif diff --git a/include/cpd/vendor/spdlog/sinks/null_sink.h b/include/cpd/vendor/spdlog/sinks/null_sink.h deleted file mode 100644 index 68bd9c94..00000000 --- a/include/cpd/vendor/spdlog/sinks/null_sink.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include - -#include - -namespace spdlog -{ -namespace sinks -{ - -template -class null_sink : public base_sink < Mutex > -{ -protected: - void _sink_it(const details::log_msg&) override - {} - - void flush() override - {} - -}; -typedef null_sink null_sink_st; -typedef null_sink null_sink_mt; - -} -} - diff --git a/include/cpd/vendor/spdlog/sinks/ostream_sink.h b/include/cpd/vendor/spdlog/sinks/ostream_sink.h deleted file mode 100644 index feb5efa1..00000000 --- a/include/cpd/vendor/spdlog/sinks/ostream_sink.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include - -#include -#include - -namespace spdlog -{ -namespace sinks -{ -template -class ostream_sink: public base_sink -{ -public: - explicit ostream_sink(std::ostream& os, bool force_flush=false) :_ostream(os), _force_flush(force_flush) {} - ostream_sink(const ostream_sink&) = delete; - ostream_sink& operator=(const ostream_sink&) = delete; - virtual ~ostream_sink() = default; - -protected: - void _sink_it(const details::log_msg& msg) override - { - _ostream.write(msg.formatted.data(), msg.formatted.size()); - if (_force_flush) - _ostream.flush(); - } - - void flush() override - { - _ostream.flush(); - } - - std::ostream& _ostream; - bool _force_flush; -}; - -typedef ostream_sink ostream_sink_mt; -typedef ostream_sink ostream_sink_st; -} -} diff --git a/include/cpd/vendor/spdlog/sinks/sink.h b/include/cpd/vendor/spdlog/sinks/sink.h deleted file mode 100644 index d27fdbe0..00000000 --- a/include/cpd/vendor/spdlog/sinks/sink.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - - -#pragma once - -#include - -namespace spdlog -{ -namespace sinks -{ -class sink -{ -public: - sink(): _level( level::trace ) {} - - virtual ~sink() {} - virtual void log(const details::log_msg& msg) = 0; - virtual void flush() = 0; - - bool should_log(level::level_enum msg_level) const; - void set_level(level::level_enum log_level); - level::level_enum level() const; - -private: - level_t _level; - -}; - -inline bool sink::should_log(level::level_enum msg_level) const -{ - return msg_level >= _level.load(std::memory_order_relaxed); -} - -inline void sink::set_level(level::level_enum log_level) -{ - _level.store(log_level); -} - -inline level::level_enum sink::level() const -{ - return static_cast(_level.load(std::memory_order_relaxed)); -} - -} -} - diff --git a/include/cpd/vendor/spdlog/sinks/stdout_sinks.h b/include/cpd/vendor/spdlog/sinks/stdout_sinks.h deleted file mode 100644 index 30c19a54..00000000 --- a/include/cpd/vendor/spdlog/sinks/stdout_sinks.h +++ /dev/null @@ -1,75 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include - -#include -#include -#include - -namespace spdlog -{ -namespace sinks -{ - -template -class stdout_sink : public base_sink -{ - using MyType = stdout_sink; -public: - stdout_sink() {} - static std::shared_ptr instance() - { - static std::shared_ptr instance = std::make_shared(); - return instance; - } - - void _sink_it(const details::log_msg& msg) override - { - fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout); - flush(); - } - - void flush() override - { - fflush(stdout); - } -}; - -typedef stdout_sink stdout_sink_st; -typedef stdout_sink stdout_sink_mt; - - -template -class stderr_sink : public base_sink -{ - using MyType = stderr_sink; -public: - stderr_sink() {} - static std::shared_ptr instance() - { - static std::shared_ptr instance = std::make_shared(); - return instance; - } - - void _sink_it(const details::log_msg& msg) override - { - fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr); - flush(); - } - - void flush() override - { - fflush(stderr); - } -}; - -typedef stderr_sink stderr_sink_mt; -typedef stderr_sink stderr_sink_st; -} -} diff --git a/include/cpd/vendor/spdlog/sinks/syslog_sink.h b/include/cpd/vendor/spdlog/sinks/syslog_sink.h deleted file mode 100644 index 0d8633c5..00000000 --- a/include/cpd/vendor/spdlog/sinks/syslog_sink.h +++ /dev/null @@ -1,81 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include - -#ifdef SPDLOG_ENABLE_SYSLOG - -#include -#include - -#include -#include -#include - - -namespace spdlog -{ -namespace sinks -{ -/** - * Sink that write to syslog using the `syscall()` library call. - * - * Locking is not needed, as `syslog()` itself is thread-safe. - */ -class syslog_sink : public sink -{ -public: - // - syslog_sink(const std::string& ident = "", int syslog_option=0, int syslog_facility=LOG_USER): - _ident(ident) - { - _priorities[static_cast(level::trace)] = LOG_DEBUG; - _priorities[static_cast(level::debug)] = LOG_DEBUG; - _priorities[static_cast(level::info)] = LOG_INFO; - _priorities[static_cast(level::warn)] = LOG_WARNING; - _priorities[static_cast(level::err)] = LOG_ERR; - _priorities[static_cast(level::critical)] = LOG_CRIT; - _priorities[static_cast(level::off)] = LOG_INFO; - - //set ident to be program name if empty - ::openlog(_ident.empty()? nullptr:_ident.c_str(), syslog_option, syslog_facility); - } - ~syslog_sink() - { - ::closelog(); - } - - syslog_sink(const syslog_sink&) = delete; - syslog_sink& operator=(const syslog_sink&) = delete; - - void log(const details::log_msg &msg) override - { - ::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str()); - } - - void flush() override - { - } - - -private: - std::array _priorities; - //must store the ident because the man says openlog might use the pointer as is and not a string copy - const std::string _ident; - - // - // Simply maps spdlog's log level to syslog priority level. - // - int syslog_prio_from_level(const details::log_msg &msg) const - { - return _priorities[static_cast(msg.level)]; - } -}; -} -} - -#endif diff --git a/include/cpd/vendor/spdlog/spdlog.h b/include/cpd/vendor/spdlog/spdlog.h deleted file mode 100644 index d4f4203e..00000000 --- a/include/cpd/vendor/spdlog/spdlog.h +++ /dev/null @@ -1,170 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -// spdlog main header file. -// see example.cpp for usage example - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -namespace spdlog -{ - -// -// Return an existing logger or nullptr if a logger with such name doesn't exist. -// example: spdlog::get("my_logger")->info("hello {}", "world"); -// -std::shared_ptr get(const std::string& name); - - -// -// Set global formatting -// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); -// -void set_pattern(const std::string& format_string); -void set_formatter(formatter_ptr f); - -// -// Set global logging level for -// -void set_level(level::level_enum log_level); - -// -// Set global error handler -// -void set_error_handler(log_err_handler); - -// -// Turn on async mode (off by default) and set the queue size for each async_logger. -// effective only for loggers created after this call. -// queue_size: size of queue (must be power of 2): -// Each logger will pre-allocate a dedicated queue with queue_size entries upon construction. -// -// async_overflow_policy (optional, block_retry by default): -// async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry. -// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows. -// -// worker_warmup_cb (optional): -// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity) -// -// worker_teardown_cb (optional): -// callback function that will be called in worker thread upon exit -// -void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function& worker_teardown_cb = nullptr); - -// Turn off async mode -void set_sync_mode(); - - -// -// Create and register multi/single threaded basic file logger. -// Basic logger simply writes to given file without any limitatons or rotations. -// -std::shared_ptr basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false); -std::shared_ptr basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false); - -// -// Create and register multi/single threaded rotating file logger -// -std::shared_ptr rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files); -std::shared_ptr rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files); - -// -// Create file logger which creates new file on the given time (default in midnight): -// -std::shared_ptr daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0); -std::shared_ptr daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0); - -// -// Create and register stdout/stderr loggers -// -std::shared_ptr stdout_logger_mt(const std::string& logger_name, bool color = false); -std::shared_ptr stdout_logger_st(const std::string& logger_name, bool color = false); -std::shared_ptr stderr_logger_mt(const std::string& logger_name, bool color = false); -std::shared_ptr stderr_logger_st(const std::string& logger_name, bool color = false); - - -// -// Create and register a syslog logger -// -#ifdef SPDLOG_ENABLE_SYSLOG -std::shared_ptr syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0); -#endif - -#if defined(__ANDROID__) -std::shared_ptr android_logger(const std::string& logger_name, const std::string& tag = "spdlog"); -#endif - -// Create and register a logger a single sink -std::shared_ptr create(const std::string& logger_name, const sink_ptr& sink); - -// Create and register a logger with multiple sinks -std::shared_ptr create(const std::string& logger_name, sinks_init_list sinks); -template -std::shared_ptr create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end); - - -// Create and register a logger with templated sink type -// Example: -// spdlog::create("mylog", "dailylog_filename", "txt"); -template -std::shared_ptr create(const std::string& logger_name, Args...); - - -// Register the given logger with the given name -void register_logger(std::shared_ptr logger); - -// Apply a user defined function on all registered loggers -// Example: -// spdlog::apply_all([&](std::shared_ptr l) {l->flush();}); -void apply_all(std::function)> fun); - -// Drop the reference to the given logger -void drop(const std::string &name); - -// Drop all references from the registry -void drop_all(); - - -/////////////////////////////////////////////////////////////////////////////// -// -// Trace & Debug can be switched on/off at compile time for zero cost debug statements. -// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable. -// SPDLOG_TRACE(..) will also print current file and line. -// -// Example: -// spdlog::set_level(spdlog::level::trace); -// SPDLOG_TRACE(my_logger, "some trace message"); -// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2); -// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4); -/////////////////////////////////////////////////////////////////////////////// - -#ifdef SPDLOG_TRACE_ON -#define SPDLOG_STR_H(x) #x -#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) -#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__) -#else -#define SPDLOG_TRACE(logger, ...) -#endif - -#ifdef SPDLOG_DEBUG_ON -#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) -#else -#define SPDLOG_DEBUG(logger, ...) -#endif - - -} - - -#include diff --git a/include/cpd/vendor/spdlog/tweakme.h b/include/cpd/vendor/spdlog/tweakme.h deleted file mode 100644 index 1af539bf..00000000 --- a/include/cpd/vendor/spdlog/tweakme.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -/////////////////////////////////////////////////////////////////////////////// -// -// Edit this file to squeeze more performance, and to customize supported features -// -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used. -// This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ. -// Uncomment to use it instead of the regular clock. -// -// #define SPDLOG_CLOCK_COARSE -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment if date/time logging is not needed and never appear in the log pattern. -// This will prevent spdlog from quering the clock on each log call. -// -// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined. -// You must set new pattern(spdlog::set_pattern(..") without any date/time in it -// -// #define SPDLOG_NO_DATETIME -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern). -// This will prevent spdlog from quering the thread id on each log call. -// -// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined. -// -// #define SPDLOG_NO_THREAD_ID -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment if logger name logging is not needed. -// This will prevent spdlog from copying the logger name on each log call. -// -// #define SPDLOG_NO_NAME -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros. -// -// #define SPDLOG_DEBUG_ON -// #define SPDLOG_TRACE_ON -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()). -// Use only if your code never modifes concurrently the registry. -// Note that upon creating a logger the registry is modified by spdlog.. -// -// #define SPDLOG_NO_REGISTRY_MUTEX -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to avoid spdlog's usage of atomic log levels -// Use only if your code never modifies a logger's log levels concurrently by different threads. -// -// #define SPDLOG_NO_ATOMIC_LEVELS -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to enable usage of wchar_t for file names on Windows. -// -// #define SPDLOG_WCHAR_FILENAMES -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows) -// -// #define SPDLOG_EOL ";-)\n" -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to use your own copy of the fmt library instead of spdlog's copy. -// In this case spdlog will try to include so set your -I flag accordingly. -// -// #define SPDLOG_FMT_EXTERNAL -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to enable syslog (disabled by default) -// -// #define SPDLOG_ENABLE_SYSLOG -/////////////////////////////////////////////////////////////////////////////// - - diff --git a/scripts/travis-install-cmake.sh b/scripts/travis-install-cmake.sh index 16f7f918..d50286a9 100755 --- a/scripts/travis-install-cmake.sh +++ b/scripts/travis-install-cmake.sh @@ -3,6 +3,6 @@ set -ex -wget https://github.com/Viq111/travis-container-packets/releases/download/cmake-2.8.12/cmake.tar.bz2 +wget https://github.com/Viq111/travis-container-packets/releases/download/cmake-3.1.2/cmake.tar.bz2 tar xjf cmake.tar.bz2 rm cmake.tar.bz2 diff --git a/scripts/travis-install-jsoncpp.sh b/scripts/travis-install-jsoncpp.sh new file mode 100755 index 00000000..67a86ffb --- /dev/null +++ b/scripts/travis-install-jsoncpp.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env sh +# Installs jsoncpp to $HOME/local + +set -ex + +wget https://github.com/open-source-parsers/jsoncpp/archive/1.8.0.tar.gz +tar xzf 1.8.0.tar.gz +rm 1.8.0.tar.gz +cd jsoncpp-1.8.0 +mkdir build +cd build +cmake .. \ + -DCMAKE_INSTALL_PREFIX=$HOME/local \ + -DBUILD_SHARED_LIBS=ON \ + -DJSONCPP_WITH_CMAKE_PACKAGE=ON \ + -DJSONCPP_WITH_TESTS=OFF \ + -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF +make install diff --git a/scripts/travis-script.sh b/scripts/travis-script.sh index bef4fda4..c2b8723a 100755 --- a/scripts/travis-script.sh +++ b/scripts/travis-script.sh @@ -11,6 +11,8 @@ cmake .. \ -DCMAKE_INSTALL_PREFIX=$HOME/local \ -DWITH_DOCS=ON \ -DWITH_FGT=$CPD_WITH_FGT \ + -DWITH_JSONCPP=ON \ + -DWITH_STRICT_WARNINGS=ON \ -DWITH_TESTS=ON make CTEST_OUTPUT_ON_FAIULRE=1 make test diff --git a/src/affine.cpp b/src/affine.cpp index 0016920e..f8bc35ea 100644 --- a/src/affine.cpp +++ b/src/affine.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,18 +15,13 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/affine.hpp" -#include "cpd/runner.hpp" +#include namespace cpd { -Affine::Result affine(const Matrix& fixed, const Matrix& moving) { - return run(fixed, moving); -} - -Affine::Result Affine::compute(const Matrix& fixed, const Matrix& moving, - const Probabilities& probabilities, - double sigma2) const { +AffineResult Affine::compute_one(const Matrix& fixed, const Matrix& moving, + const Probabilities& probabilities, + double sigma2) const { double np = probabilities.p1.sum(); Vector mu_x = fixed.transpose() * probabilities.pt1 / np; Vector mu_y = moving.transpose() * probabilities.p1 / np; @@ -38,7 +33,7 @@ Affine::Result Affine::compute(const Matrix& fixed, const Matrix& moving, .matrix() * moving - np * mu_y * mu_y.transpose(); - Affine::Result result; + AffineResult result; result.transform = b1 * b2.inverse(); result.translation = mu_x - result.transform * mu_y; result.sigma2 = @@ -53,14 +48,14 @@ Affine::Result Affine::compute(const Matrix& fixed, const Matrix& moving, return result; } -void Affine::denormalize(const Normalization& normalization, - Result& result) const { - result.translation = normalization.scale * result.translation + - normalization.fixed_mean - - result.transform * normalization.moving_mean; - result.points = - result.points * normalization.scale + - normalization.fixed_mean.transpose().replicate(result.points.rows(), 1); - return; +void AffineResult::denormalize(const Normalization& normalization) { + Result::denormalize(normalization); + translation = normalization.scale * translation + normalization.fixed_mean - + transform * normalization.moving_mean; +} + +AffineResult affine(const Matrix& fixed, const Matrix& moving) { + Affine affine; + return affine.run(fixed, moving); } } diff --git a/src/comparer/base.cpp b/src/comparer/base.cpp deleted file mode 100644 index 6f74a27a..00000000 --- a/src/comparer/base.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -#include "cpd/comparer/base.hpp" -#include "cpd/comparer/direct.hpp" -#include "cpd/exceptions.hpp" -#ifdef CPD_WITH_FGT -#include "cpd/comparer/fgt.hpp" -#endif - -namespace cpd { - -std::unique_ptr Comparer::create() { - return std::unique_ptr(new DirectComparer()); -} - -std::unique_ptr Comparer::from_name(const std::string& name) { - if (name == "direct") { - return std::unique_ptr(new DirectComparer()); - } -#ifdef CPD_WITH_FGT - if (name == "fgt") { - return std::unique_ptr(new FgtComparer()); - } -#endif - throw unknown_comparer(name); -} -} diff --git a/src/comparer/direct.cpp b/src/gauss_transform.cpp similarity index 86% rename from src/comparer/direct.cpp rename to src/gauss_transform.cpp index 612ae9c3..205e19b6 100644 --- a/src/comparer/direct.cpp +++ b/src/gauss_transform.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,12 +15,12 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/comparer/direct.hpp" +#include namespace cpd { - -Probabilities DirectComparer::compute(const Matrix& fixed, const Matrix& moving, - double sigma2, double outliers) const { +Probabilities GaussTransformDirect::compute(const Matrix& fixed, + const Matrix& moving, double sigma2, + double outliers) const { double ksig = -2.0 * sigma2; size_t cols = fixed.cols(); double outlier_tmp = diff --git a/src/comparer/fgt.cpp b/src/gauss_transform_fgt.cpp similarity index 82% rename from src/comparer/fgt.cpp rename to src/gauss_transform_fgt.cpp index 94b2590f..b00bcebc 100644 --- a/src/comparer/fgt.cpp +++ b/src/gauss_transform_fgt.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,33 +15,16 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/comparer/fgt.hpp" +#include namespace cpd { - -std::unique_ptr FgtComparer::create_transform( - const Matrix& points, double bandwidth) const { - switch (m_method) { - case FgtMethod::DirectTree: - return std::unique_ptr( - new fgt::DirectTree(points, bandwidth, m_epsilon)); - case FgtMethod::Ifgt: - return std::unique_ptr( - new fgt::Ifgt(points, bandwidth, m_epsilon)); - case FgtMethod::Switched: - if (bandwidth > m_breakpoint) { - return std::unique_ptr( - new fgt::Ifgt(points, bandwidth, m_epsilon)); - } else { - return std::unique_ptr( - new fgt::DirectTree(points, bandwidth, m_epsilon)); - } - } - return std::unique_ptr(); +std::unique_ptr GaussTransform::make_default() { + return std::unique_ptr(new GaussTransformFgt()); } -Probabilities FgtComparer::compute(const Matrix& fixed, const Matrix& moving, - double sigma2, double outliers) const { +Probabilities GaussTransformFgt::compute(const Matrix& fixed, + const Matrix& moving, double sigma2, + double outliers) const { double bandwidth = std::sqrt(2.0 * sigma2); size_t cols = fixed.cols(); std::unique_ptr transform = @@ -61,4 +44,24 @@ Probabilities FgtComparer::compute(const Matrix& fixed, const Matrix& moving, -denom_p.log().sum() + cols * fixed.rows() * std::log(sigma2) / 2; return { p1, pt1, px, l }; } + +std::unique_ptr GaussTransformFgt::create_transform( + const Matrix& points, double bandwidth) const { + switch (m_method) { + case FgtMethod::DirectTree: + return std::unique_ptr( + new fgt::DirectTree(points, bandwidth, m_epsilon)); + case FgtMethod::Ifgt: + return std::unique_ptr( + new fgt::Ifgt(points, bandwidth, m_epsilon)); + case FgtMethod::Switched: + if (bandwidth > m_breakpoint) { + return std::unique_ptr( + new fgt::Ifgt(points, bandwidth, m_epsilon)); + } else { + return std::unique_ptr( + new fgt::DirectTree(points, bandwidth, m_epsilon)); + } + } +} } diff --git a/include/cpd/logging.hpp b/src/gauss_transform_make_default.cpp similarity index 76% rename from include/cpd/logging.hpp rename to src/gauss_transform_make_default.cpp index 403aa5a9..b9272a4d 100644 --- a/include/cpd/logging.hpp +++ b/src/gauss_transform_make_default.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,13 +15,10 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -/// \file -/// Logging utilities. - -#pragma once +#include namespace cpd { - -/// The name used for all cpd logs. -const char* const LOGGER_NAME = "cpd"; +std::unique_ptr GaussTransform::make_default() { + return std::unique_ptr(new GaussTransformDirect()); +} } diff --git a/src/nonrigid.cpp b/src/nonrigid.cpp index 4e8c3183..ee8cb4a1 100644 --- a/src/nonrigid.cpp +++ b/src/nonrigid.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,13 +15,11 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/nonrigid.hpp" -#include "affinity.hpp" -#include "cpd/runner.hpp" +#include namespace cpd { -void Nonrigid::init(const Matrix&, const Matrix& moving) { +void Nonrigid::init(const Matrix& fixed, const Matrix& moving) { m_g = affinity(moving, moving, m_beta); m_w = Matrix::Zero(moving.rows(), moving.cols()); } @@ -30,17 +28,18 @@ void Nonrigid::modify_probabilities(Probabilities& probabilities) const { probabilities.l += m_lambda / 2.0 * (m_w.transpose() * m_g * m_w).trace(); } -Nonrigid::Result Nonrigid::compute(const Matrix& fixed, const Matrix& moving, - const Probabilities& probabilities, - double sigma2) { +NonrigidResult Nonrigid::compute_one(const Matrix& fixed, const Matrix& moving, + const Probabilities& probabilities, + double sigma2) const { size_t cols = fixed.cols(); auto dp = probabilities.p1.asDiagonal(); - m_w = (dp * m_g + - m_lambda * sigma2 * Matrix::Identity(moving.rows(), moving.rows())) - .colPivHouseholderQr() - .solve(probabilities.px - dp * moving); - Nonrigid::Result result; - result.points = moving + m_g * m_w; + Matrix w = + (dp * m_g + + m_lambda * sigma2 * Matrix::Identity(moving.rows(), moving.rows())) + .colPivHouseholderQr() + .solve(probabilities.px - dp * moving); + NonrigidResult result; + result.points = moving + m_g * w; double np = probabilities.p1.sum(); result.sigma2 = std::abs( ((fixed.array().pow(2) * probabilities.pt1.replicate(1, cols).array()) @@ -53,14 +52,8 @@ Nonrigid::Result Nonrigid::compute(const Matrix& fixed, const Matrix& moving, return result; } -void Nonrigid::denormalize(const Normalization& normalization, - Nonrigid::Result& result) const { - result.points = - result.points * normalization.scale + - normalization.fixed_mean.transpose().replicate(result.points.rows(), 1); -} - -Nonrigid::Result nonrigid(const Matrix& fixed, const Matrix& moving) { - return run(fixed, moving); +NonrigidResult nonrigid(const Matrix& fixed, const Matrix& moving) { + Nonrigid nonrigid; + return nonrigid.run(fixed, moving); } } diff --git a/src/affinity.cpp b/src/normalization.cpp similarity index 58% rename from src/affinity.cpp rename to src/normalization.cpp index 925cf96f..d46cd178 100644 --- a/src/affinity.cpp +++ b/src/normalization.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,22 +15,18 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "affinity.hpp" +#include namespace cpd { -Matrix affinity(const Matrix& x, const Matrix& y, double beta) { - double k = -2.0 * beta * beta; - size_t x_rows = x.rows(); - size_t y_rows = y.rows(); - Matrix g(x_rows, y_rows); - for (size_t i = 0; i < y_rows; ++i) { - g.col(i) = ((x.array() - y.row(i).replicate(x_rows, 1).array()) - .pow(2) - .rowwise() - .sum() / - k).exp(); - } - return g; +Normalization::Normalization(const Matrix& f, const Matrix& m) + : fixed_mean(f.colwise().mean()) + , fixed(f - fixed_mean.transpose().replicate(f.rows(), 1)) + , moving_mean(m.colwise().mean()) + , moving(m - moving_mean.transpose().replicate(m.rows(), 1)) + , scale(std::max(std::sqrt(fixed.array().pow(2).sum() / fixed.rows()), + std::sqrt(moving.array().pow(2).sum() / moving.rows()))) { + fixed /= scale; + moving /= scale; } } diff --git a/src/normalize.cpp b/src/normalize.cpp deleted file mode 100644 index ab51bcd1..00000000 --- a/src/normalize.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -#include - -namespace cpd { - -Normalization normalize(const Matrix& fixed, const Matrix& moving) { - Normalization normalization; - normalization.fixed_mean = fixed.colwise().mean(); - normalization.fixed = - fixed - normalization.fixed_mean.transpose().replicate(fixed.rows(), 1); - normalization.moving_mean = moving.colwise().mean(); - normalization.moving = - moving - - normalization.moving_mean.transpose().replicate(moving.rows(), 1); - double fixed_scale = - std::sqrt(normalization.fixed.array().pow(2).sum() / fixed.rows()); - double moving_scale = - std::sqrt(normalization.moving.array().pow(2).sum() / moving.rows()); - normalization.scale = std::max(fixed_scale, moving_scale); - normalization.fixed /= normalization.scale; - normalization.moving /= normalization.scale; - return normalization; -} -} diff --git a/src/rigid.cpp b/src/rigid.cpp index 36c3589a..3a6ea9c8 100644 --- a/src/rigid.cpp +++ b/src/rigid.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,14 +15,14 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/rigid.hpp" -#include "cpd/probabilities.hpp" -#include "cpd/runner.hpp" +#include +#include namespace cpd { -Rigid::Result Rigid::compute(const Matrix& fixed, const Matrix& moving, - const Probabilities& probabilities, double) const { +RigidResult Rigid::compute_one(const Matrix& fixed, const Matrix& moving, + const Probabilities& probabilities, + double sigma2) const { size_t cols = fixed.cols(); double np = probabilities.pt1.sum(); Vector mu_x = fixed.transpose() * probabilities.pt1 / np; @@ -33,11 +33,11 @@ Rigid::Result Rigid::compute(const Matrix& fixed, const Matrix& moving, a, Eigen::ComputeThinU | Eigen::ComputeThinV); Matrix s = svd.singularValues().asDiagonal(); Matrix c = Matrix::Identity(cols, cols); - if (!m_allow_reflections) { + if (!m_reflections) { c(cols - 1, cols - 1) = (svd.matrixU() * svd.matrixV().transpose()).determinant(); } - Rigid::Result result; + RigidResult result; result.rotation = svd.matrixU() * c * svd.matrixV().transpose(); if (m_scale) { result.scale = @@ -70,29 +70,14 @@ Rigid::Result Rigid::compute(const Matrix& fixed, const Matrix& moving, return result; } -void Rigid::denormalize(const Normalization& normalization, - Rigid::Result& result) const { - result.translation = - normalization.scale * result.translation + normalization.fixed_mean - - result.scale * result.rotation * normalization.moving_mean; - result.points = - result.points * normalization.scale + - normalization.fixed_mean.transpose().replicate(result.points.rows(), 1); +RigidResult rigid(const Matrix& fixed, const Matrix& moving) { + Rigid rigid; + return rigid.run(fixed, moving); } -Rigid::Result rigid(const Matrix& fixed, const Matrix& moving) { - return run(fixed, moving); -} - -std::ostream& operator<<(std::ostream& stream, const Rigid::Result& result) { - Eigen::IOFormat json_format(Eigen::StreamPrecision, 0, ", ", ",", "[", "]", - "[", "]"); - stream << "{\n" - << " \"translation\": " - << result.translation.transpose().format(json_format) - << ",\n \"rotation\": " - << result.rotation.transpose().format(json_format) - << ",\n \"scale\": " << result.scale << "\n}"; - return stream; +void RigidResult::denormalize(const Normalization& normalization) { + Result::denormalize(normalization); + translation = normalization.scale * translation + normalization.fixed_mean - + scale * rotation * normalization.moving_mean; } } diff --git a/src/transform.cpp b/src/transform.cpp new file mode 100644 index 00000000..f72628fa --- /dev/null +++ b/src/transform.cpp @@ -0,0 +1,26 @@ +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include + +namespace cpd { + +void Result::denormalize(const Normalization& normalization) { + points = points * normalization.scale + + normalization.fixed_mean.transpose().replicate(points.rows(), 1); +} +} diff --git a/src/utils.cpp b/src/utils.cpp index eb9ad868..5de1242a 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -71,4 +71,19 @@ double default_sigma2(const Matrix& fixed, const Matrix& moving) { 2 * fixed.colwise().sum() * moving.colwise().sum().transpose()) / (fixed.rows() * moving.rows() * fixed.cols()); } + +Matrix affinity(const Matrix& x, const Matrix& y, double beta) { + double k = -2.0 * beta * beta; + size_t x_rows = x.rows(); + size_t y_rows = y.rows(); + Matrix g(x_rows, y_rows); + for (size_t i = 0; i < y_rows; ++i) { + g.col(i) = ((x.array() - y.row(i).replicate(x_rows, 1).array()) + .pow(2) + .rowwise() + .sum() / + k).exp(); + } + return g; +} } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index a1ae4056..00000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -configure_file(support.hpp.in "${CMAKE_CURRENT_BINARY_DIR}/support.hpp") - -find_package(Threads) - -function(cpd_test name) - set(src ${name}.cpp) - string(REPLACE "/" "-" name ${name}) - set(target ${name}-test) - add_executable(${target} ${src}) - set_target_properties(${target} PROPERTIES OUTPUT_NAME ${name}) - add_test(NAME ${name} COMMAND ${target}) - target_link_libraries(${target} PRIVATE Library-C++ gtest_main) - target_include_directories(${target} PRIVATE - "${GOOGLETEST_DIR}/include" - "${PROJECT_SOURCE_DIR}/src" - "${PROJECT_SOURCE_DIR}/include" - "${PROJECT_SOURCE_DIR}/include/cpd/vendor" - "${CMAKE_CURRENT_BINARY_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}" - ) - - if(CMAKE_USE_PTHREADS_INIT) - target_link_libraries(${target} PRIVATE ${CMAKE_THREAD_LIBS_INIT}) - endif() -endfunction() - -cpd_test(affine) -cpd_test(affinity) -cpd_test(comparer/base) -cpd_test(comparer/direct) -if(WITH_FGT) - cpd_test(comparer/fgt) -endif() -cpd_test(nonrigid) -cpd_test(normalize) -cpd_test(rigid) -cpd_test(runner) -cpd_test(utils) -cpd_test(version) diff --git a/test/comparer/direct.cpp b/test/comparer/direct.cpp deleted file mode 100644 index 6d62e32a..00000000 --- a/test/comparer/direct.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -#include "cpd/comparer/direct.hpp" -#include "comparer/common.hpp" -#include "support.hpp" -#include "gtest/gtest.h" - -namespace cpd { - -TEST(DirectComparer, 2D) { - DirectComparer comparer; - auto fish = test_data_matrix("fish.csv"); - auto fish_distorted = test_data_matrix("fish-distorted.csv"); - Probabilities probabilities = - comparer.compute(fish, fish_distorted, 1.0, 0.1); - check_fish_probabilities(probabilities); -} - -TEST(DirectComparer, 3D) { - DirectComparer comparer; - auto face = test_data_matrix("face.csv"); - auto face_distorted = test_data_matrix("face-distorted.csv"); - Probabilities probabilities = - comparer.compute(face, face_distorted, 1.0, 0.1); - check_face_probabilities(probabilities); -} - -TEST(DirectComparer, Correspondence) { - DirectComparer comparer; - auto fish = test_data_matrix("fish.csv"); - auto fish_distorted = test_data_matrix("fish-distorted.csv"); - IndexVector correspondence = - test_data_matrix("fish-correspondence.csv").cast(); - Probabilities probabilities = - comparer.compute(fish, fish_distorted, 1.0, 0.1); - EXPECT_EQ(correspondence.size(), probabilities.correspondence.size()); - EXPECT_EQ(correspondence, probabilities.correspondence); -} -} diff --git a/test/comparer/fgt.cpp b/test/comparer/fgt.cpp deleted file mode 100644 index ab363c54..00000000 --- a/test/comparer/fgt.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -#include "cpd/comparer/fgt.hpp" -#include "comparer/common.hpp" -#include "support.hpp" -#include "gtest/gtest.h" - -namespace cpd { - -TEST(FgtComparer, 2D) { - FgtComparer comparer; - auto fish = test_data_matrix("fish.csv"); - auto fish_distorted = test_data_matrix("fish-distorted.csv"); - Probabilities probabilities = - comparer.compute(fish, fish_distorted, 1.0, 0.1); - check_fish_probabilities(probabilities); -} - -TEST(FgtComparer, 3D) { - FgtComparer comparer; - auto face = test_data_matrix("face.csv"); - auto face_distorted = test_data_matrix("face-distorted.csv"); - Probabilities probabilities = - comparer.compute(face, face_distorted, 1.0, 0.1); - check_face_probabilities(probabilities); -} -} diff --git a/test/data/README.md b/test/data/README.md deleted file mode 100644 index 86b5215f..00000000 --- a/test/data/README.md +++ /dev/null @@ -1,6 +0,0 @@ -The following test files were taken from Myronenko's origional CPD repository: - -- fish.csv -- fish-distorted.csv -- face.csv -- face-distorted.csv diff --git a/test/rigid.cpp b/test/rigid.cpp deleted file mode 100644 index 70f9ee41..00000000 --- a/test/rigid.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -#include - -#include "cpd/comparer/direct.hpp" -#include "cpd/matrix.hpp" -#include "cpd/rigid.hpp" -#include "cpd/runner.hpp" -#include "support.hpp" -#include "gtest/gtest.h" - -namespace cpd { - -const double TOLERANCE = 1e-3; - -class FishRotationTest : public FishTest, - public ::testing::WithParamInterface { -public: - FishRotationTest() - : FishTest() - , m_rotation(Eigen::Rotation2D(M_PI * GetParam() / 180.0) - .toRotationMatrix()) { - m_fish_transformed = m_fish * m_rotation; - } - - Matrix m_rotation; -}; - -class FishTranslationTest : public FishTest, - public ::testing::WithParamInterface { -public: - FishTranslationTest() - : FishTest() - , m_translation(Eigen::Vector2d(GetParam(), GetParam())) { - m_fish_transformed = - m_fish + m_translation.transpose().replicate(m_fish.rows(), 1); - } - - Vector m_translation; -}; - -TEST_F(FishTest, AllowScaling) { - m_fish_transformed = test_data_matrix("fish-distorted.csv"); - DirectComparer comparer; - Probabilities probabilities = - comparer.compute(m_fish, m_fish_transformed, 1.0, 0.1); - Rigid rigid; - rigid.scale(true); - auto result = rigid.compute(m_fish, m_fish_transformed, probabilities, 1.0); - EXPECT_NEAR(0.4165, result.sigma2, 1e-2); -} - -TEST_F(FishTest, NoScaling) { - m_fish_transformed = test_data_matrix("fish-distorted.csv"); - DirectComparer computer; - Probabilities probabilities = - computer.compute(m_fish, m_fish_transformed, 1.0, 0.1); - Rigid rigid; - rigid.scale(false); - auto result = rigid.compute(m_fish, m_fish_transformed, probabilities, 1.0); - EXPECT_NEAR(0.5238, result.sigma2, 1e-2); -} - -TEST_F(FishTest, Normalize) { - m_fish = m_fish * 10.0; - Matrix rotation = - Eigen::Rotation2D(M_PI * 20.0 / 180.0).toRotationMatrix(); - m_fish_transformed = m_fish * rotation; - auto result = rigid(m_fish_transformed, m_fish); - EXPECT_TRUE(result.rotation.isApprox(rotation.transpose(), TOLERANCE)); - EXPECT_EQ(result.points.rows(), m_fish.rows()); -} - -TEST_F(FishTest, DifferentlySized) { - Runner runner; - runner.normalize(false); - Rigid::Result result = runner.run( - m_fish, m_fish.block(0, 0, m_fish.rows() - 1, m_fish.cols())); - EXPECT_EQ(m_fish.rows() - 1, result.points.rows()); -} - -TEST_P(FishRotationTest, IsRecovered) { - auto result = rigid(m_fish_transformed, m_fish); - EXPECT_TRUE(result.rotation.isApprox(m_rotation.transpose(), TOLERANCE)); -} - -TEST_P(FishTranslationTest, IsRecovered) { - auto result = rigid(m_fish_transformed, m_fish); - EXPECT_TRUE(result.translation.isApprox(m_translation, TOLERANCE)); -} - -INSTANTIATE_TEST_CASE_P(Fish, FishRotationTest, - ::testing::Range(0.0, 70.0, 10.0)); -INSTANTIATE_TEST_CASE_P(Fish, FishTranslationTest, ::testing::Range(1.0, 10.0)); - -class FaceRotationTest : public FaceTest, - public ::testing::WithParamInterface { -public: - FaceRotationTest() - : FaceTest() - , m_rotation(Eigen::AngleAxisd(GetParam() * M_PI / 180.0, - Eigen::Vector3d::UnitX()) - .toRotationMatrix()) { - m_face_transformed = m_face * m_rotation; - } - - Matrix m_rotation; -}; - -class FaceTranslationTest : public FaceTest, - public ::testing::WithParamInterface { -public: - FaceTranslationTest() - : FaceTest() - , m_translation(Eigen::Vector3d(GetParam(), GetParam(), GetParam())) { - m_face_transformed = - m_face + - m_translation.transpose().replicate(m_face_transformed.rows(), 1); - } - - Vector m_translation; -}; - -TEST_P(FaceRotationTest, IsRecovered) { - auto result = rigid(m_face_transformed, m_face); - EXPECT_TRUE(result.rotation.isApprox(m_rotation.transpose(), TOLERANCE)); -} - -TEST_P(FaceTranslationTest, IsRecovered) { - auto result = rigid(m_face_transformed, m_face); - EXPECT_TRUE(result.translation.isApprox(m_translation, TOLERANCE)); -} - -INSTANTIATE_TEST_CASE_P(Face, FaceRotationTest, - ::testing::Range(0.0, 70.0, 10.0)); -INSTANTIATE_TEST_CASE_P(Face, FaceTranslationTest, ::testing::Range(1.0, 10.0)); -} diff --git a/test/support.hpp.in b/test/support.hpp.in deleted file mode 100644 index 34c1fe35..00000000 --- a/test/support.hpp.in +++ /dev/null @@ -1,48 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -#include "cpd/utils.hpp" -#include "gtest/gtest.h" - -namespace cpd { - -Matrix test_data_matrix(const std::string& filename) { - return matrix_from_path("@PROJECT_SOURCE_DIR@/test/data/" + filename); -} - -class FishTest : public ::testing::Test { -public: - FishTest() - : ::testing::Test() - , m_fish(test_data_matrix("fish.csv")) - , m_fish_transformed(test_data_matrix("fish-distorted.csv")) {} - - Matrix m_fish; - Matrix m_fish_transformed; -}; - -class FaceTest : public ::testing::Test { -public: - FaceTest() - : ::testing::Test() - , m_face(test_data_matrix("face.csv")) - , m_face_transformed(test_data_matrix("face-distorted.csv")) {} - - Matrix m_face; - Matrix m_face_transformed; -}; -} diff --git a/test/utils.cpp b/test/utils.cpp deleted file mode 100644 index d2ad5c2c..00000000 --- a/test/utils.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -#include "cpd/utils.hpp" -#include "support.hpp" -#include "gtest/gtest.h" - -namespace cpd { - -TEST(Utils, DefaultSigma2) { - auto fish = test_data_matrix("fish.csv"); - auto fish_distorted = test_data_matrix("fish-distorted.csv"); - double sigma2 = default_sigma2(fish, fish_distorted); - EXPECT_NEAR(1.0, sigma2, 0.1); -} - -TEST(Utils, Sigma2SamePoints) { - auto fish = test_data_matrix("fish.csv"); - double sigma2 = default_sigma2(fish, fish); - EXPECT_NEAR(1.0, sigma2, 0.001); -} - -TEST(Utils, Sigma2Helheim) { - auto helheim = test_data_matrix("helheim.csv"); - double sigma2 = default_sigma2(helheim, helheim); - EXPECT_NEAR(23235, sigma2, 1.0); -} - -TEST(Utils, MatrixFromFile) { - auto fish = test_data_matrix("fish.csv"); - EXPECT_EQ(91, fish.rows()); - EXPECT_EQ(2, fish.cols()); - EXPECT_NEAR(-0.91542, fish(0, 0), 1e-5); - EXPECT_NEAR(-0.75806, fish(90, 1), 1e-5); -} -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..ea0d38e5 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,15 @@ +configure_file(support.hpp.in "${CMAKE_CURRENT_BINARY_DIR}/support.hpp") + +include(cpd_test) + +cpd_test(affine) +cpd_test(gauss_transform) +cpd_test(nonrigid) +cpd_test(normalization) +cpd_test(rigid) +cpd_test(utils) +cpd_test(version) + +if(WITH_FGT) + cpd_test(gauss_transform_fgt) +endif() diff --git a/test/affine.cpp b/tests/affine.cpp similarity index 67% rename from test/affine.cpp rename to tests/affine.cpp index 0d2dfc49..ab9176eb 100644 --- a/test/affine.cpp +++ b/tests/affine.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,21 +15,23 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/affine.hpp" -#include "support.hpp" +#include "fixtures/face.hpp" +#include "fixtures/fish.hpp" +#include "gtest/gtest.h" +#include namespace cpd { -TEST_F(FishTest, Affine) { +TEST_F(FishTest, MatchesReference) { Matrix transform = Matrix(2, 2); transform << 1.2688, 0.9169, 1.1294, 1.4311; Vector translation = Vector(2); - translation << 1.0, 2.0; - m_fish_transformed = m_fish * transform + - translation.transpose().replicate(m_fish.rows(), 1); - Affine::Result result = affine(m_fish_transformed, m_fish); + translation << 1., 2.; + m_fish_distorted = m_fish * transform + + translation.transpose().replicate(m_fish.rows(), 1); + AffineResult result = affine(m_fish_distorted, m_fish); ASSERT_EQ(result.points.rows(), m_fish.rows()); - EXPECT_TRUE(result.points.isApprox(m_fish_transformed, 1e-4)); + EXPECT_TRUE(result.points.isApprox(m_fish_distorted, 1e-4)); EXPECT_TRUE(result.transform.isApprox(transform.transpose(), 1e-4)); EXPECT_TRUE(result.translation.isApprox(translation, 1e-4)); } @@ -40,11 +42,11 @@ TEST_F(FaceTest, Affine) { 0.8303, 0.5005; Vector translation = Vector(3); translation << 1.0, 2.0, 3.0; - m_face_transformed = m_face * transform + - translation.transpose().replicate(m_face.rows(), 1); - Affine::Result result = affine(m_face_transformed, m_face); + m_face_distorted = m_face * transform + + translation.transpose().replicate(m_face.rows(), 1); + AffineResult result = affine(m_face_distorted, m_face); ASSERT_EQ(result.points.rows(), m_face.rows()); - EXPECT_TRUE(result.points.isApprox(m_face_transformed, 1e-4)); + EXPECT_TRUE(result.points.isApprox(m_face_distorted, 1e-4)); EXPECT_TRUE(result.transform.isApprox(transform.transpose(), 1e-4)); EXPECT_TRUE(result.translation.isApprox(translation, 1e-4)); } diff --git a/test/data/face.csv b/tests/fixtures/face.csv similarity index 100% rename from test/data/face.csv rename to tests/fixtures/face.csv diff --git a/test/comparer/base.cpp b/tests/fixtures/face.hpp similarity index 69% rename from test/comparer/base.cpp rename to tests/fixtures/face.hpp index 3c45d001..4b7ef18f 100644 --- a/test/comparer/base.cpp +++ b/tests/fixtures/face.hpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,18 +15,19 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/comparer/base.hpp" -#include "cpd/exceptions.hpp" +#include "tests/support.hpp" #include "gtest/gtest.h" namespace cpd { -TEST(Comparer, FromName) { - EXPECT_NO_THROW(Comparer::from_name("direct")); -#ifdef CPD_WITH_FGT - EXPECT_NO_THROW(Comparer::from_name("fgt")); -#else - EXPECT_THROW(Comparer::from_name("fgt"), unknown_comparer); -#endif -} +class FaceTest : public ::testing::Test { +public: + FaceTest() + : ::testing::Test() + , m_face(tests::fixture("face.csv")) + , m_face_distorted(tests::fixture("face_distorted.csv")) {} + + Matrix m_face; + Matrix m_face_distorted; +}; } diff --git a/test/data/face-distorted.csv b/tests/fixtures/face_distorted.csv similarity index 100% rename from test/data/face-distorted.csv rename to tests/fixtures/face_distorted.csv diff --git a/test/data/fish.csv b/tests/fixtures/fish.csv similarity index 100% rename from test/data/fish.csv rename to tests/fixtures/fish.csv diff --git a/tests/fixtures/fish.hpp b/tests/fixtures/fish.hpp new file mode 100644 index 00000000..45253e61 --- /dev/null +++ b/tests/fixtures/fish.hpp @@ -0,0 +1,33 @@ +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include "tests/support.hpp" +#include "gtest/gtest.h" + +namespace cpd { + +class FishTest : public ::testing::Test { +public: + FishTest() + : ::testing::Test() + , m_fish(tests::fixture("fish.csv")) + , m_fish_distorted(tests::fixture("fish_distorted.csv")) {} + + Matrix m_fish; + Matrix m_fish_distorted; +}; +} diff --git a/test/data/fish-correspondence.csv b/tests/fixtures/fish_correspondence.csv similarity index 100% rename from test/data/fish-correspondence.csv rename to tests/fixtures/fish_correspondence.csv diff --git a/test/data/fish-distorted.csv b/tests/fixtures/fish_distorted.csv similarity index 100% rename from test/data/fish-distorted.csv rename to tests/fixtures/fish_distorted.csv diff --git a/test/data/helheim.csv b/tests/fixtures/helheim.csv similarity index 100% rename from test/data/helheim.csv rename to tests/fixtures/helheim.csv diff --git a/tests/fixtures/helheim.hpp b/tests/fixtures/helheim.hpp new file mode 100644 index 00000000..45af8027 --- /dev/null +++ b/tests/fixtures/helheim.hpp @@ -0,0 +1,31 @@ +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include "tests/support.hpp" +#include "gtest/gtest.h" + +namespace cpd { + +class HelheimTest : public ::testing::Test { +public: + HelheimTest() + : ::testing::Test() + , m_helheim(tests::fixture("helheim.csv")) {} + + Matrix m_helheim; +}; +} diff --git a/tests/gauss_transform.cpp b/tests/gauss_transform.cpp new file mode 100644 index 00000000..b8c7e996 --- /dev/null +++ b/tests/gauss_transform.cpp @@ -0,0 +1,69 @@ +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include "fixtures/face.hpp" +#include "fixtures/fish.hpp" +#include "tests/support.hpp" +#include "gtest/gtest.h" +#include + +namespace cpd { + +TEST_F(FishTest, MatchesReference) { + GaussTransformDirect direct; + Probabilities probabilities = + direct.compute(m_fish, m_fish_distorted, 1.0, 0.1); + ASSERT_EQ(91, probabilities.p1.size()); + EXPECT_NEAR(0.6850, probabilities.p1(0), 1e-4); + EXPECT_NEAR(1.0689, probabilities.p1(90), 1e-4); + ASSERT_EQ(91, probabilities.pt1.size()); + EXPECT_NEAR(0.9882, probabilities.pt1(0), 1e-4); + EXPECT_NEAR(0.9869, probabilities.pt1(90), 1e-4); + ASSERT_EQ(91, probabilities.px.rows()); + ASSERT_EQ(2, probabilities.px.cols()); + EXPECT_NEAR(-0.2625, probabilities.px(0, 0), 1e-4); + EXPECT_NEAR(-0.3164, probabilities.px(90, 1), 1e-4); + EXPECT_NEAR(-338.4930, probabilities.l, 1e-3); +} + +TEST_F(FaceTest, MatchesReference) { + GaussTransformDirect direct; + Probabilities probabilities = + direct.compute(m_face, m_face_distorted, 1.0, 0.1); + ASSERT_EQ(392, probabilities.p1.size()); + EXPECT_NEAR(0.6171, probabilities.p1(0), 1e-4); + EXPECT_NEAR(0.4869, probabilities.p1(391), 1e-4); + ASSERT_EQ(392, probabilities.pt1.size()); + EXPECT_NEAR(0.9850, probabilities.pt1(0), 1e-4); + EXPECT_NEAR(0.9823, probabilities.pt1(391), 1e-4); + ASSERT_EQ(392, probabilities.px.rows()); + ASSERT_EQ(3, probabilities.px.cols()); + EXPECT_NEAR(-0.2361, probabilities.px(0, 0), 1e-4); + EXPECT_NEAR(-0.0271, probabilities.px(391, 2), 1e-4); + EXPECT_NEAR(-2.0397e3, probabilities.l, 1e-1); +} + +TEST_F(FishTest, CorrespondencesMatchReference) { + GaussTransformDirect direct; + Probabilities probabilities = + direct.compute(m_fish, m_fish_distorted, 1.0, 0.1); + IndexVector correspondence = + tests::fixture("fish_correspondence.csv").cast(); + ASSERT_EQ(correspondence.size(), probabilities.correspondence.size()); + EXPECT_EQ(correspondence, probabilities.correspondence); +} +} diff --git a/test/comparer/common.hpp b/tests/gauss_transform_fgt.cpp similarity index 67% rename from test/comparer/common.hpp rename to tests/gauss_transform_fgt.cpp index bef13862..9b746535 100644 --- a/test/comparer/common.hpp +++ b/tests/gauss_transform_fgt.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,14 +15,18 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#pragma once - -#include "cpd/probabilities.hpp" +#include "fixtures/face.hpp" +#include "fixtures/fish.hpp" +#include "tests/support.hpp" #include "gtest/gtest.h" +#include namespace cpd { -void check_fish_probabilities(const Probabilities& probabilities) { +TEST_F(FishTest, MatchesReference) { + GaussTransformFgt direct; + Probabilities probabilities = + direct.compute(m_fish, m_fish_distorted, 1.0, 0.1); ASSERT_EQ(91, probabilities.p1.size()); EXPECT_NEAR(0.6850, probabilities.p1(0), 1e-4); EXPECT_NEAR(1.0689, probabilities.p1(90), 1e-4); @@ -36,7 +40,10 @@ void check_fish_probabilities(const Probabilities& probabilities) { EXPECT_NEAR(-338.4930, probabilities.l, 1e-3); } -void check_face_probabilities(const Probabilities& probabilities) { +TEST_F(FaceTest, MatchesReference) { + GaussTransformFgt direct; + Probabilities probabilities = + direct.compute(m_face, m_face_distorted, 1.0, 0.1); ASSERT_EQ(392, probabilities.p1.size()); EXPECT_NEAR(0.6171, probabilities.p1(0), 1e-4); EXPECT_NEAR(0.4869, probabilities.p1(391), 1e-4); @@ -49,4 +56,14 @@ void check_face_probabilities(const Probabilities& probabilities) { EXPECT_NEAR(-0.0271, probabilities.px(391, 2), 1e-4); EXPECT_NEAR(-2.0397e3, probabilities.l, 1e-1); } + +TEST_F(FishTest, CorrespondencesMatchReference) { + GaussTransformDirect direct; + Probabilities probabilities = + direct.compute(m_fish, m_fish_distorted, 1.0, 0.1); + IndexVector correspondence = + tests::fixture("fish_correspondence.csv").cast(); + ASSERT_EQ(correspondence.size(), probabilities.correspondence.size()); + EXPECT_EQ(correspondence, probabilities.correspondence); +} } diff --git a/test/nonrigid.cpp b/tests/nonrigid.cpp similarity index 77% rename from test/nonrigid.cpp rename to tests/nonrigid.cpp index cf675be4..f3b4144e 100644 --- a/test/nonrigid.cpp +++ b/tests/nonrigid.cpp @@ -15,23 +15,23 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/nonrigid.hpp" -#include "cpd/runner.hpp" -#include "support.hpp" +#include "fixtures/face.hpp" +#include "fixtures/fish.hpp" +#include "tests/support.hpp" #include "gtest/gtest.h" +#include namespace cpd { TEST_F(FishTest, Works) { - Runner runner; - auto result = runner.run(m_fish, m_fish_transformed); + NonrigidResult result = nonrigid(m_fish, m_fish_distorted); EXPECT_TRUE(result.points.isApprox(m_fish, 0.1)); } TEST_F(FaceTest, Works) { - Runner runner; - runner.normalize(false).sigma2(1.0).outliers(0.1); - auto result = runner.run(m_face, m_face_transformed); + Nonrigid nonrigid; + nonrigid.normalize(false).sigma2(1.0).outliers(0.1); + NonrigidResult result = nonrigid.run(m_face, m_face_distorted); EXPECT_TRUE(result.points.row(0).isApprox(m_face.row(0), 0.01)); EXPECT_TRUE(result.points.row(391).isApprox(m_face.row(391), 0.5)); } diff --git a/test/normalize.cpp b/tests/normalization.cpp similarity index 64% rename from test/normalize.cpp rename to tests/normalization.cpp index f30859d6..ee1aa181 100644 --- a/test/normalize.cpp +++ b/tests/normalization.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,17 +15,17 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "cpd/normalize.hpp" -#include "support.hpp" +#include "fixtures/fish.hpp" #include "gtest/gtest.h" +#include namespace cpd { -TEST(Normalize, Fish) { - auto fish = test_data_matrix("fish.csv"); - auto normalization = normalize(fish, fish); - EXPECT_TRUE(fish.isApprox(normalization.fixed * normalization.scale + - normalization.fixed_mean, - 1e-4)); +TEST_F(FishTest, CanBeRetrieved) { + Normalization normalization(m_fish, m_fish); + ASSERT_EQ(m_fish.rows(), normalization.fixed.rows()); + EXPECT_TRUE(m_fish.isApprox(normalization.fixed * normalization.scale + + normalization.fixed_mean, + 1e-4)); } } diff --git a/tests/rigid.cpp b/tests/rigid.cpp new file mode 100644 index 00000000..cfee7768 --- /dev/null +++ b/tests/rigid.cpp @@ -0,0 +1,48 @@ +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#include "fixtures/fish.hpp" +#include "gtest/gtest.h" +#include + +namespace cpd { + +TEST_F(FishTest, ComputeOneMatchesReference) { + GaussTransformDirect direct; + Probabilities probabilities = + direct.compute(m_fish, m_fish_distorted, 1.0, 0.1); + Rigid rigid; + rigid.scale(true); + RigidResult result = + rigid.compute_one(m_fish, m_fish_distorted, probabilities, 1.0); + EXPECT_NEAR(0.4165, result.sigma2, 1e-2); + + rigid.scale(false); + result = rigid.compute_one(m_fish, m_fish_distorted, probabilities, 1.0); + EXPECT_NEAR(0.5238, result.sigma2, 1e-2); +} + +TEST_F(FishTest, Normalize) { + m_fish = m_fish * 10.0; + Matrix rotation = + Eigen::Rotation2D(M_PI * 20.0 / 180.0).toRotationMatrix(); + m_fish_distorted = m_fish * rotation; + RigidResult result = rigid(m_fish_distorted, m_fish); + EXPECT_TRUE(result.rotation.isApprox(rotation.transpose(), 1e-4)); + EXPECT_EQ(result.points.rows(), m_fish.rows()); +} +} diff --git a/tests/support.hpp.in b/tests/support.hpp.in new file mode 100644 index 00000000..bb62bf11 --- /dev/null +++ b/tests/support.hpp.in @@ -0,0 +1,29 @@ +// cpd - Coherent Point Drift +// Copyright (C) 2017 Pete Gadomski +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +#pragma once + +#include "gtest/gtest.h" +#include + +namespace cpd { +namespace tests { +Matrix fixture(const std::string& filename) { + return matrix_from_path("@PROJECT_SOURCE_DIR@/tests/fixtures/" + filename); +} +} +} diff --git a/test/affinity.cpp b/tests/utils.cpp similarity index 65% rename from test/affinity.cpp rename to tests/utils.cpp index 481121c6..6f588753 100644 --- a/test/affinity.cpp +++ b/tests/utils.cpp @@ -1,5 +1,5 @@ // cpd - Coherent Point Drift -// Copyright (C) 2016 Pete Gadomski +// Copyright (C) 2017 Pete Gadomski // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -15,16 +15,23 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#include "affinity.hpp" -#include "support.hpp" +#include "fixtures/fish.hpp" +#include "fixtures/helheim.hpp" #include "gtest/gtest.h" namespace cpd { +TEST_F(FishTest, DefaultSigma2) { + double sigma2 = cpd::default_sigma2(m_fish, m_fish_distorted); + EXPECT_NEAR(1.0, sigma2, 0.1); +} + +TEST_F(HelheimTest, DefaultSigma2) { + double sigma2 = cpd::default_sigma2(m_helheim, m_helheim); + EXPECT_NEAR(23235., sigma2, 1.0); +} -TEST(AffinityMatrix, Fish) { - auto fish = test_data_matrix("fish.csv"); - auto fish_distorted = test_data_matrix("fish-distorted.csv"); - auto matrix = affinity(fish, fish_distorted, 3.0); +TEST_F(FishTest, AffinityMatrix) { + Matrix matrix = cpd::affinity(m_fish, m_fish_distorted, 3.0); ASSERT_EQ(91, matrix.rows()); ASSERT_EQ(91, matrix.cols()); EXPECT_NEAR(0.9911, matrix(0, 0), 1e-4); diff --git a/test/version.cpp b/tests/version.cpp similarity index 100% rename from test/version.cpp rename to tests/version.cpp diff --git a/vendor/docopt.cpp-af03fa0/.gitignore b/vendor/docopt.cpp-af03fa0/.gitignore deleted file mode 100644 index 6d7eb1cd..00000000 --- a/vendor/docopt.cpp-af03fa0/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# Compiled Object files -*.slo -*.lo -*.o - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Compiled Static libraries -*.lai -*.la -*.a - -# Compiled examples, as per docs -example -naval_fate -run_testcase -run_testcase.exe - -# CMake temporary files -CMakeCache.txt -CMakeFiles -CPackConfig.cmake -CPackSourceConfig.cmake -Makefile -cmake_install.cmake -docopt-config-version.cmake - -# Files configured by CMake -run_tests diff --git a/vendor/docopt.cpp-af03fa0/.travis.yml b/vendor/docopt.cpp-af03fa0/.travis.yml deleted file mode 100644 index d3e842ce..00000000 --- a/vendor/docopt.cpp-af03fa0/.travis.yml +++ /dev/null @@ -1,76 +0,0 @@ -language: cpp -sudo: false # Use the new container infrastructure - -matrix: - include: - - os: linux - env: - - COMPILER=g++-5 STDLIB=libc++ - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'george-edison55-precise-backports'] - packages: ["g++-5", "cmake-data", "cmake"] - - os: linux - env: - - COMPILER=g++-4.8 USE_BOOST_REGEX=ON - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'george-edison55-precise-backports', 'boost-latest'] - packages: ["g++-4.8", "cmake-data", "cmake", "libboost-regex1.55-dev"] - - - os: linux - env: - - COMPILER=clang++-3.6 STDLIB=libc++ - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6', 'george-edison55-precise-backports'] - packages: ["clang-3.6", "cmake-data", "cmake"] - - - os: linux - env: - - COMPILER=clang++-3.7 STDLIB=libc++ - addons: - apt: - sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7', 'george-edison55-precise-backports'] - packages: ["clang-3.7", "cmake-data", "cmake"] - - - os: osx - osx_image: xcode6.4 - env: - - COMPILER=clang++ V='Apple LLVM 6.4' - - COMPILER=clang++ V='Apple LLVM 6.4' WITH_CPP14=true - - - os: osx - osx_image: xcode7 - env: - - COMPILER=clang++ V='Apple LLVM 7.0' - - COMPILER=clang++ V='Apple LLVM 7.0' WITH_CPP14=true - -before_install: - - | - if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then - brew rm --force cmake && brew install cmake - fi - - - CMAKE_CXX_FLAGS+=" -Wall" - - - if [[ "${WITH_CPP14}" == "true" ]]; then CMAKE_OPTIONS+=" -DCMAKE_CXX_STANDARD=14"; fi - - | - if [[ "${USE_BOOST_REGEX}" == "ON" ]]; then - CMAKE_OPTIONS+=" -DUSE_BOOST_REGEX=ON" - CMAKE_OPTIONS+=" -DBoost_REGEX_LIBRARY_DEBUG=/usr/lib/x86_64-linux-gnu/libboost_regex.so.1.55.0" - CMAKE_OPTIONS+=" -DBoost_REGEX_LIBRARY_RELEASE=/usr/lib/x86_64-linux-gnu/libboost_regex.so.1.55.0" - fi - - if [[ "${STDLIB}" == "libc++" ]]; then CMAKE_CXX_FLAGS+=" -stdlib=libc++"; fi - - - ${COMPILER} --version - -before_script: - - rm -rf build/ - - mkdir build - - cd build - - cmake -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DWITH_TESTS=1 -DWITH_EXAMPLE=1 ${CMAKE_OPTIONS} .. - -script: - - cmake --build . - - python run_tests diff --git a/vendor/docopt.cpp-af03fa0/CMakeLists.txt b/vendor/docopt.cpp-af03fa0/CMakeLists.txt deleted file mode 100644 index ee01e7cf..00000000 --- a/vendor/docopt.cpp-af03fa0/CMakeLists.txt +++ /dev/null @@ -1,151 +0,0 @@ -cmake_minimum_required(VERSION 3.1) -project(docopt.cpp VERSION 0.6.1) - -include(GNUInstallDirs) - -#============================================================================ -# Settable options -#============================================================================ -option(WITH_TESTS "Build tests." OFF) -option(WITH_EXAMPLE "Build example." OFF) -option(USE_BOOST_REGEX "Replace std::regex with Boost.Regex" OFF) - -#============================================================================ -# Internal compiler options -#============================================================================ -# C++ standard -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) -if(NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS 11) - set(CMAKE_CXX_STANDARD 11) -endif() - -# Suppression of "unknown pragma" warning on GCC -if(CMAKE_COMPILER_IS_GNUCXX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas") # Code uses #pragma mark -endif() - -#============================================================================ -# Sources & headers -#============================================================================ -set(docopt_SOURCES docopt.cpp) -set(docopt_HEADERS - docopt.h - docopt_private.h - docopt_util.h - docopt_value.h - ) - -#============================================================================ -# Compile targets -#============================================================================ -if(MSVC OR XCODE) - # MSVC requires __declspec() attributes, which are achieved via the - # DOCOPT_DLL and DOCOPT_EXPORTS macros below. Since those macros are only - # defined when building a shared library, we must build the shared and - # static libraries completely separately. - # Xcode does not support libraries with only object files as sources. - # See https://cmake.org/cmake/help/v3.0/command/add_library.html?highlight=add_library - add_library(docopt SHARED ${docopt_SOURCES} ${docopt_HEADERS}) - add_library(docopt_s STATIC ${docopt_SOURCES} ${docopt_HEADERS}) -else() - # If not using MSVC or Xcode, we will create an intermediate object target - # to avoid compiling the source code twice. - add_library(docopt_o OBJECT ${docopt_SOURCES} ${docopt_HEADERS}) - set_target_properties(docopt_o PROPERTIES POSITION_INDEPENDENT_CODE TRUE) - - add_library(docopt SHARED $) - add_library(docopt_s STATIC $) -endif() - -target_include_directories(docopt PUBLIC $ $) -target_include_directories(docopt_s PUBLIC $ $) - -if(MSVC) - # DOCOPT_DLL: Must be specified when building *and* when using the DLL. - # That's what the "PUBLIC" means. - # DOCOPT_EXPORTS: Must use __declspec(dllexport) when building the DLL. - # "PRIVATE" means it's only defined when building the DLL. - target_compile_definitions(docopt PUBLIC DOCOPT_DLL - PRIVATE DOCOPT_EXPORTS) -endif() - -if(NOT MSVC) - set_target_properties(docopt PROPERTIES OUTPUT_NAME docopt) - set_target_properties(docopt_s PROPERTIES OUTPUT_NAME docopt) -endif() - -if(USE_BOOST_REGEX) - add_definitions("-DDOCTOPT_USE_BOOST_REGEX") - # This is needed on Linux, where linking a static library into docopt.so - # fails because boost static libs are not compiled with -fPIC - set(Boost_USE_STATIC_LIBS OFF) - find_package(Boost 1.53 REQUIRED COMPONENTS regex) - include_directories(${Boost_INCLUDE_DIRS}) - target_link_libraries(docopt ${Boost_LIBRARIES}) - if(WITH_STATIC) - target_link_libraries(docopt_s ${Boost_LIBRARIES}) - endif() -endif() - -#============================================================================ -# Examples -#============================================================================ -if(WITH_EXAMPLE) - add_executable(docopt_example examples/naval_fate.cpp) - target_link_libraries(docopt_example docopt) -endif() - -#============================================================================ -# Tests -#============================================================================ -if(WITH_TESTS) - set(TESTPROG "${CMAKE_CURRENT_BINARY_DIR}/run_testcase") - set(TESTCASES "${PROJECT_SOURCE_DIR}/testcases.docopt") - add_executable(run_testcase run_testcase.cpp) - target_link_libraries(run_testcase docopt) - configure_file( - "${PROJECT_SOURCE_DIR}/run_tests.py" - "${CMAKE_CURRENT_BINARY_DIR}/run_tests" - ESCAPE_QUOTES - ) - add_test("Testcases docopt" ${TESTPROG}) -endif() - -#============================================================================ -# Install -#============================================================================ -set(export_name "docopt-targets") - -# Runtime package -install(TARGETS docopt EXPORT ${export_name} DESTINATION ${CMAKE_INSTALL_LIBDIR}) - -# Development package -install(TARGETS docopt_s EXPORT ${export_name} DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(FILES ${docopt_HEADERS} DESTINATION include/docopt) - -# CMake Package -include(CMakePackageConfigHelpers) -write_basic_package_version_file("${PROJECT_BINARY_DIR}/docopt-config-version.cmake" COMPATIBILITY SameMajorVersion) -install(FILES docopt-config.cmake ${PROJECT_BINARY_DIR}/docopt-config-version.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/docopt") -install(EXPORT ${export_name} DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/docopt") - -#============================================================================ -# CPack -#============================================================================ -set(CPACK_PACKAGE_NAME "docopt") -set(CPACK_DEBIAN_PACKAGE_DEPENDS "") -set(CPACK_RPM_PACKAGE_REQUIRES "") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Beautiful command line interfaces") -set(CPACK_PACKAGE_VENDOR "Jared Grubb") -set(CPACK_PACKAGE_CONTACT ${CPACK_PACKAGE_VENDOR}) -set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.rst") -set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE-MIT") -set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) -set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) -set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) -set(CPACK_DEBIAN_PACKAGE_SECTION "Development") -set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") -set(CPACK_RPM_PACKAGE_LICENSE "MIT") -set(CPACK_STRIP_FILES TRUE) -include(CPack) diff --git a/vendor/docopt.cpp-af03fa0/LICENSE-Boost-1.0 b/vendor/docopt.cpp-af03fa0/LICENSE-Boost-1.0 deleted file mode 100644 index 36b7cd93..00000000 --- a/vendor/docopt.cpp-af03fa0/LICENSE-Boost-1.0 +++ /dev/null @@ -1,23 +0,0 @@ -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/docopt.cpp-af03fa0/LICENSE-MIT b/vendor/docopt.cpp-af03fa0/LICENSE-MIT deleted file mode 100644 index 58ff1bc8..00000000 --- a/vendor/docopt.cpp-af03fa0/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2012 Vladimir Keleshev, - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to -whom the Software is furnished to do so, subject to the -following conditions: - -The above copyright notice and this permission notice shall -be included in all copies or substantial portions of the -Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/docopt.cpp-af03fa0/README.rst b/vendor/docopt.cpp-af03fa0/README.rst deleted file mode 100644 index b7e871ed..00000000 --- a/vendor/docopt.cpp-af03fa0/README.rst +++ /dev/null @@ -1,444 +0,0 @@ -``docopt.cpp``: A C++11 Port -============================ -docopt creates *beautiful* command-line interfaces --------------------------------------------------- - -Isn't it awesome how ``getopt`` (and ``boost::program_options`` for you fancy -folk!) generate help messages based on your code?! These timeless functions -have been around for decades and have proven we don't need anything better, right? - -*Hell no!* You know what's awesome? It's when the option parser *is* -generated based on the beautiful help message that you write yourself! -This way you don't need to write this stupid repeatable parser-code, -and instead can write only the help message--*the way you want it*. - -**docopt** helps you create most beautiful command-line interfaces -*easily*: - -.. code:: c++ - - #include "docopt.h" - - #include - - static const char USAGE[] = - R"(Naval Fate. - - Usage: - naval_fate ship new ... - naval_fate ship move [--speed=] - naval_fate ship shoot - naval_fate mine (set|remove) [--moored | --drifting] - naval_fate (-h | --help) - naval_fate --version - - Options: - -h --help Show this screen. - --version Show version. - --speed= Speed in knots [default: 10]. - --moored Moored (anchored) mine. - --drifting Drifting mine. - )"; - - int main(int argc, const char** argv) - { - std::map args - = docopt::docopt(USAGE, - { argv + 1, argv + argc }, - true, // show help if requested - "Naval Fate 2.0"); // version string - - for(auto const& arg : args) { - std::cout << arg.first << arg.second << std::endl; - } - - return 0; - } - -Beat that! The option parser is generated based on the docstring above -that is passed to ``docopt::docopt`` function. ``docopt`` parses the usage -pattern (``"Usage: ..."``) and option descriptions (lines starting -with dash "``-``") and ensures that the program invocation matches the -usage pattern; it parses options, arguments and commands based on -that. The basic idea is that *a good help message has all necessary -information in it to make a parser*. - -C++11 port details ---------------------------------------------------- - -This is a port of the ``docopt.py`` module (https://github.com/docopt/docopt), -and we have tried to maintain full feature parity (and code structure) as the -original. - -This port is written in C++11 and also requires a good C++11 standard library -(in particular, one with ``regex`` support). The following compilers are known -to work with docopt: - -- Clang 3.3 and later -- GCC 4.9 -- Visual C++ 2015 RC - -GCC-4.8 can work, but the std::regex module needs to be replaced with ``Boost.Regex``. -In that case, you will need to define ``DOCTOPT_USE_BOOST_REGEX`` when compiling -docopt, and link your code with the appropriated Boost libraries. A relativley -recent version of Boost is needed: 1.55 works, but 1.46 does not for example. - -This port is licensed under the MIT license, just like the original module. -However, we are also dual-licensing this code under the Boost License, version 1.0, -as this is a popular C++ license. The licenses are similar and you are free to -use this code under the terms of either license. - -The differences from the Python port are: - -* the addition of a ``docopt_parse`` function, which does not terminate - the program on error -* a ``docopt::value`` type to hold the various value types that can be parsed. - We considered using boost::variant, but it seems better to have no external - dependencies (beyond a good STL). -* because C++ is statically-typed and Python is not, we had to make some - changes to the interfaces of the internal parse tree types. -* because ``std::regex`` does not have an equivalent to Python's regex.split, - some of the regex's had to be restructured and additional loops used. - -API ---------------------------------------------------- - -.. code:: c++ - - docopt::docopt(doc, argv, help /* =true */, version /* ="" */, options_first /* =false */) - -``docopt`` takes 2 required and 3 optional arguments: - -- ``doc`` is a string that contains a **help message** that will be parsed to - create the option parser. The simple rules of how to write such a - help message are given in next sections. Here is a quick example of - such a string (note that this example uses the "raw string literal" feature - that was added to C++11): - -.. code:: c++ - - R"(Usage: my_program [-hso FILE] [--quiet | --verbose] [INPUT ...] - - -h --help show this - -s --sorted sorted output - -o FILE specify output file [default: ./test.txt] - --quiet print less text - --verbose print more text - )" - -- ``argv`` is a vector of strings representing the args passed. Although - main usually takes a ``(int argc, const char** argv)`` pair, you can - pass the value ``{argv+1, argv+argc}`` to generate the vector automatically. - (Note we skip the argv[0] argument!) Alternatively you can supply a list of - strings like ``{ "--verbose", "-o", "hai.txt" }``. - -- ``help``, by default ``true``, specifies whether the parser should - automatically print the help message (supplied as ``doc``) and - terminate, in case ``-h`` or ``--help`` option is encountered - (options should exist in usage pattern, more on that below). If you - want to handle ``-h`` or ``--help`` options manually (as other - options), set ``help=false``. - -- ``version``, by default empty, is an optional argument that - specifies the version of your program. If supplied, then, (assuming - ``--version`` option is mentioned in usage pattern) when parser - encounters the ``--version`` option, it will print the supplied - version and terminate. ``version`` could be any printable object, - but most likely a string, e.g. ``"2.1.0rc1"``. - - Note, when ``docopt`` is set to automatically handle ``-h``, - ``--help`` and ``--version`` options, you still need to mention - them in usage pattern for this to work (also so your users to - know about them!) - -- ``options_first``, by default ``false``. If set to ``true`` will - disallow mixing options and positional argument. I.e. after first - positional argument, all arguments will be interpreted as positional - even if the look like options. This can be used for strict - compatibility with POSIX, or if you want to dispatch your arguments - to other programs. - -The **return** value is a ``map`` with options, -arguments and commands as keys, spelled exactly like in your help message. -Long versions of options are given priority. For example, if you invoke the -top example as:: - - naval_fate ship Guardian move 100 150 --speed=15 - -the return dictionary will be: - -.. code:: python - - {"--drifting": false, "mine": false, - "--help": false, "move": true, - "--moored": false, "new": false, - "--speed": "15", "remove": false, - "--version": false, "set": false, - "": ["Guardian"], "ship": true, - "": "100", "shoot": false, - "": "150"} - -If any parsing error (in either the usage, or due to incorrect user inputs) is -encountered, the program will exit with exit code -1. - -Note that there is another function that does not exit on error, and instead will -propogate an exception that you can catch and process as you like. See the docopt.h file -for information on the exceptions and usage: - -.. code:: c++ - - docopt::docopt_parse(doc, argv, help /* =true */, version /* =true */, options_first /* =false) - - -Help message format ---------------------------------------------------- - -Help message consists of 2 parts: - -- Usage pattern, e.g.:: - - Usage: my_program [-hso FILE] [--quiet | --verbose] [INPUT ...] - -- Option descriptions, e.g.:: - - -h --help show this - -s --sorted sorted output - -o FILE specify output file [default: ./test.txt] - --quiet print less text - --verbose print more text - -Their format is described below; other text is ignored. - -Usage pattern format ----------------------------------------------------------------------- - -**Usage pattern** is a substring of ``doc`` that starts with -``usage:`` (case *insensitive*) and ends with a *visibly* empty line. -Minimum example: - -.. code:: python - - """Usage: my_program - - """ - -The first word after ``usage:`` is interpreted as your program's name. -You can specify your program's name several times to signify several -exclusive patterns: - -.. code:: python - - """Usage: my_program FILE - my_program COUNT FILE - - """ - -Each pattern can consist of the following elements: - -- ****, **ARGUMENTS**. Arguments are specified as either - upper-case words, e.g. ``my_program CONTENT-PATH`` or words - surrounded by angular brackets: ``my_program ``. -- **--options**. Options are words started with dash (``-``), e.g. - ``--output``, ``-o``. You can "stack" several of one-letter - options, e.g. ``-oiv`` which will be the same as ``-o -i -v``. The - options can have arguments, e.g. ``--input=FILE`` or ``-i FILE`` or - even ``-iFILE``. However it is important that you specify option - descriptions if you want your option to have an argument, a default - value, or specify synonymous short/long versions of the option (see - next section on option descriptions). -- **commands** are words that do *not* follow the described above - conventions of ``--options`` or ```` or ``ARGUMENTS``, - plus two special commands: dash "``-``" and double dash "``--``" - (see below). - -Use the following constructs to specify patterns: - -- **[ ]** (brackets) **optional** elements. e.g.: ``my_program - [-hvqo FILE]`` -- **( )** (parens) **required** elements. All elements that are *not* - put in **[ ]** are also required, e.g.: ``my_program - --path= ...`` is the same as ``my_program - (--path= ...)``. (Note, "required options" might be not - a good idea for your users). -- **|** (pipe) **mutually exclusive** elements. Group them using **( - )** if one of the mutually exclusive elements is required: - ``my_program (--clockwise | --counter-clockwise) TIME``. Group - them using **[ ]** if none of the mutually-exclusive elements are - required: ``my_program [--left | --right]``. -- **...** (ellipsis) **one or more** elements. To specify that - arbitrary number of repeating elements could be accepted, use - ellipsis (``...``), e.g. ``my_program FILE ...`` means one or - more ``FILE``-s are accepted. If you want to accept zero or more - elements, use brackets, e.g.: ``my_program [FILE ...]``. Ellipsis - works as a unary operator on the expression to the left. -- **[options]** (case sensitive) shortcut for any options. You can - use it if you want to specify that the usage pattern could be - provided with any options defined below in the option-descriptions - and do not want to enumerate them all in usage-pattern. -- "``[--]``". Double dash "``--``" is used by convention to separate - positional arguments that can be mistaken for options. In order to - support this convention add "``[--]``" to your usage patterns. -- "``[-]``". Single dash "``-``" is used by convention to signify that - ``stdin`` is used instead of a file. To support this add "``[-]``" - to your usage patterns. "``-``" acts as a normal command. - -If your pattern allows to match argument-less option (a flag) several -times:: - - Usage: my_program [-v | -vv | -vvv] - -then number of occurrences of the option will be counted. I.e. -``args['-v']`` will be ``2`` if program was invoked as ``my_program --vv``. Same works for commands. - -If your usage patterns allows to match same-named option with argument -or positional argument several times, the matched arguments will be -collected into a list:: - - Usage: my_program --path=... - -I.e. invoked with ``my_program file1 file2 --path=./here ---path=./there`` the returned dict will contain ``args[''] == -['file1', 'file2']`` and ``args['--path'] == ['./here', './there']``. - - -Option descriptions format ----------------------------------------------------------------------- - -**Option descriptions** consist of a list of options that you put -below your usage patterns. - -It is necessary to list option descriptions in order to specify: - -- synonymous short and long options, -- if an option has an argument, -- if option's argument has a default value. - -The rules are as follows: - -- Every line in ``doc`` that starts with ``-`` or ``--`` (not counting - spaces) is treated as an option description, e.g.:: - - Options: - --verbose # GOOD - -o FILE # GOOD - Other: --bad # BAD, line does not start with dash "-" - -- To specify that option has an argument, put a word describing that - argument after space (or equals "``=``" sign) as shown below. Follow - either or UPPER-CASE convention for options' - arguments. You can use comma if you want to separate options. In - the example below, both lines are valid, however you are recommended - to stick to a single style.:: - - -o FILE --output=FILE # without comma, with "=" sign - -i , --input # with comma, without "=" sing - -- Use two spaces to separate options with their informal description:: - - --verbose More text. # BAD, will be treated as if verbose option had - # an argument "More", so use 2 spaces instead - -q Quit. # GOOD - -o FILE Output file. # GOOD - --stdout Use stdout. # GOOD, 2 spaces - -- If you want to set a default value for an option with an argument, - put it into the option-description, in form ``[default: - ]``:: - - --coefficient=K The K coefficient [default: 2.95] - --output=FILE Output file [default: test.txt] - --directory=DIR Some directory [default: ./] - -- If the option is not repeatable, the value inside ``[default: ...]`` - will be interpreted as string. If it *is* repeatable, it will be - splited into a list on whitespace:: - - Usage: my_program [--repeatable= --repeatable=] - [--another-repeatable=]... - [--not-repeatable=] - - # will be ['./here', './there'] - --repeatable= [default: ./here ./there] - - # will be ['./here'] - --another-repeatable= [default: ./here] - - # will be './here ./there', because it is not repeatable - --not-repeatable= [default: ./here ./there] - -Examples ----------------------------------------------------------------------- - -We have an extensive list of `examples -`_ which cover -every aspect of functionality of **docopt**. Try them out, read the -source if in doubt. - -There are also very intersting applications and ideas at that page. -Check out the sister project for more information! - -Subparsers, multi-level help and *huge* applications (like git) ----------------------------------------------------------------------- - -If you want to split your usage-pattern into several, implement -multi-level help (with separate help-screen for each subcommand), -want to interface with existing scripts that don't use **docopt**, or -you're building the next "git", you will need the new ``options_first`` -parameter (described in API section above). To get you started quickly -we implemented a subset of git command-line interface as an example: -`examples/git -`_ - -Compiling the example / Running the tests ----------------------------------------------------------------------- -The original Python module includes some language-agnostic unit tests, -and these can be run with this port as well. - -The tests are a Python driver that uses the testcases.docopt file to then invoke -a C++ test case runner (run_testcase.cpp):: - - $ clang++ --std=c++11 --stdlib=libc++ docopt.cpp run_testcase.cpp -o run_testcase - $ python run_tests.py - PASS (175) - -You can also compile the example shown at the start (included as example.cpp):: - - $ clang++ --std=c++11 --stdlib=libc++ -I . docopt.cpp examples/naval_fate.cpp -o naval_fate - $ ./naval_fate --help - [ ... ] - $ ./naval_fate ship Guardian move 100 150 --speed=15 - --drifting: false - --help: false - --moored: false - --speed: "15" - --version: false - : ["Guardian"] - : "100" - : "150" - mine: false - move: true - new: false - remove: false - set: false - ship: true - shoot: false - -Development ---------------------------------------------------- - -Comments and suggestions are *very* welcome! If you find issues, please -file them and help improve our code! - -Please note, however, that we have tried to stay true to the original -Python code. If you have any major patches, structural changes, or new features, -we might want to first negotiate these changes into the Python code first. -However, bring it up! Let's hear it! - -Changelog ---------------------------------------------------- - -**docopt** follows `semantic versioning `_. The -first release with stable API will be 1.0.0 (soon). - -- 0.6.1 The initial C++ port of docopt.py diff --git a/vendor/docopt.cpp-af03fa0/docopt-config.cmake b/vendor/docopt.cpp-af03fa0/docopt-config.cmake deleted file mode 100644 index 33c36c08..00000000 --- a/vendor/docopt.cpp-af03fa0/docopt-config.cmake +++ /dev/null @@ -1 +0,0 @@ -include("${CMAKE_CURRENT_LIST_DIR}/docopt-targets.cmake") diff --git a/vendor/docopt.cpp-af03fa0/docopt.cpp b/vendor/docopt.cpp-af03fa0/docopt.cpp deleted file mode 100644 index e875d2fc..00000000 --- a/vendor/docopt.cpp-af03fa0/docopt.cpp +++ /dev/null @@ -1,685 +0,0 @@ -// -// docopt.cpp -// docopt -// -// Created by Jared Grubb on 2013-11-03. -// Copyright (c) 2013 Jared Grubb. All rights reserved. -// - -#include "docopt.h" -#include "docopt_util.h" -#include "docopt_private.h" - -#include "docopt_value.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace docopt; - -DOCOPT_INLINE -std::ostream& docopt::operator<<(std::ostream& os, value const& val) -{ - if (val.isBool()) { - bool b = val.asBool(); - os << (b ? "true" : "false"); - } else if (val.isLong()) { - long v = val.asLong(); - os << v; - } else if (val.isString()) { - std::string const& str = val.asString(); - os << '"' << str << '"'; - } else if (val.isStringList()) { - auto const& list = val.asStringList(); - os << "["; - bool first = true; - for(auto const& el : list) { - if (first) { - first = false; - } else { - os << ", "; - } - os << '"' << el << '"'; - } - os << "]"; - } else { - os << "null"; - } - return os; -} - -#pragma mark - -#pragma mark Parsing stuff - -class Tokens { -public: - Tokens(std::vector tokens, bool isParsingArgv = true) - : fTokens(std::move(tokens)), - fIsParsingArgv(isParsingArgv) - {} - - explicit operator bool() const { - return fIndex < fTokens.size(); - } - - static Tokens from_pattern(std::string const& source) { - static const std::regex re_separators { - "(?:\\s*)" // any spaces (non-matching subgroup) - "(" - "[\\[\\]\\(\\)\\|]" // one character of brackets or parens or pipe character - "|" - "\\.\\.\\." // elipsis - ")" }; - - static const std::regex re_strings { - "(?:\\s*)" // any spaces (non-matching subgroup) - "(" - "\\S*<.*?>" // strings, but make sure to keep "< >" strings together - "|" - "[^<>\\s]+" // string without <> - ")" }; - - // We do two stages of regex matching. The '[]()' and '...' are strong delimeters - // and need to be split out anywhere they occur (even at the end of a token). We - // first split on those, and then parse the stuff between them to find the string - // tokens. This is a little harder than the python version, since they have regex.split - // and we dont have anything like that. - - std::vector tokens; - std::for_each(std::sregex_iterator{ source.begin(), source.end(), re_separators }, - std::sregex_iterator{}, - [&](std::smatch const& match) - { - // handle anything before the separator (this is the "stuff" between the delimeters) - if (match.prefix().matched) { - std::for_each(std::sregex_iterator{match.prefix().first, match.prefix().second, re_strings}, - std::sregex_iterator{}, - [&](std::smatch const& m) - { - tokens.push_back(m[1].str()); - }); - } - - // handle the delimter token itself - if (match[1].matched) { - tokens.push_back(match[1].str()); - } - }); - - return Tokens(tokens, false); - } - - std::string const& current() const { - if (*this) - return fTokens[fIndex]; - - static std::string const empty; - return empty; - } - - std::string the_rest() const { - if (!*this) - return {}; - return join(fTokens.begin()+static_cast(fIndex), - fTokens.end(), - " "); - } - - std::string pop() { - return std::move(fTokens.at(fIndex++)); - } - - bool isParsingArgv() const { return fIsParsingArgv; } - - struct OptionError : std::runtime_error { using runtime_error::runtime_error; }; - -private: - std::vector fTokens; - size_t fIndex = 0; - bool fIsParsingArgv; -}; - -// Get all instances of 'T' from the pattern -template -std::vector flat_filter(Pattern& pattern) { - std::vector flattened = pattern.flat([](Pattern const* p) -> bool { - return dynamic_cast(p) != nullptr; - }); - - // now, we're guaranteed to have T*'s, so just use static_cast - std::vector ret; - std::transform(flattened.begin(), flattened.end(), std::back_inserter(ret), [](Pattern* p) { - return static_cast(p); - }); - return ret; -} - -static std::vector parse_section(std::string const& name, std::string const& source) { - // ECMAScript regex only has "?=" for a non-matching lookahead. In order to make sure we always have - // a newline to anchor our matching, we have to avoid matching the final newline of each grouping. - // Therefore, our regex is adjusted from the docopt Python one to use ?= to match the newlines before - // the following lines, rather than after. - std::regex const re_section_pattern { - "(?:^|\\n)" // anchored at a linebreak (or start of string) - "(" - "[^\\n]*" + name + "[^\\n]*(?=\\n?)" // a line that contains the name - "(?:\\n[ \\t].*?(?=\\n|$))*" // followed by any number of lines that are indented - ")", - std::regex::icase - }; - - std::vector ret; - std::for_each(std::sregex_iterator(source.begin(), source.end(), re_section_pattern), - std::sregex_iterator(), - [&](std::smatch const& match) - { - ret.push_back(trim(match[1].str())); - }); - - return ret; -} - -static bool is_argument_spec(std::string const& token) { - if (token.empty()) - return false; - - if (token[0]=='<' && token[token.size()-1]=='>') - return true; - - if (std::all_of(token.begin(), token.end(), &::isupper)) - return true; - - return false; -} - -template -std::vector longOptions(I iter, I end) { - std::vector ret; - std::transform(iter, end, - std::back_inserter(ret), - [](typename I::reference opt) { return opt->longOption(); }); - return ret; -} - -static PatternList parse_long(Tokens& tokens, std::vector