From 1e82e0e9515dad9e20f69a480558ad0a87091425 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Thu, 16 Aug 2018 10:50:05 +0200 Subject: [PATCH 01/21] #36 run unit tests in Travis CI --- .travis.yml | 16 +-- .travis/build_install_sirius.sh | 30 +++++ .travis/create_cmake_project.sh | 2 + .travis/deploy.sh | 23 ++-- .travis/on_success.sh | 52 +++++--- .travis/run_tests.sh | 28 +++++ CMakeLists.txt | 6 + data/filters/dirac_filter.tif | Bin 0 -> 218 bytes data/filters/sinc_zoom2_filter.tif | Bin 0 -> 17070 bytes src/CMakeLists.txt | 2 +- tests/filter_tests.cc | 4 +- tests/frequency_resampler_tests.cc | 190 ++++------------------------- tests/functional_tests.cc | 82 +++++++------ tests/image_tests.cc | 4 +- tests/utils.h | 11 ++ 15 files changed, 201 insertions(+), 249 deletions(-) create mode 100755 .travis/build_install_sirius.sh create mode 100755 .travis/run_tests.sh create mode 100644 data/filters/dirac_filter.tif create mode 100644 data/filters/sinc_zoom2_filter.tif diff --git a/.travis.yml b/.travis.yml index 9707a8ea..72482259 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,24 +9,20 @@ before_install: - docker pull ldumas/sirius_dockerfile:sirius_pages before_script: -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD; fi - export SRCDIR=/data - export SIRIUS_BUILD_DIR=.build - export INSTALL_DIR=/opt/sirius - SIRIUS_VERSION=$(git describe 2> /dev/null); if [ "x${SIRIUS_VERSION}" = "x" ]; then SIRIUS_VERSION="0.0.0"; fi; export SIRIUS_VERSION; script: -- docker run -v $TRAVIS_BUILD_DIR/:/data ldumas/sirius_dockerfile:sirius_build /bin/sh -c "source /opt/rh/devtoolset-6/enable; cd $SRCDIR; .travis/create_cmake_project.sh $SIRIUS_BUILD_DIR $INSTALL_DIR $SIRIUS_VERSION $TRAVIS_COMMIT; cd $SIRIUS_BUILD_DIR; make -j4 build_tests && make -j4 && make install; cd ..; mv $SIRIUS_BUILD_DIR/html doxy_html; mv $SIRIUS_BUILD_DIR/xml doxy_xml; rm -rf $SIRIUS_BUILD_DIR" -- CID=$(docker ps --latest --quiet) -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then docker commit --change "ENV PATH $PATH:/opt/sirius/bin" $CID sirius_built; fi -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then docker tag sirius_built ldumas/sirius_dockerfile:sirius_built; fi -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then docker push ldumas/sirius_dockerfile:sirius_built; fi +- docker run -v $TRAVIS_BUILD_DIR/:/data ldumas/sirius_dockerfile:sirius_build /bin/sh -c "source /opt/rh/devtoolset-6/enable; ${SRCDIR}/.travis/build_install_sirius.sh ${SRCDIR} ${INSTALL_DIR} ${SIRIUS_VERSION} ${TRAVIS_COMMIT}" +- export BUILD_CONTAINER_ID=$(docker ps --latest --quiet) + +after_success: +- .travis/on_success.sh deploy: provider: script - script: if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then .travis/deploy.sh; fi + script: .travis/deploy.sh on: branch: master - -after_success: -- if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then .travis/on_success.sh; fi diff --git a/.travis/build_install_sirius.sh b/.travis/build_install_sirius.sh new file mode 100755 index 00000000..d4401d02 --- /dev/null +++ b/.travis/build_install_sirius.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# This script will build sirius project (library, binary and unit tests) +# Usage: ./build_sirius.sh PROJECT_DIR INSTALL_DIR SIRIUS_VERSION SIRIUS_REVISION_COMMIT + +set -ev + +PROJECT_DIR=$1 +INSTALL_DIR=$2 +SIRIUS_VERSION=$3 +SIRIUS_REVISION_COMMIT=$4 + +BUILD_DIR="${PROJECT_DIR}/.build" + +cd ${PROJECT_DIR} + +.travis/create_cmake_project.sh ${BUILD_DIR} ${INSTALL_DIR} ${SIRIUS_VERSION} ${SIRIUS_REVISION_COMMIT} + +cd ${BUILD_DIR} +make -j4 build_tests + +${PROJECT_DIR}/.travis/run_tests.sh + +make -j4 + +make install +cd ${PROJECT_DIR} +mv "${BUILD_DIR}/html" doxy_html +mv "${BUILD_DIR}/xml" doxy_xml +rm -rf ${BUILD_DIR} \ No newline at end of file diff --git a/.travis/create_cmake_project.sh b/.travis/create_cmake_project.sh index b6814c78..7e514fc3 100755 --- a/.travis/create_cmake_project.sh +++ b/.travis/create_cmake_project.sh @@ -3,6 +3,8 @@ # This script will create the cmake sirius project build directory # Usage: ./create_cmake_project.sh BUILD_DIR INSTALL_DIR SIRIUS_VERSION SIRIUS_REVISION_COMMIT +set -ev + BUILD_DIR=$1 INSTALL_DIR=$2 SIRIUS_VERSION=$3 diff --git a/.travis/deploy.sh b/.travis/deploy.sh index 18942d48..bd2d99e3 100755 --- a/.travis/deploy.sh +++ b/.travis/deploy.sh @@ -1,17 +1,20 @@ #!/bin/sh -cat >Dockerfile <Dockerfile </dev/null - cd "$TRAVIS_BUILD_DIR" -fi +set -ev + +if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ] && [ "x$DOCKER_USERNAME" != "x" ] && [ "x$DOCKER_PASSWORD" != "x" ]; then + + # update sirius built container on docker hub + docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD + docker commit --change "ENV PATH $PATH:/opt/sirius/bin" ${BUILD_CONTAINER_ID} sirius_built + docker tag sirius_built ldumas/sirius_dockerfile:sirius_built + docker push ldumas/sirius_dockerfile:sirius_built + # update gh-pages branch on CI-SI/SIRIUS repository only + if [ "x${TRAVIS_REPO_SLUG}" = "xCS-SI/SIRIUS" ]; then + openssl aes-256-cbc -K $encrypted_f05cf190aee2_key -iv $encrypted_f05cf190aee2_iv -in .travis/github_deploy_key.enc -out github_deploy_key -d + chmod 600 github_deploy_key + eval `ssh-agent -s` + ssh-add github_deploy_key + + cd "$TRAVIS_BUILD_DIR" + mkdir doc/sphinx/build + docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_xml doc/sphinx/source/ ; cd doc/sphinx/ ; ln -s source/doxy_xml . " + docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data/doc/sphinx; make html" + docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_html doc/sphinx/build/" + cd $TRAVIS_BUILD_DIR/doc/sphinx/build/ + touch .nojekyll + git init + git checkout -b gh-pages + git add . + git -c user.name='travis' -c user.email='travis' commit -m init + git push -q -f git@github.com:${TRAVIS_REPO_SLUG}.git gh-pages &2>/dev/null + cd "$TRAVIS_BUILD_DIR" + fi + +fi diff --git a/.travis/run_tests.sh b/.travis/run_tests.sh new file mode 100755 index 00000000..80e45815 --- /dev/null +++ b/.travis/run_tests.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +unit_tests=( + concurrent_queue_tests \ + filter_tests \ + frequency_resampler_tests \ + image_tests \ + types_tests \ + utils_tests ) + +echo "Unit tests:" + +test_exit_status=0 +for unit_test in "${unit_tests[@]}" +do + echo "* running ${unit_test}" + ./tests/${unit_test} &> "./tests/output/${unit_test}.log" + exit_code=$? + if [ $exit_code -ne 0 ]; then + echo "........ FAILED" + cat ./tests/output/${unit_test}.log + tests_exit_status=1 + else + echo "........ OK" + fi +done + +exit ${tests_exit_status} diff --git a/CMakeLists.txt b/CMakeLists.txt index 55904953..bda0c400 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,12 @@ add_subdirectory(src) if (${ENABLE_UNIT_TESTS}) enable_testing() add_subdirectory(tests) + + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests/input + ${CMAKE_CURRENT_BINARY_DIR}/tests/filters + ${CMAKE_CURRENT_BINARY_DIR}/tests/output) + file(COPY data/input DESTINATION tests) + file(COPY data/filters DESTINATION tests) endif() if (${ENABLE_DOCUMENTATION}) diff --git a/data/filters/dirac_filter.tif b/data/filters/dirac_filter.tif new file mode 100644 index 0000000000000000000000000000000000000000..b1a235a827ba7223e048da87dfff72bd5bf90b3e GIT binary patch literal 218 zcmebD)MDUZU|`^8U|?isU<9(5ftV4A%>-pTfaIWTkUBObwjd)5SnnjDxG0ji7?ce% ZQyi+s1IU&^QWK1%1}H@&_+U>+AprB31TX*q literal 0 HcmV?d00001 diff --git a/data/filters/sinc_zoom2_filter.tif b/data/filters/sinc_zoom2_filter.tif new file mode 100644 index 0000000000000000000000000000000000000000..fb0f0168bd8ee334336d743c8e3f1bffac132791 GIT binary patch literal 17070 zcmeI3e~48@6vtkyr_NCOE%(bcHL5Is?W2Na5w0kAZgH*rM=x(VS==}&Ui9?MbM{t6 zLipk3zip6w{KEy8OFs9jcJE03a?zQJ4?I+%_=(~w#Y>kBQT$2g(Tb-{enjztpBE~A zz2<$z1)Gj2?(%u>jndxex90Sf{GNuo^c62JPfMO#PCf3U9omHlUgSV7*jqX&ASCwdEaba_p)N!%FbxOw3V*jqX&ASC-XzZEtl6co5YP_j+-~|*}NIXzxA8zGp3i= z)6J<*i9OvV_g=4fdhd~n|17Cj{L_r570-INQSqUUTNLm6`k>urr9KJ40GJP@P+rk z&7)!bTR-~uez;ET>B~mlA@=oWl}yUu`Hw3;a>fG1FV(J9yt_}c;_dqj#om6%kPF5B z{;ox16j#-KsCe0cXVVjJ{xL1N{GW2_aUbo_E}dGrjI6yecjwIW{SPtz)$BX-dy^I;(60QQaot>0mU}2 zyEksB68pPT+fEbvySqDlsrdA3Ua1~jceGma+;Zx1AMMaCJn$k1av>*jGe4jgdZM>* zN0-+$o5YP_j++-gkDqORmghZd^K2OZ*3W)bkLShS{?Ny-iM{>%6`K_|kNR41*Tw=J z@2e;F5PSP?($^|ZEqF@tg13KBe8<35=?@CqH%Tt_D5oCx(GKmx121wQ7jiN`pa*)P zCwfQRa(PX&N!%FbxOoGgt-Bn4xA|R;_pZ&mVf6S>g?z0ecAg*&>urr9KJ40GJP@Ok3+7RUA0kKW=qVf}CGxZwYr zALn_G+dLk|zuD10UR$c;|AJZl)eo?BqObgQLQ`YW<|fIbPVkgdkNaqc`2ikyk%RRG zauR<)5A;G$^fm*~9|mK8E|TI6dfUEEi2pOri0fHgC#?T%UFY!Yi4XE#w|U($uFbBF zxS{!lZgJhx)WX6xHIhpwP)WWKKOz2)-%gy5amhO0)_K9tH$N}D=h-)4oU<;l_kiD8-eFVQ6}N4YJn95bIrX@Y zcJLqIvFF1h2XY}Na-#?P8tBQsXF_(T|BXImEz4uWw(slM_v1ayxU{&hGrKs8`{?># zq-PxC_nY6R?|=Ef&HFn31)JU7DJyr>3>r|;Px9Py<_GS>f1q7>;3fWmT*!&s=z(77 ziQd93``?nw{-@-!|0TKXe@HIzZ^q9spm1jZp@e=ss2CaA&5X?au81; zzCyf(_zUqE;xoi+h~F@O5Z@u*L;T15_ppw{{}BHnA4h8W7$o)_RU><2MFuwR7#z&MuvwZ|=uw|2WH+#cU^@O=m0d+_}S--9qe@VyBB z1K*RdzTkTk;tza}LVS|=qr?~U-?3o+ke{&K?2gu|pZ~5c)1j@^pJtwaJiq_j`&vzI pzW3ad|IQU#NZxAwYmNS0dsZ!WUgXMu_nxr;{gISClHygp{{q+=DFFZg literal 0 HcmV?d00001 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bda39270..13a16094 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -141,7 +141,7 @@ if (ENABLE_SIRIUS_EXECUTABLE OR ENABLE_UNIT_TESTS) sirius/gdal/wrapper.h sirius/gdal/wrapper.cc) add_library(libsirius-gdal-static STATIC EXCLUDE_FROM_ALL ${LIBSIRIUS_GDAL_SRC}) - target_link_libraries(libsirius-gdal-static gdal libsirius) + target_link_libraries(libsirius-gdal-static gdal libsirius-static) endif () if (ENABLE_SIRIUS_EXECUTABLE) diff --git a/tests/filter_tests.cc b/tests/filter_tests.cc index 8a0e8e62..539fea03 100644 --- a/tests/filter_tests.cc +++ b/tests/filter_tests.cc @@ -49,7 +49,7 @@ TEST_CASE("filter - dirac filter", "[sirius]") { std::memset(complex_array.get(), 1, size.CellCount()); auto filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/dirac_filter.tiff"), zoom_ratio); + sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), zoom_ratio); REQUIRE(filter.IsLoaded()); auto output = filter.Process(size, std::move(complex_array)); @@ -59,7 +59,7 @@ TEST_CASE("filter - sinc2 filter", "[sirius]") { LOG_SET_LEVEL(trace); auto zoom_ratio = sirius::ZoomRatio::Create(2, 1); auto filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/sinc_zoom2_filter.tif"), + sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), zoom_ratio); sirius::Size regular_size = {100, 100}; sirius::Size large_size = {1000, 1000}; diff --git a/tests/frequency_resampler_tests.cc b/tests/frequency_resampler_tests.cc index 36b7c4f2..445cbc7e 100644 --- a/tests/frequency_resampler_tests.cc +++ b/tests/frequency_resampler_tests.cc @@ -67,15 +67,14 @@ TEST_CASE("frequency resampler - classic decomposition - zero padding zoom", auto zoom_ratio = sirius::ZoomRatio::Create(2, 1); // test inputs - auto dummy_image = sirius::tests::CreateDummyImage({20, 20}); - auto lena_image = sirius::gdal::LoadImage("./input/lena.jpg"); - auto disp0_image = sirius::gdal::LoadImage("./input/disparity.png"); + auto dummy_image = sirius::tests::CreateDummyImage({2048, 1536}); + auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); // filters auto dirac_filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/dirac_filter.tiff"), zoom_ratio); + sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), zoom_ratio); auto sinc_zoom2_filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/sinc_zoom2_filter.tif"), + sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), zoom_ratio); // output @@ -130,8 +129,6 @@ TEST_CASE("frequency resampler - classic decomposition - zero padding zoom", LOG("tests", debug, "output size: {}, {}", output.size.row, output.size.col); - - sirius::gdal::SaveImage(output, "./output/lena_no_filter.tif"); } SECTION("Lena - dirac filter") { @@ -145,8 +142,6 @@ TEST_CASE("frequency resampler - classic decomposition - zero padding zoom", LOG("tests", debug, "output size: {}, {}", output.size.row, output.size.col); - - sirius::gdal::SaveImage(output, "./output/lena_dirac_filter.tif"); } SECTION("Lena - sinc filter") { @@ -160,50 +155,6 @@ TEST_CASE("frequency resampler - classic decomposition - zero padding zoom", LOG("tests", debug, "output size: {}, {}", output.size.row, output.size.col); - - sirius::gdal::SaveImage(output, "./output/lena_sinc_filter.tif"); - } - - SECTION("disp0 - no filter") { - sirius::Image output; - REQUIRE_NOTHROW( - output = freq_resampler->Compute(zoom_ratio, disp0_image, {})); - REQUIRE(output.data.size() > 0); - - LOG("tests", debug, "output size: {}, {}", output.size.row, - output.size.col); - - sirius::gdal::SaveImage(output, "./output/disparity_no_filter.tif"); - } - - SECTION("disp0 - dirac") { - sirius::Image output; - REQUIRE_NOTHROW(output = freq_resampler->Compute( - zoom_ratio, disp0_image, dirac_filter.padding(), - dirac_filter)); - REQUIRE(output.data.size() > 0); - REQUIRE(output.size == - (disp0_image.size * zoom_ratio.input_resolution())); - - LOG("tests", debug, "output size: {}, {}", output.size.row, - output.size.col); - - sirius::gdal::SaveImage(output, "./output/disparity_dirac_filter.tif"); - } - - SECTION("disp0 - sinc") { - sirius::Image output; - REQUIRE_NOTHROW(output = freq_resampler->Compute( - zoom_ratio, disp0_image, - sinc_zoom2_filter.padding(), sinc_zoom2_filter)); - REQUIRE(output.data.size() > 0); - REQUIRE(output.size == - (disp0_image.size * zoom_ratio.input_resolution())); - - LOG("tests", debug, "output size: {}, {}", output.size.row, - output.size.col); - - sirius::gdal::SaveImage(output, "./output/disparity_sinc_filter.tif"); } } @@ -212,16 +163,15 @@ TEST_CASE("frequency resampler - classic - periodization zoom", "[sirius]") { auto zoom_ratio = sirius::ZoomRatio::Create(2, 1); - // test input - auto dummy_image = sirius::tests::CreateDummyImage({4, 4}); - auto lena_image = sirius::gdal::LoadImage("./input/lena.jpg"); - auto disp0_image = sirius::gdal::LoadImage("./input/disparity.png"); + // test inputs + auto dummy_image = sirius::tests::CreateDummyImage({2048, 1536}); + auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); // filters auto dirac_filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/dirac_filter.tiff"), zoom_ratio); + sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), zoom_ratio); auto sinc_zoom2_filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/sinc_zoom2_filter.tif"), + sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), zoom_ratio); // output @@ -276,9 +226,6 @@ TEST_CASE("frequency resampler - classic - periodization zoom", "[sirius]") { LOG("tests", debug, "output size: {}, {}", output.size.row, output.size.col); - - sirius::gdal::SaveImage(output, - "./output/lena_periodization_no_filter.tif"); } SECTION("Lena - dirac filter") { @@ -292,9 +239,6 @@ TEST_CASE("frequency resampler - classic - periodization zoom", "[sirius]") { LOG("tests", debug, "output size: {}, {}", output.size.row, output.size.col); - - sirius::gdal::SaveImage(output, - "./output/lena_periodization_dirac_filter.tif"); } SECTION("Lena - sinc filter") { @@ -308,56 +252,6 @@ TEST_CASE("frequency resampler - classic - periodization zoom", "[sirius]") { LOG("tests", debug, "output size: {}, {}", output.size.row, output.size.col); - - sirius::gdal::SaveImage(output, - "./output/lena_periodization_sinc_filter.tif"); - } - - SECTION("disp0 - no filter") { - sirius::Image output; - REQUIRE_NOTHROW( - output = freq_resampler->Compute(zoom_ratio, disp0_image, {})); - REQUIRE(output.data.size() > 0); - REQUIRE(output.size == - (disp0_image.size * zoom_ratio.input_resolution())); - - LOG("tests", debug, "output size: {}, {}", output.size.row, - output.size.col); - - sirius::gdal::SaveImage( - output, "./output/disparity_periodization_no_filter.tif"); - } - - SECTION("disp0 - dirac") { - sirius::Image output; - REQUIRE_NOTHROW(output = freq_resampler->Compute( - zoom_ratio, disp0_image, dirac_filter.padding(), - dirac_filter)); - REQUIRE(output.data.size() > 0); - REQUIRE(output.size == - (disp0_image.size * zoom_ratio.input_resolution())); - - LOG("tests", debug, "output size: {}, {}", output.size.row, - output.size.col); - - sirius::gdal::SaveImage( - output, "./output/disparity_periodization_dirac_filter.tif"); - } - - SECTION("disp0 - sinc") { - sirius::Image output; - REQUIRE_NOTHROW(output = freq_resampler->Compute( - zoom_ratio, disp0_image, - sinc_zoom2_filter.padding(), sinc_zoom2_filter)); - REQUIRE(output.data.size() > 0); - REQUIRE(output.size == - (disp0_image.size * zoom_ratio.input_resolution())); - - LOG("tests", debug, "output size: {}, {}", output.size.row, - output.size.col); - - sirius::gdal::SaveImage( - output, "./output/disparity_periodization_sinc_filter.tif"); } } @@ -366,10 +260,9 @@ TEST_CASE("frequency resampler - periodic smooth - zero padding", "[sirius]") { auto zoom_ratio = sirius::ZoomRatio::Create(2, 1); - // test input - auto dummy_image = sirius::tests::CreateDummyImage({256, 256}); - auto lena_image = sirius::gdal::LoadImage("./input/lena.jpg"); - auto disp0_image = sirius::gdal::LoadImage("./input/disparity.png"); + // test inputs + auto dummy_image = sirius::tests::CreateDummyImage({2048, 1536}); + auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); // output sirius::Image output; @@ -399,36 +292,17 @@ TEST_CASE("frequency resampler - periodic smooth - zero padding", "[sirius]") { LOG("tests", debug, "output size: {}, {}", output.size.row, output.size.col); - - sirius::gdal::SaveImage(output, - "./output/lena_periodic_smooth_no_filter.tif"); - } - - SECTION("disp0 - no filter") { - sirius::Image output; - REQUIRE_NOTHROW( - output = freq_resampler->Compute(zoom_ratio, disp0_image, {})); - REQUIRE(output.data.size() > 0); - REQUIRE(output.size == - (disp0_image.size * zoom_ratio.input_resolution())); - - LOG("tests", debug, "output size: {}, {}", output.size.row, - output.size.col); - - sirius::gdal::SaveImage( - output, "./output/disparity_periodic_smooth_no_filter.tif"); } } TEST_CASE("frequency resampler - real zoom", "[sirius]") { LOG_SET_LEVEL(trace); - auto zoom_ratio = sirius::ZoomRatio::Create(7, 4); + auto zoom_ratio = sirius::ZoomRatio::Create(3, 2); - // test input - auto dummy_image = sirius::tests::CreateDummyImage({256, 256}); - auto lena_image = sirius::gdal::LoadImage("./input/lena.jpg"); - auto disp0_image = sirius::gdal::LoadImage("./input/disparity.png"); + // test inputs + auto dummy_image = sirius::tests::CreateDummyImage({2048, 1536}); + auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); // output sirius::Image output; @@ -456,24 +330,7 @@ TEST_CASE("frequency resampler - real zoom", "[sirius]") { LOG("tests", debug, "output size: {}, {}", output.size.row, output.size.col); - - sirius::gdal::SaveImage(output, - "./output/lena_unzoom_7_4_no_filter.tif"); } - - /*SECTION("disp0 - no filter") { - sirius::Image output; - REQUIRE_NOTHROW( - output = freq_resampler->Compute(zoom_ratio, disp0_image, {})); - REQUIRE(output.data.size() > 0); - REQUIRE(output.size == disp0_image.size * zoom_ratio.ratio()); - - LOG("tests", debug, "output size: {}, {}", output.size.row, - output.size.col); - - sirius::gdal::SaveImage(output, - "./output/disparity_unzoom_7_4_no_filter.tif"); - }*/ } TEST_CASE("frequency resampler - example", "[sirius]") { @@ -482,13 +339,15 @@ TEST_CASE("frequency resampler - example", "[sirius]") { auto zoom_ratio_2_1 = sirius::ZoomRatio::Create(2, 1); auto zoom_ratio_7_4 = sirius::ZoomRatio::Create(7, 4); - sirius::Image image = sirius::tests::CreateDummyImage({256, 256}); + // test inputs + auto dummy_image = sirius::tests::CreateDummyImage({256, 256}); + // filters auto dirac_filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/dirac_filter.tiff"), + sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), zoom_ratio_7_4); - auto sinc_filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/sinc_zoom2_filter.tif"), + auto sinc_zoom2_filter = sirius::Filter::Create( + sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), zoom_ratio_2_1); sirius::IFrequencyResampler::UPtr freq_resampler = @@ -497,8 +356,9 @@ TEST_CASE("frequency resampler - example", "[sirius]") { sirius::FrequencyZoomStrategies::kZeroPadding); sirius::Image zoomed_image_7_4 = freq_resampler->Compute( - zoom_ratio_7_4, image, dirac_filter.padding(), dirac_filter); + zoom_ratio_7_4, dummy_image, dirac_filter.padding(), dirac_filter); sirius::Image zoomed_image_2_1 = freq_resampler->Compute( - zoom_ratio_2_1, image, sinc_filter.padding(), sinc_filter); + zoom_ratio_2_1, dummy_image, sinc_zoom2_filter.padding(), + sinc_zoom2_filter); } diff --git a/tests/functional_tests.cc b/tests/functional_tests.cc index 2978c5a5..730f5dc1 100644 --- a/tests/functional_tests.cc +++ b/tests/functional_tests.cc @@ -19,6 +19,7 @@ * along with Sirius. If not, see . */ +#include #include #include @@ -32,6 +33,8 @@ #include "sirius/utils/log.h" +#include "utils.h" + void GenerateZoomedImage(const std::string& output_filepath, const sirius::Image& image, const sirius::ZoomRatio& zoom_ratio, @@ -40,7 +43,9 @@ void GenerateZoomedImage(const std::string& output_filepath, LOG("functional_tests", debug, "* generating {}", output_filepath); auto output_image = freq_resampler.Compute(zoom_ratio, image, filter.padding(), filter); - sirius::gdal::SaveImage(output_image, output_filepath); + std::stringstream ss; + ss << sirius::tests::kOutputDirectoryPath << '/' << output_filepath; + sirius::gdal::SaveImage(output_image, ss.str()); } TEST_CASE("functional tests", "[sirius]") { @@ -55,7 +60,7 @@ TEST_CASE("functional tests", "[sirius]") { auto zoom_ratio_1_3 = sirius::ZoomRatio::Create(1, 3); auto zoom_ratio_210_120 = sirius::ZoomRatio::Create(210, 120); - auto lena_image = sirius::gdal::LoadImage("./input/lena.jpg"); + auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); auto lena_255_image = sirius::gdal::LoadImage("./input/lena-255x255.tif"); auto disp0_image = sirius::gdal::LoadImage("./input/disparity.png"); auto triangle_image = sirius::gdal::LoadImage("./input/triangle_10x10.tif"); @@ -64,10 +69,10 @@ TEST_CASE("functional tests", "[sirius]") { sirius::Filter no_filter; auto dirac_2_1_filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/dirac_filter.tiff"), + sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), zoom_ratio_2_1); auto sinc_2_1_filter = sirius::Filter::Create( - sirius::gdal::LoadImage("./filters/sinc_zoom2_filter.tif"), + sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), zoom_ratio_2_1); auto regular_zero_padding_freq_resampler = @@ -85,47 +90,44 @@ TEST_CASE("functional tests", "[sirius]") { sirius::ImageDecompositionPolicies::kPeriodicSmooth, sirius::FrequencyZoomStrategies::kZeroPadding); - GenerateZoomedImage("./output/TN1.tif", lena_image, zoom_ratio_1_1, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN2.tif", lena_image, zoom_ratio_2_1, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN3.tif", disp0_image, zoom_ratio_2_1, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN4.tif", disp0_image, zoom_ratio_2_1, - dirac_2_1_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN5.tif", lena_image, zoom_ratio_2_1, + GenerateZoomedImage("TN1.tif", lena_image, zoom_ratio_1_1, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TN2.tif", lena_image, zoom_ratio_2_1, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TN3.tif", disp0_image, zoom_ratio_2_1, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TN4.tif", disp0_image, zoom_ratio_2_1, dirac_2_1_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN6.tif", disp0_image, zoom_ratio_2_1, - sinc_2_1_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN7.tif", disp0_image, zoom_ratio_2_1, - sinc_2_1_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN8.tif", disp0_image, zoom_ratio_2_1, - sinc_2_1_filter, *regular_periodization_freq_resampler); - GenerateZoomedImage("./output/TN9.tif", triangle_image, zoom_ratio_3_1, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN10.tif", triangle_image, zoom_ratio_3_1, - no_filter, + GenerateZoomedImage("TN5.tif", lena_image, zoom_ratio_2_1, dirac_2_1_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TN6.tif", disp0_image, zoom_ratio_2_1, sinc_2_1_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TN7.tif", disp0_image, zoom_ratio_2_1, sinc_2_1_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TN8.tif", disp0_image, zoom_ratio_2_1, sinc_2_1_filter, + *regular_periodization_freq_resampler); + GenerateZoomedImage("TN9.tif", triangle_image, zoom_ratio_3_1, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TN10.tif", triangle_image, zoom_ratio_3_1, no_filter, *periodic_smooth_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN11.tif", door_image, zoom_ratio_3_1, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN12.tif", door_image, zoom_ratio_3_1, - no_filter, + GenerateZoomedImage("TN11.tif", door_image, zoom_ratio_3_1, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TN12.tif", door_image, zoom_ratio_3_1, no_filter, *periodic_smooth_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN13.tif", half_door_image, zoom_ratio_3_1, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TN14.tif", half_door_image, zoom_ratio_3_1, - no_filter, + GenerateZoomedImage("TN13.tif", half_door_image, zoom_ratio_3_1, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TN14.tif", half_door_image, zoom_ratio_3_1, no_filter, *periodic_smooth_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TF1.tif", lena_image, zoom_ratio_15_1, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TF2.tif", lena_255_image, zoom_ratio_2_1, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TF3.tif", lena_image, zoom_ratio_7_4, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TF4.tif", disp0_image, zoom_ratio_1_3, - no_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("./output/TF5.tif", triangle_image, zoom_ratio_210_120, + GenerateZoomedImage("TF1.tif", lena_image, zoom_ratio_15_1, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TF2.tif", lena_255_image, zoom_ratio_2_1, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TF3.tif", lena_image, zoom_ratio_7_4, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TF4.tif", disp0_image, zoom_ratio_1_3, no_filter, + *regular_zero_padding_freq_resampler); + GenerateZoomedImage("TF5.tif", triangle_image, zoom_ratio_210_120, no_filter, *periodic_smooth_zero_padding_freq_resampler); } diff --git a/tests/image_tests.cc b/tests/image_tests.cc index e430b7b4..301e9d06 100644 --- a/tests/image_tests.cc +++ b/tests/image_tests.cc @@ -254,14 +254,14 @@ TEST_CASE("Image - load well-formed image", "[sirius]") { sirius::Image image; REQUIRE_NOTHROW( - image = sirius::gdal::LoadImage("./filters/dirac_filter.tiff")); + image = sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath)); REQUIRE(!image.data.empty()); REQUIRE(image.size.row > 0); REQUIRE(image.size.col > 0); REQUIRE(image.IsLoaded()); REQUIRE_NOTHROW( - image = sirius::gdal::LoadImage("./filters/sinc_zoom2_filter.tif")); + image = sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath)); REQUIRE(!image.data.empty()); REQUIRE(image.size.row > 0); REQUIRE(image.size.col > 0); diff --git a/tests/utils.h b/tests/utils.h index cf779449..6d6b5ace 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -28,6 +28,17 @@ namespace sirius { namespace tests { +// filter paths +static constexpr char kDiracFilterPath[] = "./tests/filters/dirac_filter.tif"; +static constexpr char kSincZoom2FilterPath[] = + "./tests/filters/sinc_zoom2_filter.tif"; + +// image paths +static constexpr char kLenaImagePath[] = "./tests/input/lena.jpg"; + +// output path +static constexpr char kOutputDirectoryPath[] = "./tests/output"; + sirius::Image CreateDummyImage(const sirius::Size& size); } // namespace tests From cdd1205d5f1acb88be855ec55eb5db4d890641f6 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Thu, 23 Aug 2018 12:03:21 +0200 Subject: [PATCH 02/21] #33 support MacOS and improve Travis matrix * test OSX build with XCode 8.3 and 9.4 * test Linux build with GCC5, GCC6 and GCC7 on Ubuntu 14.04 * deploy (doc, docker image) only if tests succeeded --- .travis.yml | 105 ++++++++++++++---- .travis/build_install_sirius.sh | 30 ----- .travis/create_cmake_project.sh | 25 ----- .travis/deploy.sh | 20 ---- .travis/docker/before_build.sh | 19 ++++ .travis/docker/build.sh | 13 +++ .travis/docker/build_install_sirius.sh | 50 +++++++++ .travis/docker/deploy.sh | 58 ++++++++++ .travis/docker/install_deps.sh | 13 +++ .travis/linux/before_build.sh | 20 ++++ .travis/linux/build.sh | 46 ++++++++ .travis/on_success.sh | 35 ------ .travis/osx/before_build.sh | 20 ++++ .travis/osx/build.sh | 44 ++++++++ .travis/osx/install_deps.sh | 24 ++++ .travis/run_tests.sh | 5 +- README.md | 2 +- src/sirius/filter.cc | 6 +- src/sirius/gdal/error_code.h | 1 + .../utils/concurrent_queue_error_code.h | 1 + 20 files changed, 402 insertions(+), 135 deletions(-) delete mode 100755 .travis/build_install_sirius.sh delete mode 100755 .travis/create_cmake_project.sh delete mode 100755 .travis/deploy.sh create mode 100755 .travis/docker/before_build.sh create mode 100755 .travis/docker/build.sh create mode 100755 .travis/docker/build_install_sirius.sh create mode 100755 .travis/docker/deploy.sh create mode 100755 .travis/docker/install_deps.sh create mode 100755 .travis/linux/before_build.sh create mode 100755 .travis/linux/build.sh delete mode 100755 .travis/on_success.sh create mode 100755 .travis/osx/before_build.sh create mode 100755 .travis/osx/build.sh create mode 100755 .travis/osx/install_deps.sh diff --git a/.travis.yml b/.travis.yml index 72482259..9cf987ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,93 @@ -services: -- docker +# travis config checker: http://config.travis-ci.org/ + +language: cpp git: depth: false -before_install: -- docker pull ldumas/sirius_dockerfile:sirius_build -- docker pull ldumas/sirius_dockerfile:sirius_pages +before_install: git fetch --tags + +stages: + - test + - deploy + +matrix: + include: + # OS X + - &osx-template + stage: test + name: "OS X - Xcode 8.3" + os: osx + osx_image: xcode8.3 + install: .travis/osx/install_deps.sh + before_script: source .travis/osx/before_build.sh + script: .travis/osx/build.sh + + - <<: *osx-template + name: "OS X - Xcode 9.4" + osx_image: xcode9.4 + + # Ubuntu trusty + - &ubuntu-trusty-template + stage: test + name: "Ubuntu Trusty GCC 5" + os: linux + dist: trusty + before_script: source .travis/linux/before_build.sh + script: .travis/linux/build.sh + env: CXX_COMPILER=/usr/bin/g++-5 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - g++-5 + - libfftw3-dev + - libgdal-dev -before_script: -- export SRCDIR=/data -- export SIRIUS_BUILD_DIR=.build -- export INSTALL_DIR=/opt/sirius -- SIRIUS_VERSION=$(git describe 2> /dev/null); if [ "x${SIRIUS_VERSION}" = "x" ]; then SIRIUS_VERSION="0.0.0"; fi; export SIRIUS_VERSION; + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty GCC 6" + env: CXX_COMPILER=/usr/bin/g++-6 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - g++-6 + - libfftw3-dev + - libgdal-dev -script: -- docker run -v $TRAVIS_BUILD_DIR/:/data ldumas/sirius_dockerfile:sirius_build /bin/sh -c "source /opt/rh/devtoolset-6/enable; ${SRCDIR}/.travis/build_install_sirius.sh ${SRCDIR} ${INSTALL_DIR} ${SIRIUS_VERSION} ${TRAVIS_COMMIT}" -- export BUILD_CONTAINER_ID=$(docker ps --latest --quiet) + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty GCC 7" + env: CXX_COMPILER=/usr/bin/g++-7 + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - g++-7 + - libfftw3-dev + - libgdal-dev -after_success: -- .travis/on_success.sh + # Docker + - ¢os-7-template + name: "CentOS Docker" + stage: test + os: linux + services: + - docker + install: .travis/docker/install_deps.sh + before_script: source .travis/docker/before_build.sh + script: .travis/docker/build.sh -deploy: - provider: script - script: .travis/deploy.sh - on: - branch: master + - <<: *centos-7-template + name: "CentOS Docker - Deploy" + stage: deploy + deploy: + provider: script + script: .travis/docker/deploy.sh + on: + branch: master diff --git a/.travis/build_install_sirius.sh b/.travis/build_install_sirius.sh deleted file mode 100755 index d4401d02..00000000 --- a/.travis/build_install_sirius.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh - -# This script will build sirius project (library, binary and unit tests) -# Usage: ./build_sirius.sh PROJECT_DIR INSTALL_DIR SIRIUS_VERSION SIRIUS_REVISION_COMMIT - -set -ev - -PROJECT_DIR=$1 -INSTALL_DIR=$2 -SIRIUS_VERSION=$3 -SIRIUS_REVISION_COMMIT=$4 - -BUILD_DIR="${PROJECT_DIR}/.build" - -cd ${PROJECT_DIR} - -.travis/create_cmake_project.sh ${BUILD_DIR} ${INSTALL_DIR} ${SIRIUS_VERSION} ${SIRIUS_REVISION_COMMIT} - -cd ${BUILD_DIR} -make -j4 build_tests - -${PROJECT_DIR}/.travis/run_tests.sh - -make -j4 - -make install -cd ${PROJECT_DIR} -mv "${BUILD_DIR}/html" doxy_html -mv "${BUILD_DIR}/xml" doxy_xml -rm -rf ${BUILD_DIR} \ No newline at end of file diff --git a/.travis/create_cmake_project.sh b/.travis/create_cmake_project.sh deleted file mode 100755 index 7e514fc3..00000000 --- a/.travis/create_cmake_project.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -# This script will create the cmake sirius project build directory -# Usage: ./create_cmake_project.sh BUILD_DIR INSTALL_DIR SIRIUS_VERSION SIRIUS_REVISION_COMMIT - -set -ev - -BUILD_DIR=$1 -INSTALL_DIR=$2 -SIRIUS_VERSION=$3 -SIRIUS_REVISION_COMMIT=$4 - -[ ! -d ${BUILD_DIR} ] && mkdir ${BUILD_DIR}; -cd ${BUILD_DIR}; - -cmake3 .. -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ - -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ - -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ - -DENABLE_CACHE_OPTIMIZATION=ON \ - -DENABLE_GSL_CONTRACTS=OFF \ - -DENABLE_LOGS=ON \ - -DENABLE_UNIT_TESTS=ON \ - -DENABLE_DOCUMENTATION=ON -cd .. \ No newline at end of file diff --git a/.travis/deploy.sh b/.travis/deploy.sh deleted file mode 100755 index bd2d99e3..00000000 --- a/.travis/deploy.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -set -ev - -if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ]; then - - cat >Dockerfile < /dev/null || echo "") +if [[ "x${SIRIUS_VERSION}" = "x" ]]; then + SIRIUS_VERSION="0.0.0" +fi +export SIRIUS_VERSION + +# docker before script +export PROJECT_DIR=/data +export INSTALL_DIR=/opt/sirius \ No newline at end of file diff --git a/.travis/docker/build.sh b/.travis/docker/build.sh new file mode 100755 index 00000000..01ef6f89 --- /dev/null +++ b/.travis/docker/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'linux' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +# docker script +docker run -v $TRAVIS_BUILD_DIR/:/data ldumas/sirius_dockerfile:sirius_build /bin/sh -c "source /opt/rh/devtoolset-6/enable; ${PROJECT_DIR}/.travis/docker/build_install_sirius.sh ${PROJECT_DIR} ${INSTALL_DIR} ${SIRIUS_VERSION} ${TRAVIS_COMMIT}" ${DEPLOY} +export BUILD_CONTAINER_ID=$(docker ps --latest --quiet) \ No newline at end of file diff --git a/.travis/docker/build_install_sirius.sh b/.travis/docker/build_install_sirius.sh new file mode 100755 index 00000000..dadad01d --- /dev/null +++ b/.travis/docker/build_install_sirius.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# This script will build sirius project (library, binary and unit tests) and rrun unit tests +# Usage: ./build_sirius.sh PROJECT_DIR INSTALL_DIR SIRIUS_VERSION SIRIUS_REVISION_COMMIT + +set -ev + +PROJECT_DIR=$1 +INSTALL_DIR=$2 +SIRIUS_VERSION=$3 +SIRIUS_REVISION_COMMIT=$4 + +BUILD_DIR="${PROJECT_DIR}/.build" + +echo "SIRIUS_VERSION=${SIRIUS_VERSION}" +echo "SIRIUS_REVISION_COMMIT=${SIRIUS_REVISION_COMMIT}" +echo "PROJECT_DIR=${PROJECT_DIR}" +echo "INSTALL_DIR=${INSTALL_DIR}" +echo "BUILD_DIR=${BUILD_DIR}" + +[ ! -d ${BUILD_DIR} ] && mkdir -p ${BUILD_DIR}; +cd ${BUILD_DIR} + +# cmake3 is used instead of cmake due to CentOS docker image +cmake3 ${PROJECT_DIR} \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ + -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ + -DENABLE_CACHE_OPTIMIZATION=ON \ + -DENABLE_GSL_CONTRACTS=OFF \ + -DENABLE_LOGS=ON \ + -DENABLE_UNIT_TESTS=ON \ + -DENABLE_DOCUMENTATION=ON + +# build and run tests +make -j4 build_tests +${PROJECT_DIR}/.travis/run_tests.sh + +# build executables +make -j4 + +# install project +make -j4 install + +cd ${PROJECT_DIR} +mv "${BUILD_DIR}/html" doxy_html +mv "${BUILD_DIR}/xml" doxy_xml +rm -rf ${BUILD_DIR} \ No newline at end of file diff --git a/.travis/docker/deploy.sh b/.travis/docker/deploy.sh new file mode 100755 index 00000000..2b7164b3 --- /dev/null +++ b/.travis/docker/deploy.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# This script will update gh-pages with current doc and update sirius docker containers (sirius_built and sirius) + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'linux' ]]; + # noop if not linux + exit 0 +fi + +cd "${TRAVIS_BUILD_DIR}" + +# update gh-pages branch on CI-SI/SIRIUS repository only +if [ "x${TRAVIS_REPO_SLUG}" = "xCS-SI/SIRIUS" ]; then + openssl aes-256-cbc -K $encrypted_f05cf190aee2_key -iv $encrypted_f05cf190aee2_iv -in .travis/github_deploy_key.enc -out github_deploy_key -d + chmod 600 github_deploy_key + eval `ssh-agent -s` + ssh-add github_deploy_key + + mkdir doc/sphinx/build + docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_xml doc/sphinx/source/ ; cd doc/sphinx/ ; ln -s source/doxy_xml . " + docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data/doc/sphinx; make html" + docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_html doc/sphinx/build/" + cd $TRAVIS_BUILD_DIR/doc/sphinx/build/ + touch .nojekyll + git init + git checkout -b gh-pages + git add . + git -c user.name='travis' -c user.email='travis' commit -m init + git push -q -f git@github.com:${TRAVIS_REPO_SLUG}.git gh-pages &2>/dev/null + + cd "${TRAVIS_BUILD_DIR}" +fi + +if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ] && [ "x$DOCKER_USERNAME" != "x" ] && [ "x$DOCKER_PASSWORD" != "x" ]; then + docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD + + # update sirius built container on docker hub + docker commit --change "ENV PATH $PATH:/opt/sirius/bin" ${BUILD_CONTAINER_ID} sirius_built + docker tag sirius_built ldumas/sirius_dockerfile:sirius_built + docker push ldumas/sirius_dockerfile:sirius_built + + # create sirius container with sirius executable as entry point + cat >Dockerfile < /dev/null || echo "") +if [[ "x${SIRIUS_VERSION}" = "x" ]]; then + SIRIUS_VERSION="0.0.0" +fi +export SIRIUS_VERSION + +# osx before script +export PROJECT_DIR=${TRAVIS_BUILD_DIR} +export INSTALL_DIR=${PROJECT_DIR}/install_directory +mkdir ${INSTALL_DIR} \ No newline at end of file diff --git a/.travis/linux/build.sh b/.travis/linux/build.sh new file mode 100755 index 00000000..159379b4 --- /dev/null +++ b/.travis/linux/build.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'linux' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +SIRIUS_REVISION_COMMIT=${TRAVIS_COMMIT} +BUILD_DIR="${PROJECT_DIR}/.build" + +echo "SIRIUS_VERSION=${SIRIUS_VERSION}" +echo "SIRIUS_REVISION_COMMIT=${SIRIUS_REVISION_COMMIT}" +echo "PROJECT_DIR=${PROJECT_DIR}" +echo "INSTALL_DIR=${INSTALL_DIR}" +echo "BUILD_DIR=${BUILD_DIR}" +echo "CXX_COMPILER=${CXX_COMPILER}" + +[ ! -d ${BUILD_DIR} ] && mkdir -p ${BUILD_DIR}; +cd ${BUILD_DIR} + +# create makefile project +cmake ${PROJECT_DIR} \ + -G "Unix Makefiles" \ + -DCMAKE_CXX_COMPILER="${CXX_COMPILER}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ + -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ + -DENABLE_CACHE_OPTIMIZATION=ON \ + -DENABLE_GSL_CONTRACTS=ON \ + -DENABLE_LOGS=ON \ + -DENABLE_UNIT_TESTS=ON \ + -DENABLE_DOCUMENTATION=OFF + +# build and run tests +make -j4 build_tests +${PROJECT_DIR}/.travis/run_tests.sh + +# build executables +make -j4 + +# install project +make -j4 install \ No newline at end of file diff --git a/.travis/on_success.sh b/.travis/on_success.sh deleted file mode 100755 index f17e3498..00000000 --- a/.travis/on_success.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -set -ev - -if [ "x${TRAVIS_PULL_REQUEST}" = "xfalse" ] && [ "x$DOCKER_USERNAME" != "x" ] && [ "x$DOCKER_PASSWORD" != "x" ]; then - - # update sirius built container on docker hub - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD - docker commit --change "ENV PATH $PATH:/opt/sirius/bin" ${BUILD_CONTAINER_ID} sirius_built - docker tag sirius_built ldumas/sirius_dockerfile:sirius_built - docker push ldumas/sirius_dockerfile:sirius_built - - # update gh-pages branch on CI-SI/SIRIUS repository only - if [ "x${TRAVIS_REPO_SLUG}" = "xCS-SI/SIRIUS" ]; then - openssl aes-256-cbc -K $encrypted_f05cf190aee2_key -iv $encrypted_f05cf190aee2_iv -in .travis/github_deploy_key.enc -out github_deploy_key -d - chmod 600 github_deploy_key - eval `ssh-agent -s` - ssh-add github_deploy_key - - cd "$TRAVIS_BUILD_DIR" - mkdir doc/sphinx/build - docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_xml doc/sphinx/source/ ; cd doc/sphinx/ ; ln -s source/doxy_xml . " - docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data/doc/sphinx; make html" - docker run -v $TRAVIS_BUILD_DIR:/data ldumas/sirius_dockerfile:sirius_pages /bin/sh -c "cd /data; mv doxy_html doc/sphinx/build/" - cd $TRAVIS_BUILD_DIR/doc/sphinx/build/ - touch .nojekyll - git init - git checkout -b gh-pages - git add . - git -c user.name='travis' -c user.email='travis' commit -m init - git push -q -f git@github.com:${TRAVIS_REPO_SLUG}.git gh-pages &2>/dev/null - cd "$TRAVIS_BUILD_DIR" - fi - -fi diff --git a/.travis/osx/before_build.sh b/.travis/osx/before_build.sh new file mode 100755 index 00000000..4c93e5ad --- /dev/null +++ b/.travis/osx/before_build.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +if [[ ${TRAVIS_OS_NAME} != 'osx' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +cd ${TRAVIS_BUILD_DIR} + +SIRIUS_VERSION=$(git describe --tags 2> /dev/null || echo "") +if [[ "x${SIRIUS_VERSION}" = "x" ]]; then + SIRIUS_VERSION="0.0.0" +fi +export SIRIUS_VERSION + +# osx before script +export PROJECT_DIR=${TRAVIS_BUILD_DIR} +export INSTALL_DIR=${PROJECT_DIR}/install_directory +mkdir ${INSTALL_DIR} \ No newline at end of file diff --git a/.travis/osx/build.sh b/.travis/osx/build.sh new file mode 100755 index 00000000..e842dff2 --- /dev/null +++ b/.travis/osx/build.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'osx' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +SIRIUS_REVISION_COMMIT=${TRAVIS_COMMIT} +BUILD_DIR="${PROJECT_DIR}/.build" + +echo "SIRIUS_VERSION=${SIRIUS_VERSION}" +echo "SIRIUS_REVISION_COMMIT=${SIRIUS_REVISION_COMMIT}" +echo "PROJECT_DIR=${PROJECT_DIR}" +echo "INSTALL_DIR=${INSTALL_DIR}" +echo "BUILD_DIR=${BUILD_DIR}" + +[ ! -d ${BUILD_DIR} ] && mkdir -p ${BUILD_DIR}; +cd ${BUILD_DIR} + +# create makefile project +cmake ${PROJECT_DIR} \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ + -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ + -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ + -DENABLE_CACHE_OPTIMIZATION=ON \ + -DENABLE_GSL_CONTRACTS=ON \ + -DENABLE_LOGS=ON \ + -DENABLE_UNIT_TESTS=ON \ + -DENABLE_DOCUMENTATION=OFF + +# build and run tests +make -j4 build_tests +${PROJECT_DIR}/.travis/run_tests.sh + +# build executables +make -j4 + +# install project +make -j4 install \ No newline at end of file diff --git a/.travis/osx/install_deps.sh b/.travis/osx/install_deps.sh new file mode 100755 index 00000000..098d8db5 --- /dev/null +++ b/.travis/osx/install_deps.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -ev + +if [[ ${TRAVIS_OS_NAME} != 'osx' ]]; then + # unsupported platform + echo "unsupported platform" + exit 22 +fi + +# os x install + +# the following line should resolve the error: +# Possible conflicting files are: +# /usr/local/include/c++ -> /usr/local/Caskroom/oclint/0.13.1,17.4.0/oclint-0.13.1/include/c++ +brew cask uninstall oclint + +brew update + +# try to update gdal +if brew outdated --quiet | grep -qx gdal; then brew upgrade gdal || true; fi + +# install fftw +brew install fftw diff --git a/.travis/run_tests.sh b/.travis/run_tests.sh index 80e45815..b91c7ca5 100755 --- a/.travis/run_tests.sh +++ b/.travis/run_tests.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash unit_tests=( concurrent_queue_tests \ @@ -10,7 +10,8 @@ unit_tests=( echo "Unit tests:" -test_exit_status=0 +tests_exit_status=0 + for unit_test in "${unit_tests[@]}" do echo "* running ${unit_test}" diff --git a/README.md b/README.md index b636be6b..3192ac41 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ cmake --build . --target doc cmake --build . --target install ``` -See also `.travis.yml` and `.travis/create_cmake_project.sh` +See also `.travis.yml` and `.travis/*/build.sh` ## How to use diff --git a/src/sirius/filter.cc b/src/sirius/filter.cc index 9d5e3871..dc19368e 100644 --- a/src/sirius/filter.cc +++ b/src/sirius/filter.cc @@ -116,12 +116,14 @@ fftw::ComplexUPtr Filter::Process(const Size& image_size, LOG("filter", trace, "cache filter fft for image {}x{}", image_size.row, image_size.col); fftw::ComplexUPtr uptr_filter_fft = CreateFilterFFT(image_size); - filter_fft = {std::move(uptr_filter_fft)}; + filter_fft = {uptr_filter_fft.release(), uptr_filter_fft.get_deleter()}; filter_fft_cache_->Insert(image_size, filter_fft); } #else // no cache version - fftw::ComplexSPtr filter_fft{std::move(CreateFilterFFT(image_size))}; + fftw::ComplexUPtr uptr_filter_fft = CreateFilterFFT(image_size); + fftw::ComplexSPtr filter_fft = {uptr_filter_fft.release(), + uptr_filter_fft.get_deleter()}; #endif // SIRIUS_ENABLE_CACHE_OPTIMIZATION auto image_fft_span = utils::MakeSmartPtrArraySpan(image_fft, image_size); diff --git a/src/sirius/gdal/error_code.h b/src/sirius/gdal/error_code.h index 6a3c5a7f..d1f9d373 100644 --- a/src/sirius/gdal/error_code.h +++ b/src/sirius/gdal/error_code.h @@ -22,6 +22,7 @@ #ifndef SIRIUS_GDAL_ERROR_CODE_H_ #define SIRIUS_GDAL_ERROR_CODE_H_ +#include #include #include diff --git a/src/sirius/utils/concurrent_queue_error_code.h b/src/sirius/utils/concurrent_queue_error_code.h index 0c497f03..dfe8e888 100644 --- a/src/sirius/utils/concurrent_queue_error_code.h +++ b/src/sirius/utils/concurrent_queue_error_code.h @@ -22,6 +22,7 @@ #ifndef SIRIUS_UTILS_CONCURRENT_ERROR_CODE_H_ #define SIRIUS_UTILS_CONCURRENT_ERROR_CODE_H_ +#include #include // This implementation is inspired by From 1f3106e89b5dd1f9d45370f437e6a1a447abe437 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Thu, 23 Aug 2018 12:04:39 +0200 Subject: [PATCH 03/21] #32 support Windows and use AppVeyor for Windows testing * use vcpkg to build fftw3 and gdal dependencies * test build with Visual Studio 2017 Win64 --- .appveyor.yml | 74 ++++++++++ .../vcpkg/triplets/x64-windows-release.cmake | 10 ++ .appveyor/windows/install.cmd | 37 +++++ .travis/docker/build_install_sirius.sh | 20 ++- .travis/linux/build.sh | 19 ++- .travis/osx/build.sh | 19 ++- .travis/run_tests.sh | 29 ---- CMakeLists.txt | 30 +++- README.md | 47 ++++++- src/main.cc | 8 +- src/sirius/fftw/fftw.h | 1 + src/sirius/fftw/types.h | 12 +- src/sirius/filter.cc | 4 +- src/sirius/filter.h | 1 + src/sirius/gdal/resampled_output_stream.cc | 18 +-- src/sirius/gdal/stream_block.h | 8 +- src/sirius/gdal/wrapper.cc | 6 +- src/sirius/gdal/wrapper.h | 6 +- src/sirius/image.h | 2 +- src/sirius/image_streamer.h | 2 +- .../zoom_strategy/zero_padding_strategy.cc | 2 +- src/sirius/types.h | 4 +- src/sirius/utils/numeric.cc | 6 +- tests/filter_tests.cc | 5 +- tests/frequency_resampler_tests.cc | 23 ++- tests/functional_tests.cc | 133 ------------------ tests/image_tests.cc | 20 +-- tests/utils.h | 8 +- third_party/CMakeLists.txt | 12 +- 29 files changed, 316 insertions(+), 250 deletions(-) create mode 100644 .appveyor.yml create mode 100644 .appveyor/vcpkg/triplets/x64-windows-release.cmake create mode 100644 .appveyor/windows/install.cmd delete mode 100755 .travis/run_tests.sh delete mode 100644 tests/functional_tests.cc diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..0d56e394 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,74 @@ +# version format +version: 0.1.0.{build} + +# Build worker image (VM template) +image: Visual Studio 2017 + +# clone directory +clone_folder: C:\projects\SIRIUS + +# build platform, i.e. x86, x64, Any CPU. This setting is optional. +platform: x64 + +# build Configuration, i.e. Debug, Release, etc. +configuration: Release + +# environment variables +environment: + VCPKG_DIR: C:/Tools/vcpkg + VCPKG_TRIPLET: x64-windows-release + CMAKE_GENERATOR: Visual Studio 15 2017 Win64 + +# scripts that run after cloning repository +install: + # check if cache exists + - IF EXIST %VCPKG_DIR%\installed (set CACHE_EXISTS=1) ELSE (set CACHE_EXISTS=0) + # if the cache exists, CI will skip install scripts + - IF "%CACHE_EXISTS%"=="1" appveyor AddMessage "Project cache exists. Skipping install scripts. Delete the cache (REST API) and re-build the commit to update cached dependencies." -Category Warning + # if the cache does not exist, this build will only generate the project cache (fftw3 and gdal dependencies) + - IF "%CACHE_EXISTS%"=="0" appveyor AddMessage "This build will only generate the project cache (fftw3 and gdal dependencies)" -Category Warning + - IF "%CACHE_EXISTS%"=="0" .appveyor\windows\install.cmd + # terminate current build with success if cache did not exist + # this build should only create the cache since dependencies build time is quite long (~50min) and cache must be saved before AppVeyor timeout (1h) + # re-build the commit to actually build the project and run its tests + - IF "%CACHE_EXISTS%"=="0" appveyor AddMessage "This build only generated the project cache (fftw3 and gdal dependencies). Re-build the commit to actually build the project and run its tests" -Category Warning + - IF "%CACHE_EXISTS%"=="0" appveyor exit + +before_build: + - cd %APPVEYOR_BUILD_FOLDER% + - git fetch --tags + - mkdir %APPVEYOR_BUILD_FOLDER%\.build + - mkdir %APPVEYOR_BUILD_FOLDER%\.install-directory + - cd .build + - appveyor AddMessage "Creating Sirius Visual Studio solution..." + - cmake .. -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/.install-directory -DENABLE_CACHE_OPTIMIZATION=ON -DENABLE_GSL_CONTRACTS=ON -DENABLE_LOGS=ON -DENABLE_UNIT_TESTS=ON -DENABLE_DOCUMENTATION=OFF + +build: Script + +build_script: + - cd %APPVEYOR_BUILD_FOLDER%/.build + - appveyor AddMessage "Building sirius shared library..." + - cmake --build . --target libsirius --config %configuration% -- /m + - appveyor AddMessage "Building sirius static library..." + - cmake --build . --target libsirius-static --config %configuration% -- /m + - appveyor AddMessage "Building tests..." + - cmake --build . --target build_tests --config %configuration% -- /m + - appveyor AddMessage "Building sirius executable..." + - cmake --build . --target sirius --config %configuration% -- /m + - appveyor AddMessage "Installing sirius..." + - cmake --build . --target install --config %configuration% -- /m + +test_script: + - cd %APPVEYOR_BUILD_FOLDER%/.build + - appveyor AddMessage "Running tests..." + - ctest --output-on-failure + - appveyor AddMessage "Running sirius..." + - src\%configuration%\sirius.exe -h + +# to disable deployment +deploy: off + +# build cache to preserve files/folders between builds +# workaround to clear cache: https://github.com/appveyor/ci/issues/985 +cache: + - c:\tools\vcpkg\installed\ diff --git a/.appveyor/vcpkg/triplets/x64-windows-release.cmake b/.appveyor/vcpkg/triplets/x64-windows-release.cmake new file mode 100644 index 00000000..b0112543 --- /dev/null +++ b/.appveyor/vcpkg/triplets/x64-windows-release.cmake @@ -0,0 +1,10 @@ +# This vcpkg triplet is specialized to reduce build time for SIRIUS dependencies +# Appveyor build timeout is 1h and gdal and its dependencies take a long time to build... + +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE dynamic) + +# do not build debug version for packages (save space and build time for CI) +# see also: https://github.com/Microsoft/vcpkg/issues/143 +set(VCPKG_BUILD_TYPE release) \ No newline at end of file diff --git a/.appveyor/windows/install.cmd b/.appveyor/windows/install.cmd new file mode 100644 index 00000000..d4dde2ed --- /dev/null +++ b/.appveyor/windows/install.cmd @@ -0,0 +1,37 @@ +@echo on + +cd %VCPKG_DIR% + +appveyor AddMessage "Updating vcpkg knowledge base..." +:: update vcpkg knowledge base +:: git pull + +:: use fork to fix multiple issues with release only version of the dependencies +git remote add release-build https://github.com/hlysunnaram/vcpkg.git || goto error +git fetch release-build fix-vcpck-build-type || goto error +git checkout fix-vcpck-build-type || goto error + +:: need to execute bootstrapping in a new process since it exits the current cmd process at the end of its execution... +cmd.exe /C "bootstrap-vcpkg.bat -disableMetrics" || goto error +appveyor AddMessage "Updating vcpkg knowledge base done" + +:: force release version of the dependencies with vcpkg triplet (VCPKG_BUILD_TYPE="release") to +:: * reduce compilation time (appveyor timeout is 1h) +:: * reduce disk space for dependencies cache +copy "%APPVEYOR_BUILD_FOLDER%\.appveyor\vcpkg\triplets\%VCPKG_TRIPLET%.cmake" "%VCPKG_DIR%\triplets" || goto error +:: install fftw3 and gdal packages +:: force x64 build due to gdal package only available for x64 (libmysql only available for x64) +appveyor AddMessage "Installing FFTW3 dependency..." +vcpkg install fftw3:%VCPKG_TRIPLET% || goto error +appveyor AddMessage "Installing FFTW3 dependency done" + +appveyor AddMessage "Installing GDAL dependency..." +vcpkg install gdal:%VCPKG_TRIPLET% || goto error +appveyor AddMessage "Installing GDAL dependency done" + +exit /b 0 + +:error +set cmd_errorlevel=%errorlevel% +appveyor AddMessage "Dependencies were not generated. Check the logs and fix vcpkg generation" -Category Error +exit /b %cmd_errorlevel% \ No newline at end of file diff --git a/.travis/docker/build_install_sirius.sh b/.travis/docker/build_install_sirius.sh index dadad01d..05e94779 100755 --- a/.travis/docker/build_install_sirius.sh +++ b/.travis/docker/build_install_sirius.sh @@ -34,16 +34,28 @@ cmake3 ${PROJECT_DIR} \ -DENABLE_UNIT_TESTS=ON \ -DENABLE_DOCUMENTATION=ON -# build and run tests +# build sirius shared library +make -j4 libsirius + +# build sirius static library +make -j4 libsirius-static + +# build tests make -j4 build_tests -${PROJECT_DIR}/.travis/run_tests.sh -# build executables -make -j4 +# run tests +ctest --output-on-failure + +# build sirius executable +make -j4 sirius + +# execute sirius +./src/sirius -h # install project make -j4 install +# move generated API documentations outside build directory cd ${PROJECT_DIR} mv "${BUILD_DIR}/html" doxy_html mv "${BUILD_DIR}/xml" doxy_xml diff --git a/.travis/linux/build.sh b/.travis/linux/build.sh index 159379b4..6ef3ac26 100755 --- a/.travis/linux/build.sh +++ b/.travis/linux/build.sh @@ -35,12 +35,23 @@ cmake ${PROJECT_DIR} \ -DENABLE_UNIT_TESTS=ON \ -DENABLE_DOCUMENTATION=OFF -# build and run tests +# build sirius shared library +make -j4 libsirius + +# build sirius static library +make -j4 libsirius-static + +# build tests make -j4 build_tests -${PROJECT_DIR}/.travis/run_tests.sh -# build executables -make -j4 +# run tests +ctest --output-on-failure + +# build sirius executable +make -j4 sirius + +# execute sirius +./src/sirius -h # install project make -j4 install \ No newline at end of file diff --git a/.travis/osx/build.sh b/.travis/osx/build.sh index e842dff2..cd03a50b 100755 --- a/.travis/osx/build.sh +++ b/.travis/osx/build.sh @@ -33,12 +33,23 @@ cmake ${PROJECT_DIR} \ -DENABLE_UNIT_TESTS=ON \ -DENABLE_DOCUMENTATION=OFF -# build and run tests +# build sirius shared library +make -j4 libsirius + +# build sirius static library +make -j4 libsirius-static + +# build tests make -j4 build_tests -${PROJECT_DIR}/.travis/run_tests.sh -# build executables -make -j4 +# run tests +ctest --output-on-failure + +# build sirius executable +make -j4 sirius + +# execute sirius +./src/sirius -h # install project make -j4 install \ No newline at end of file diff --git a/.travis/run_tests.sh b/.travis/run_tests.sh deleted file mode 100755 index b91c7ca5..00000000 --- a/.travis/run_tests.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -unit_tests=( - concurrent_queue_tests \ - filter_tests \ - frequency_resampler_tests \ - image_tests \ - types_tests \ - utils_tests ) - -echo "Unit tests:" - -tests_exit_status=0 - -for unit_test in "${unit_tests[@]}" -do - echo "* running ${unit_test}" - ./tests/${unit_test} &> "./tests/output/${unit_test}.log" - exit_code=$? - if [ $exit_code -ne 0 ]; then - echo "........ FAILED" - cat ./tests/output/${unit_test}.log - tests_exit_status=1 - else - echo "........ OK" - fi -done - -exit ${tests_exit_status} diff --git a/CMakeLists.txt b/CMakeLists.txt index bda0c400..cc5b54d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,8 +24,8 @@ project(Sirius CXX) include(cmake/BuildType.cmake) -set(SIRIUS_VERSION "0.0.0" CACHE STRING "Sirius version") -set(SIRIUS_REVISION_COMMIT "sirius-no-revision-commit" CACHE STRING "Sirius revision commit") +set(SIRIUS_VERSION "" CACHE STRING "Sirius version") +set(SIRIUS_REVISION_COMMIT "" CACHE STRING "Sirius revision commit") option(ENABLE_SIRIUS_EXECUTABLE "Enable Sirius executable target" ON) option(ENABLE_CACHE_OPTIMIZATION "Enable cache optimization (FFTW plan, Filter FFT)" ON) @@ -34,6 +34,10 @@ option(ENABLE_GSL_CONTRACTS "Enable GSL contracts" OFF) option(ENABLE_DOCUMENTATION "Enable documentation generation" OFF) option(ENABLE_UNIT_TESTS "Enable unit test targets" OFF) +if (WIN32) + option(ENABLE_WINDOWS_STATIC_RUNTIME "Build libraries and executables with static runtime" OFF) +endif() + # path to additional cmake modules list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" @@ -45,7 +49,7 @@ if(GIT_FOUND AND "${SIRIUS_VERSION}" STREQUAL "" # try to extract version and revision commit from Git using git describe include(GetGitRevisionDescription) get_git_head_revision(SIRIUS_GIT_REFSPEC SIRIUS_REVISION_COMMIT) - git_describe(SIRIUS_GIT_DESCRIBE) + git_describe(SIRIUS_GIT_DESCRIBE --tags) if (SIRIUS_GIT_DESCRIBE MATCHES ".*NOTFOUND") message(STATUS "No recent tag in the commit history, set SIRIUS_VERSION to 0.0.0") set(SIRIUS_VERSION "0.0.0") @@ -78,6 +82,9 @@ message(STATUS "Enable logs: ${ENABLE_LOGS}") message(STATUS "Enable GSL contracts: ${ENABLE_GSL_CONTRACTS}") message(STATUS "Enable documentation: ${ENABLE_DOCUMENTATION}") message(STATUS "Enable unit tests: ${ENABLE_UNIT_TESTS}") +if (WIN32) + message(STATUS "Enable windows static runtime: ${ENABLE_WINDOWS_STATIC_RUNTIME}") +endif() set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -87,9 +94,24 @@ set(SIRIUS_CXX_FLAGS "") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # Clang or GCC - set(SIRIUS_CXX_FLAGS "-Wall -Wextra") + set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} -Wall -Wextra") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # MSVC compiler + # /D_USE_MATH_DEFINES will allow M_PI declaration from #include + set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} /D_USE_MATH_DEFINES") + # disable 4251 (from gdal): 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' + set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} /wd4251") + if (ENABLE_WINDOWS_STATIC_RUNTIME) + # extracted from https://gitlab.kitware.com/cmake/community/wikis/FAQ#dynamic-replace + foreach(flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif(${flag_var} MATCHES "/MD") + endforeach(flag_var) + endif() + endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIRIUS_CXX_FLAGS}") diff --git a/README.md b/README.md index 3192ac41..a34dd83c 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@

+ @@ -72,7 +73,7 @@ Sirius is using [CMake] to build its libraries and executables. ### Requirements -* C++14 compiler (GCC >= 5) +* C++14 compiler (GCC >= 5, XCode >=8.3, Visual Studio 2017) * [CMake] >=3.2 * [GDAL] development kit, >=2 * [FFTW] development kit, >=3 @@ -98,8 +99,9 @@ Sirius is using [CMake] to build its libraries and executables. * `ENABLE_LOGS`: set to `ON` if you want to build Sirius with the logs * `ENABLE_UNIT_TESTS`: set to `ON` if you want to build the unit tests * `ENABLE_DOCUMENTATION`: set to `ON` if you want to build the documentation +* `ENABLE_WINDOWS_STATIC_RUNTIME`: set to `ON` to statically link Sirius binaries with Windows runtime (Windows only) -Sirius version can be extracted from `git describe` and revision commit from `git rev-parse HEAD`. +Sirius version can be extracted from `git describe --tags` and revision commit from `git rev-parse HEAD`. If version and revision commit are not provided, [CMake] will try to extract them with the latter git commands. ### Example @@ -121,7 +123,45 @@ cmake --build . --target doc cmake --build . --target install ``` -See also `.travis.yml` and `.travis/*/build.sh` +See also [.travis.yml](.travis.yml) and `.travis/*/build.sh` + +### Example on Windows using [vcpkg] + +Clone [vcpkg repository][vcpkg] into VCPKG_DIR and bootstrap [vcpkg] project + +```sh +git clone https://github.com/Microsoft/vcpkg.git %VCPKG_DIR% +cd %VCPKG_DIR% +bootstrap-vcpkg.bat +``` + +Install FFTW3 and GDAL with [vcpkg] + +```sh +cd %VCPKG_DIR% +vcpkg install fftw3:x64-windows +vcpkg install gdal:x64-windows +``` + +Create Visual Studio solution and build sirius executable. + +```sh +:: CWD is Sirius root directory +mkdir .build +cd .build +cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows ^ + -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake ^ + -G "Visual Studio 15 2017 Win64" ^ + -DENABLE_SIRIUS_EXECUTABLE=ON ^ + -DENABLE_CACHE_OPTIMIZATION=ON ^ + -DENABLE_GSL_CONTRACTS=OFF ^ + -DENABLE_LOGS=ON ^ + -DENABLE_UNIT_TESTS=OFF ^ + -DENABLE_DOCUMENTATION=OFF +cmake --build . --target sirius --config Release +``` + +See also [.appveyor.yml](.appveyor.yml) ## How to use @@ -421,6 +461,7 @@ Sirius developers would like to thank: [CS-SI]: https://uk.c-s.fr/ "CS Systèmes d'information" [CMake]: https://cmake.org/ "CMake" +[vcpkg]: https://github.com/Microsoft/vcpkg "vcpkg" [GDAL]: http://www.gdal.org/ "Geospatial Data Abstraction Library" [FFTW]: http://www.fftw.org/ "Fastest Fourier Transform in the West" [Doxygen]: http://www.doxygen.org "Doxygen" diff --git a/src/main.cc b/src/main.cc index 479c06b2..ba7b92e1 100644 --- a/src/main.cc +++ b/src/main.cc @@ -133,7 +133,7 @@ int main(int argc, const char* argv[]) { LOG("sirius", info, "filter path: {}", params.filter_path); sirius::Point hp(params.hot_point_x, params.hot_point_y); filter = sirius::Filter::Create( - sirius::gdal::LoadImage(params.filter_path), zoom_ratio, hp, + sirius::gdal::Load(params.filter_path), zoom_ratio, hp, padding_type, params.filter_normalize); } @@ -192,7 +192,7 @@ void RunRegularMode(const sirius::IFrequencyResampler& frequency_resampler, const sirius::ZoomRatio& zoom_ratio, const CliParameters& params) { LOG("sirius", info, "regular mode"); - auto input_image = sirius::gdal::LoadImage(params.input_image_path); + auto input_image = sirius::gdal::Load(params.input_image_path); LOG("sirius", info, "input image '{}' ({}x{})", params.input_image_path, input_image.size.row, input_image.size.col); @@ -204,8 +204,8 @@ void RunRegularMode(const sirius::IFrequencyResampler& frequency_resampler, LOG("sirius", info, "resampled image '{}' ({}x{})", params.output_image_path, resampled_image.size.row, resampled_image.size.col); - sirius::gdal::SaveImage(resampled_image, params.output_image_path, - resampled_geo_ref); + sirius::gdal::Save(resampled_image, params.output_image_path, + resampled_geo_ref); } void RunStreamMode(const sirius::IFrequencyResampler& frequency_resampler, diff --git a/src/sirius/fftw/fftw.h b/src/sirius/fftw/fftw.h index 45af2455..56c89a38 100644 --- a/src/sirius/fftw/fftw.h +++ b/src/sirius/fftw/fftw.h @@ -66,6 +66,7 @@ class Fftw { /** * \brief Get a r2c fftw plan of the given size + * \param size plan size * \param in real input array complying with the size * \param out complex output array complying with the size * \return unique ptr to the created plan diff --git a/src/sirius/fftw/types.h b/src/sirius/fftw/types.h index de76173d..1ac463af 100644 --- a/src/sirius/fftw/types.h +++ b/src/sirius/fftw/types.h @@ -56,8 +56,9 @@ struct RealDeleter { using ComplexUPtr = std::unique_ptr<::fftw_complex[], detail::ComplexDeleter>; -#if (!defined(__GNUC__) && __cplusplus <= 201402L) || \ - (defined(__GNUC__) && __GNUC__ < 7 && __cplusplus <= 201402L) +#if (!defined(__GNUC__) && !defined(_MSC_VER) && __cplusplus <= 201402L) || \ + (defined(__GNUC__) && __GNUC__ < 7 && __cplusplus <= 201402L) || \ + (defined(_MSC_VER) && _MSC_VER < 1900) // C++14: no shared_ptr array syntax, classic definition using ComplexSPtr = std::shared_ptr<::fftw_complex>; @@ -66,7 +67,7 @@ using ComplexSPtr = std::shared_ptr<::fftw_complex>; // C++17: std::shared_ptr array syntax -// compiling with GCC7 breaks the build if C++14 syntax is used +// compiling with GCC7 and VS2017 breaks the build if C++14 syntax is used // definition of std::shared_ptr::element_type has changed: // <7: typedef _Tp element_type; // >=7: using element_type = typename remove_extent<_Tp>::type; @@ -78,8 +79,9 @@ using ComplexSPtr = std::shared_ptr<::fftw_complex>; using ComplexSPtr = std::shared_ptr<::fftw_complex[]>; -#endif // (!defined(__GNUC__) && __cplusplus <= 201402L) || - // (defined(__GNUC__) && __GNUC__ < 7 && __cplusplus <= 201402L) +#endif // (!defined(__GNUC__) && !defined(_MSC_VER) && __cplusplus <= 201402L) || + // (defined(__GNUC__) && __GNUC__ < 7 && __cplusplus <= 201402L) || + // (defined(_MSC_VER) && _MSC_VER < 1900) using RealUPtr = std::unique_ptr; diff --git a/src/sirius/filter.cc b/src/sirius/filter.cc index dc19368e..21f19b93 100644 --- a/src/sirius/filter.cc +++ b/src/sirius/filter.cc @@ -122,8 +122,8 @@ fftw::ComplexUPtr Filter::Process(const Size& image_size, #else // no cache version fftw::ComplexUPtr uptr_filter_fft = CreateFilterFFT(image_size); - fftw::ComplexSPtr filter_fft = {uptr_filter_fft.release(), - uptr_filter_fft.get_deleter()}; + fftw::ComplexSPtr filter_fft{uptr_filter_fft.release(), + uptr_filter_fft.get_deleter()}; #endif // SIRIUS_ENABLE_CACHE_OPTIMIZATION auto image_fft_span = utils::MakeSmartPtrArraySpan(image_fft, image_size); diff --git a/src/sirius/filter.h b/src/sirius/filter.h index 247b3c92..f816efc8 100644 --- a/src/sirius/filter.h +++ b/src/sirius/filter.h @@ -60,6 +60,7 @@ class Filter { * \brief Filter which is adapted specifically for a particular zoom ratio * \param filter_image image of the filter * \param zoom_ratio ratio on which the filter must be applied + * \param hot_point hot point coordinates * \param padding_type padding type * \param normalize normalize filter * diff --git a/src/sirius/gdal/resampled_output_stream.cc b/src/sirius/gdal/resampled_output_stream.cc index 3bfa6309..0a2f4903 100644 --- a/src/sirius/gdal/resampled_output_stream.cc +++ b/src/sirius/gdal/resampled_output_stream.cc @@ -35,10 +35,10 @@ ResampledOutputStream::ResampledOutputStream(const std::string& input_path, : zoom_ratio_(zoom_ratio) { auto input_dataset = gdal::LoadDataset(input_path); - int output_h = - std::ceil(input_dataset->GetRasterYSize() * zoom_ratio_.ratio()); - int output_w = - std::ceil(input_dataset->GetRasterXSize() * zoom_ratio_.ratio()); + int output_h = static_cast( + std::ceil(input_dataset->GetRasterYSize() * zoom_ratio_.ratio())); + int output_w = static_cast( + std::ceil(input_dataset->GetRasterXSize() * zoom_ratio_.ratio())); auto geo_ref = gdal::ComputeResampledGeoReference(input_path, zoom_ratio); output_dataset_ = @@ -48,12 +48,12 @@ ResampledOutputStream::ResampledOutputStream(const std::string& input_path, } void ResampledOutputStream::Write(StreamBlock&& block, std::error_code& ec) { - int out_row_idx = + int out_row_idx = static_cast( std::floor(block.row_idx * zoom_ratio_.input_resolution() / - static_cast(zoom_ratio_.output_resolution())); - int out_col_idx = + static_cast(zoom_ratio_.output_resolution()))); + int out_col_idx = static_cast( std::floor(block.col_idx * zoom_ratio_.input_resolution() / - static_cast(zoom_ratio_.output_resolution())); + static_cast(zoom_ratio_.output_resolution()))); LOG("resampled_output_stream", debug, "writing block ({},{}) to ({},{}) (size: {}x{})", block.row_idx, @@ -75,4 +75,4 @@ void ResampledOutputStream::Write(StreamBlock&& block, std::error_code& ec) { } } // namespace gdal -} // namespace sirus +} // namespace sirius diff --git a/src/sirius/gdal/stream_block.h b/src/sirius/gdal/stream_block.h index e58560b8..fd965cf5 100644 --- a/src/sirius/gdal/stream_block.h +++ b/src/sirius/gdal/stream_block.h @@ -37,10 +37,10 @@ struct StreamBlock { * \brief Instanciate a stream block from its block image and its position * in the input image * - * \param block_image image buffer of this block - * \param row_idx row index of the top left corner in the input image - * \param col_idx col index of the top left corner in the input image - * \param padding required filter padding + * \param i_block_image image buffer of this block + * \param i_row_idx row index of the top left corner in the input image + * \param i_col_idx col index of the top left corner in the input image + * \param i_padding required filter padding */ StreamBlock(Image&& i_block_image, int i_row_idx, int i_col_idx, const Padding& i_padding) diff --git a/src/sirius/gdal/wrapper.cc b/src/sirius/gdal/wrapper.cc index efcff5f6..e3ca331f 100644 --- a/src/sirius/gdal/wrapper.cc +++ b/src/sirius/gdal/wrapper.cc @@ -96,7 +96,7 @@ DatasetUPtr CreateDataset(const std::string& filepath, int w, int h, return dataset; } -Image LoadImage(const std::string& filepath) { +Image Load(const std::string& filepath) { if (filepath.empty()) { LOG("gdal", debug, "no filepath provided"); return {}; @@ -123,8 +123,8 @@ Image LoadImage(const std::string& filepath) { return {tmp_size, std::move(tmp_buffer)}; } -void SaveImage(const Image& image, const std::string& output_filepath, - const GeoReference& geoRef) { +void Save(const Image& image, const std::string& output_filepath, + const GeoReference& geoRef) { LOG("gdal", trace, "saving image into '{}'", output_filepath); // TODO: basic save implementation, test only ATM diff --git a/src/sirius/gdal/wrapper.h b/src/sirius/gdal/wrapper.h index 5fb75b82..baf9a420 100644 --- a/src/sirius/gdal/wrapper.h +++ b/src/sirius/gdal/wrapper.h @@ -49,10 +49,10 @@ struct GeoReference { bool is_initialized{false}; }; -Image LoadImage(const std::string& filepath); +Image Load(const std::string& filepath); -void SaveImage(const Image& image, const std::string& output_filepath, - const GeoReference& geoRef = {}); +void Save(const Image& image, const std::string& output_filepath, + const GeoReference& geoRef = {}); DatasetUPtr LoadDataset(const std::string& filepath); diff --git a/src/sirius/image.h b/src/sirius/image.h index bd4ee25d..c18bff2c 100644 --- a/src/sirius/image.h +++ b/src/sirius/image.h @@ -143,7 +143,7 @@ class Image { /** * \brief Create a padded image using mirroring on borders - * \param padding_size size of the margins + * \param mirror_padding size of the margins * \return generated image */ Image CreateMirrorPaddedImage(const Padding& mirror_padding) const; diff --git a/src/sirius/image_streamer.h b/src/sirius/image_streamer.h index 33d8d43d..49f1ba80 100644 --- a/src/sirius/image_streamer.h +++ b/src/sirius/image_streamer.h @@ -44,7 +44,7 @@ class ImageStreamer { * \param block_size stream block size * \param zoom_ratio zoom ratio * \param filter_metadata filter metadata - * \param padding_type filter padding type + * \param max_parallel_workers max parallel workers * \param max_parallel_workers max parallel workers to compute the zoom on * stream blocks */ diff --git a/src/sirius/resampler/zoom_strategy/zero_padding_strategy.cc b/src/sirius/resampler/zoom_strategy/zero_padding_strategy.cc index 93453911..307441cf 100644 --- a/src/sirius/resampler/zoom_strategy/zero_padding_strategy.cc +++ b/src/sirius/resampler/zoom_strategy/zero_padding_strategy.cc @@ -77,7 +77,7 @@ fftw::ComplexUPtr ZeroPaddingZoomStrategy::ZeroPadFFT( int image_row_count = image.size.row; int image_col_count = image.size.col; - int half_row_count = std::ceil(image_row_count / 2.0); + int half_row_count = static_cast(std::ceil(image_row_count / 2.0)); int fft_row_count = image_row_count; int fft_col_count = (image_col_count / 2) + 1; diff --git a/src/sirius/types.h b/src/sirius/types.h index d2cd27b0..191671d1 100644 --- a/src/sirius/types.h +++ b/src/sirius/types.h @@ -64,8 +64,8 @@ struct Size { Size operator*(double scale) const { Size result(*this); - result.row = std::ceil(result.row * scale); - result.col = std::ceil(result.col * scale); + result.row = static_cast(std::ceil(result.row * scale)); + result.col = static_cast(std::ceil(result.col * scale)); return result; } diff --git a/src/sirius/utils/numeric.cc b/src/sirius/utils/numeric.cc index 5f322682..db49823e 100644 --- a/src/sirius/utils/numeric.cc +++ b/src/sirius/utils/numeric.cc @@ -58,8 +58,10 @@ void FFTShift2D(const double* data, const Size& size, double* shifted_data) { } void IFFTShift2D(const double* data, const Size& size, double* shifted_data) { - int row_shift = std::ceil(static_cast(size.row) / 2); - int col_shift = std::ceil(static_cast(size.col) / 2); + int row_shift = + static_cast(std::ceil(static_cast(size.row) / 2)); + int col_shift = + static_cast(std::ceil(static_cast(size.col) / 2)); for (int row = 0; row < size.row; ++row) { int shifted_row = (row + row_shift) % size.row; diff --git a/tests/filter_tests.cc b/tests/filter_tests.cc index 539fea03..961735a8 100644 --- a/tests/filter_tests.cc +++ b/tests/filter_tests.cc @@ -49,7 +49,7 @@ TEST_CASE("filter - dirac filter", "[sirius]") { std::memset(complex_array.get(), 1, size.CellCount()); auto filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), zoom_ratio); + sirius::gdal::Load(sirius::tests::kDiracFilterPath), zoom_ratio); REQUIRE(filter.IsLoaded()); auto output = filter.Process(size, std::move(complex_array)); @@ -59,8 +59,7 @@ TEST_CASE("filter - sinc2 filter", "[sirius]") { LOG_SET_LEVEL(trace); auto zoom_ratio = sirius::ZoomRatio::Create(2, 1); auto filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), - zoom_ratio); + sirius::gdal::Load(sirius::tests::kSincZoom2FilterPath), zoom_ratio); sirius::Size regular_size = {100, 100}; sirius::Size large_size = {1000, 1000}; diff --git a/tests/frequency_resampler_tests.cc b/tests/frequency_resampler_tests.cc index 445cbc7e..1217a9b8 100644 --- a/tests/frequency_resampler_tests.cc +++ b/tests/frequency_resampler_tests.cc @@ -68,14 +68,13 @@ TEST_CASE("frequency resampler - classic decomposition - zero padding zoom", // test inputs auto dummy_image = sirius::tests::CreateDummyImage({2048, 1536}); - auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); + auto lena_image = sirius::gdal::Load(sirius::tests::kLenaImagePath); // filters auto dirac_filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), zoom_ratio); + sirius::gdal::Load(sirius::tests::kDiracFilterPath), zoom_ratio); auto sinc_zoom2_filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), - zoom_ratio); + sirius::gdal::Load(sirius::tests::kSincZoom2FilterPath), zoom_ratio); // output sirius::Image output; @@ -165,14 +164,13 @@ TEST_CASE("frequency resampler - classic - periodization zoom", "[sirius]") { // test inputs auto dummy_image = sirius::tests::CreateDummyImage({2048, 1536}); - auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); + auto lena_image = sirius::gdal::Load(sirius::tests::kLenaImagePath); // filters auto dirac_filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), zoom_ratio); + sirius::gdal::Load(sirius::tests::kDiracFilterPath), zoom_ratio); auto sinc_zoom2_filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), - zoom_ratio); + sirius::gdal::Load(sirius::tests::kSincZoom2FilterPath), zoom_ratio); // output sirius::Image output; @@ -262,7 +260,7 @@ TEST_CASE("frequency resampler - periodic smooth - zero padding", "[sirius]") { // test inputs auto dummy_image = sirius::tests::CreateDummyImage({2048, 1536}); - auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); + auto lena_image = sirius::gdal::Load(sirius::tests::kLenaImagePath); // output sirius::Image output; @@ -302,7 +300,7 @@ TEST_CASE("frequency resampler - real zoom", "[sirius]") { // test inputs auto dummy_image = sirius::tests::CreateDummyImage({2048, 1536}); - auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); + auto lena_image = sirius::gdal::Load(sirius::tests::kLenaImagePath); // output sirius::Image output; @@ -344,10 +342,9 @@ TEST_CASE("frequency resampler - example", "[sirius]") { // filters auto dirac_filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), - zoom_ratio_7_4); + sirius::gdal::Load(sirius::tests::kDiracFilterPath), zoom_ratio_7_4); auto sinc_zoom2_filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), + sirius::gdal::Load(sirius::tests::kSincZoom2FilterPath), zoom_ratio_2_1); sirius::IFrequencyResampler::UPtr freq_resampler = diff --git a/tests/functional_tests.cc b/tests/functional_tests.cc deleted file mode 100644 index 730f5dc1..00000000 --- a/tests/functional_tests.cc +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) - * - * This file is part of Sirius - * - * https://github.com/CS-SI/SIRIUS - * - * Sirius 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 3 of the License, or - * (at your option) any later version. - * - * Sirius 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 Sirius. If not, see . - */ - -#include -#include - -#include - -#include "sirius/filter.h" -#include "sirius/image.h" - -#include "sirius/frequency_resampler_factory.h" - -#include "sirius/gdal/wrapper.h" - -#include "sirius/utils/log.h" - -#include "utils.h" - -void GenerateZoomedImage(const std::string& output_filepath, - const sirius::Image& image, - const sirius::ZoomRatio& zoom_ratio, - const sirius::Filter& filter, - const sirius::IFrequencyResampler& freq_resampler) { - LOG("functional_tests", debug, "* generating {}", output_filepath); - auto output_image = - freq_resampler.Compute(zoom_ratio, image, filter.padding(), filter); - std::stringstream ss; - ss << sirius::tests::kOutputDirectoryPath << '/' << output_filepath; - sirius::gdal::SaveImage(output_image, ss.str()); -} - -TEST_CASE("functional tests", "[sirius]") { - LOG_SET_LEVEL(trace); - - sirius::ZoomRatio zoom_ratio_1_1; - - auto zoom_ratio_2_1 = sirius::ZoomRatio::Create(2, 1); - auto zoom_ratio_3_1 = sirius::ZoomRatio::Create(3, 1); - auto zoom_ratio_15_1 = sirius::ZoomRatio::Create(15, 1); - auto zoom_ratio_7_4 = sirius::ZoomRatio::Create(7, 4); - auto zoom_ratio_1_3 = sirius::ZoomRatio::Create(1, 3); - auto zoom_ratio_210_120 = sirius::ZoomRatio::Create(210, 120); - - auto lena_image = sirius::gdal::LoadImage(sirius::tests::kLenaImagePath); - auto lena_255_image = sirius::gdal::LoadImage("./input/lena-255x255.tif"); - auto disp0_image = sirius::gdal::LoadImage("./input/disparity.png"); - auto triangle_image = sirius::gdal::LoadImage("./input/triangle_10x10.tif"); - auto door_image = sirius::gdal::LoadImage("./input/door.tif"); - auto half_door_image = sirius::gdal::LoadImage("./input/half-door.tif"); - - sirius::Filter no_filter; - auto dirac_2_1_filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath), - zoom_ratio_2_1); - auto sinc_2_1_filter = sirius::Filter::Create( - sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath), - zoom_ratio_2_1); - - auto regular_zero_padding_freq_resampler = - sirius::FrequencyResamplerFactory::Create( - sirius::ImageDecompositionPolicies::kRegular, - sirius::FrequencyZoomStrategies::kZeroPadding); - - auto regular_periodization_freq_resampler = - sirius::FrequencyResamplerFactory::Create( - sirius::ImageDecompositionPolicies::kRegular, - sirius::FrequencyZoomStrategies::kPeriodization); - - auto periodic_smooth_zero_padding_freq_resampler = - sirius::FrequencyResamplerFactory::Create( - sirius::ImageDecompositionPolicies::kPeriodicSmooth, - sirius::FrequencyZoomStrategies::kZeroPadding); - - GenerateZoomedImage("TN1.tif", lena_image, zoom_ratio_1_1, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN2.tif", lena_image, zoom_ratio_2_1, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN3.tif", disp0_image, zoom_ratio_2_1, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN4.tif", disp0_image, zoom_ratio_2_1, - dirac_2_1_filter, *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN5.tif", lena_image, zoom_ratio_2_1, dirac_2_1_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN6.tif", disp0_image, zoom_ratio_2_1, sinc_2_1_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN7.tif", disp0_image, zoom_ratio_2_1, sinc_2_1_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN8.tif", disp0_image, zoom_ratio_2_1, sinc_2_1_filter, - *regular_periodization_freq_resampler); - GenerateZoomedImage("TN9.tif", triangle_image, zoom_ratio_3_1, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN10.tif", triangle_image, zoom_ratio_3_1, no_filter, - *periodic_smooth_zero_padding_freq_resampler); - GenerateZoomedImage("TN11.tif", door_image, zoom_ratio_3_1, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN12.tif", door_image, zoom_ratio_3_1, no_filter, - *periodic_smooth_zero_padding_freq_resampler); - GenerateZoomedImage("TN13.tif", half_door_image, zoom_ratio_3_1, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TN14.tif", half_door_image, zoom_ratio_3_1, no_filter, - *periodic_smooth_zero_padding_freq_resampler); - - GenerateZoomedImage("TF1.tif", lena_image, zoom_ratio_15_1, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TF2.tif", lena_255_image, zoom_ratio_2_1, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TF3.tif", lena_image, zoom_ratio_7_4, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TF4.tif", disp0_image, zoom_ratio_1_3, no_filter, - *regular_zero_padding_freq_resampler); - GenerateZoomedImage("TF5.tif", triangle_image, zoom_ratio_210_120, - no_filter, - *periodic_smooth_zero_padding_freq_resampler); -} diff --git a/tests/image_tests.cc b/tests/image_tests.cc index 301e9d06..8912a605 100644 --- a/tests/image_tests.cc +++ b/tests/image_tests.cc @@ -19,6 +19,8 @@ * along with Sirius. If not, see . */ +#include + #include #include "sirius/image.h" @@ -215,7 +217,7 @@ TEST_CASE("Image - load empty path", "[sirius]") { LOG_SET_LEVEL(trace); sirius::Image image; - REQUIRE_NOTHROW(image = sirius::gdal::LoadImage("")); + REQUIRE_NOTHROW(image = sirius::gdal::Load("")); REQUIRE(image.data.empty()); REQUIRE(image.size.row == 0); @@ -226,9 +228,8 @@ TEST_CASE("GDAL - load unknown image", "[sirius]") { LOG_SET_LEVEL(trace); sirius::Image image; - REQUIRE_THROWS_AS( - image = sirius::gdal::LoadImage("/this/is/not/a/file.ext"), - sirius::gdal::Exception); + REQUIRE_THROWS_AS(image = sirius::gdal::Load("/this/is/not/a/file.ext"), + sirius::gdal::Exception); REQUIRE(image.data.empty()); REQUIRE(image.size.row == 0); @@ -239,9 +240,8 @@ TEST_CASE("Image - load invalid image", "[sirius]") { LOG_SET_LEVEL(trace); sirius::Image image; - REQUIRE_THROWS_AS( - image = sirius::gdal::LoadImage("/this/is/not/a/file.ext"), - sirius::gdal::Exception); + REQUIRE_THROWS_AS(image = sirius::gdal::Load("/this/is/not/a/file.ext"), + sirius::gdal::Exception); REQUIRE(image.data.empty()); REQUIRE(image.size.row == 0); @@ -253,15 +253,15 @@ TEST_CASE("Image - load well-formed image", "[sirius]") { sirius::Image image; - REQUIRE_NOTHROW( - image = sirius::gdal::LoadImage(sirius::tests::kDiracFilterPath)); + REQUIRE_NOTHROW(image = + sirius::gdal::Load(sirius::tests::kDiracFilterPath)); REQUIRE(!image.data.empty()); REQUIRE(image.size.row > 0); REQUIRE(image.size.col > 0); REQUIRE(image.IsLoaded()); REQUIRE_NOTHROW( - image = sirius::gdal::LoadImage(sirius::tests::kSincZoom2FilterPath)); + image = sirius::gdal::Load(sirius::tests::kSincZoom2FilterPath)); REQUIRE(!image.data.empty()); REQUIRE(image.size.row > 0); REQUIRE(image.size.col > 0); diff --git a/tests/utils.h b/tests/utils.h index 6d6b5ace..15affd05 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -29,15 +29,15 @@ namespace sirius { namespace tests { // filter paths -static constexpr char kDiracFilterPath[] = "./tests/filters/dirac_filter.tif"; +static constexpr char kDiracFilterPath[] = "./filters/dirac_filter.tif"; static constexpr char kSincZoom2FilterPath[] = - "./tests/filters/sinc_zoom2_filter.tif"; + "./filters/sinc_zoom2_filter.tif"; // image paths -static constexpr char kLenaImagePath[] = "./tests/input/lena.jpg"; +static constexpr char kLenaImagePath[] = "./input/lena.jpg"; // output path -static constexpr char kOutputDirectoryPath[] = "./tests/output"; +static constexpr char kOutputDirectoryPath[] = "./output"; sirius::Image CreateDummyImage(const sirius::Size& size); diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index e990fd9e..3c1a2ee2 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -24,7 +24,11 @@ cmake_minimum_required(VERSION 3.2) # create library from package dependencies find_package(FFTW3 REQUIRED) -add_library(fftw3 SHARED IMPORTED GLOBAL) +if (WIN32) + add_library(fftw3 UNKNOWN IMPORTED GLOBAL) +else() + add_library(fftw3 SHARED IMPORTED GLOBAL) +endif() set_target_properties(fftw3 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIR} IMPORTED_LOCATION ${FFTW3_LIBRARY}) @@ -35,7 +39,11 @@ add_subdirectory(gsl) if (ENABLE_SIRIUS_EXECUTABLE OR ENABLE_UNIT_TESTS) find_package(GDAL REQUIRED) - add_library(gdal SHARED IMPORTED GLOBAL) + if (WIN32) + add_library(gdal UNKNOWN IMPORTED GLOBAL) + else() + add_library(gdal SHARED IMPORTED GLOBAL) + endif() set_target_properties(gdal PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${GDAL_INCLUDE_DIR} IMPORTED_LOCATION ${GDAL_LIBRARY} From 24afc160144ec3cf763d0e1c868010332aa10428 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Tue, 28 Aug 2018 10:10:46 +0200 Subject: [PATCH 04/21] #36 improve travis matrix * test Linux build with Clang 4, Clang 5 and Clang 6 * test Clang build using stdlibc++ and libc++ --- .travis.yml | 104 ++++++++++++++++++++++++++++++++++- .travis/linux/build.sh | 3 +- CMakeLists.txt | 19 +++++-- src/sirius/fftw/types.h | 13 ++--- src/sirius/image_streamer.cc | 8 +-- 5 files changed, 126 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9cf987ab..7c6f8568 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ matrix: # Ubuntu trusty - &ubuntu-trusty-template stage: test - name: "Ubuntu Trusty GCC 5" + name: "Ubuntu Trusty 14.04 GCC 5" os: linux dist: trusty before_script: source .travis/linux/before_build.sh @@ -47,7 +47,7 @@ matrix: - libgdal-dev - <<: *ubuntu-trusty-template - name: "Ubuntu Trusty GCC 6" + name: "Ubuntu Trusty 14.04 GCC 6" env: CXX_COMPILER=/usr/bin/g++-6 addons: apt: @@ -60,7 +60,7 @@ matrix: - libgdal-dev - <<: *ubuntu-trusty-template - name: "Ubuntu Trusty GCC 7" + name: "Ubuntu Trusty 14.04 GCC 7" env: CXX_COMPILER=/usr/bin/g++-7 addons: apt: @@ -72,6 +72,104 @@ matrix: - libfftw3-dev - libgdal-dev + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 4.0 - libc++" + env: CXX_COMPILER=/usr/bin/clang++-4.0 ENABLE_CLANG_LIBCPP=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-4.0 + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-4.0 + - libc++-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 5.0 - libc++" + env: CXX_COMPILER=/usr/bin/clang++-5.0 ENABLE_CLANG_LIBCPP=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-5.0 + - libc++-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 6.0 - libc++" + env: CXX_COMPILER=/usr/bin/clang++-6.0 ENABLE_CLANG_LIBCPP=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-6.0 + - libc++-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 4.0 - stdlibc++ 5" + env: CXX_COMPILER=/usr/bin/clang++-4.0 ENABLE_CLANG_LIBCPP=OFF + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-4.0 + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-4.0 + - libstdc++-5-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 5.0 - stdlibc++ 6" + env: CXX_COMPILER=/usr/bin/clang++-5.0 ENABLE_CLANG_LIBCPP=OFF + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-5.0 + - libstdc++-6-dev + - libfftw3-dev + - libgdal-dev + + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 6.0 - stdlibc++ 7" + env: CXX_COMPILER=/usr/bin/clang++-6.0 ENABLE_CLANG_LIBCPP=OFF + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-6.0 + - libstdc++-7-dev + - libfftw3-dev + - libgdal-dev + # Docker - ¢os-7-template name: "CentOS Docker" diff --git a/.travis/linux/build.sh b/.travis/linux/build.sh index 6ef3ac26..6e63b865 100755 --- a/.travis/linux/build.sh +++ b/.travis/linux/build.sh @@ -33,7 +33,8 @@ cmake ${PROJECT_DIR} \ -DENABLE_GSL_CONTRACTS=ON \ -DENABLE_LOGS=ON \ -DENABLE_UNIT_TESTS=ON \ - -DENABLE_DOCUMENTATION=OFF + -DENABLE_DOCUMENTATION=OFF \ + -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} # build sirius shared library make -j4 libsirius diff --git a/CMakeLists.txt b/CMakeLists.txt index cc5b54d5..5e5bdc43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,9 +34,12 @@ option(ENABLE_GSL_CONTRACTS "Enable GSL contracts" OFF) option(ENABLE_DOCUMENTATION "Enable documentation generation" OFF) option(ENABLE_UNIT_TESTS "Enable unit test targets" OFF) -if (WIN32) +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") option(ENABLE_WINDOWS_STATIC_RUNTIME "Build libraries and executables with static runtime" OFF) endif() +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + option(ENABLE_CLANG_LIBCPP "Force use of libc++" OFF) +endif() # path to additional cmake modules list(APPEND CMAKE_MODULE_PATH @@ -82,19 +85,24 @@ message(STATUS "Enable logs: ${ENABLE_LOGS}") message(STATUS "Enable GSL contracts: ${ENABLE_GSL_CONTRACTS}") message(STATUS "Enable documentation: ${ENABLE_DOCUMENTATION}") message(STATUS "Enable unit tests: ${ENABLE_UNIT_TESTS}") -if (WIN32) +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") message(STATUS "Enable windows static runtime: ${ENABLE_WINDOWS_STATIC_RUNTIME}") endif() +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + message(STATUS "Force use of libc++: ${ENABLE_CLANG_LIBCPP}") +endif() set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) # set CXX flags set(SIRIUS_CXX_FLAGS "") -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR - "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # Clang or GCC +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # GCC or Clang set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} -Wall -Wextra") + if (ENABLE_CLANG_LIBCPP) + set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} -stdlib=libc++") + endif() elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # MSVC compiler # /D_USE_MATH_DEFINES will allow M_PI declaration from #include @@ -111,7 +119,6 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") endif(${flag_var} MATCHES "/MD") endforeach(flag_var) endif() - endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIRIUS_CXX_FLAGS}") diff --git a/src/sirius/fftw/types.h b/src/sirius/fftw/types.h index 1ac463af..47c14e93 100644 --- a/src/sirius/fftw/types.h +++ b/src/sirius/fftw/types.h @@ -56,8 +56,8 @@ struct RealDeleter { using ComplexUPtr = std::unique_ptr<::fftw_complex[], detail::ComplexDeleter>; -#if (!defined(__GNUC__) && !defined(_MSC_VER) && __cplusplus <= 201402L) || \ - (defined(__GNUC__) && __GNUC__ < 7 && __cplusplus <= 201402L) || \ +//_GLIBCXX_RELEASE is only defined for stdlibc++ >= 7 +#if (!defined(_GLIBCXX_RELEASE) && !defined(_MSC_VER) && __cplusplus <= 201402L) || \ (defined(_MSC_VER) && _MSC_VER < 1900) // C++14: no shared_ptr array syntax, classic definition @@ -67,20 +67,19 @@ using ComplexSPtr = std::shared_ptr<::fftw_complex>; // C++17: std::shared_ptr array syntax -// compiling with GCC7 and VS2017 breaks the build if C++14 syntax is used +// compiling with stdlibc++ > 7 or VS2017 breaks the build if C++14 syntax is used // definition of std::shared_ptr::element_type has changed: // <7: typedef _Tp element_type; // >=7: using element_type = typename remove_extent<_Tp>::type; -// With GCC <7: +// With stdlibc <7: // std::shared_ptr::element_type <=> double[2] -// With GCC >=7: +// With stdlibc++ >=7 or VS2017: // std::shared_ptr::element_type <=> double // std::shared_ptr::element_type <=> double[2] using ComplexSPtr = std::shared_ptr<::fftw_complex[]>; -#endif // (!defined(__GNUC__) && !defined(_MSC_VER) && __cplusplus <= 201402L) || - // (defined(__GNUC__) && __GNUC__ < 7 && __cplusplus <= 201402L) || +#endif // (!defined(_GLIBCXX_RELEASE) && !defined(_MSC_VER) && __cplusplus <= 201402L) || // (defined(_MSC_VER) && _MSC_VER < 1900) using RealUPtr = std::unique_ptr; diff --git a/src/sirius/image_streamer.cc b/src/sirius/image_streamer.cc index 81d4056f..9e64a5ac 100644 --- a/src/sirius/image_streamer.cc +++ b/src/sirius/image_streamer.cc @@ -67,8 +67,8 @@ void ImageStreamer::RunMonothreadStream( break; } - block.buffer = std::move(frequency_resampler.Compute( - zoom_ratio_, block.buffer, block.padding, filter)); + block.buffer = frequency_resampler.Compute( + zoom_ratio_, block.buffer, block.padding, filter); std::error_code write_ec; output_stream_.Write(std::move(block), write_ec); @@ -129,8 +129,8 @@ void ImageStreamer::RunMultithreadStream( break; } - block.buffer = std::move(frequency_resampler.Compute( - zoom_ratio_, block.buffer, block.padding, filter)); + block.buffer = frequency_resampler.Compute( + zoom_ratio_, block.buffer, block.padding, filter); std::error_code push_output_ec; output_queue.Push(std::move(block), push_output_ec); From 3dbda10625b02e02cfefbe20e77420c35bfee6cb Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Fri, 31 Aug 2018 12:23:58 +0200 Subject: [PATCH 05/21] minor improvements * use VERSION option in CMake project function * use object library to create sirius shared and static libraries * fix Windows warnings --- CMakeLists.txt | 44 ++++------------ cmake/ExtractVersion.cmake | 32 ++++++++++++ src/CMakeLists.txt | 93 +++++++++++++++++---------------- src/sirius/fftw/exception.cc | 10 ++++ src/sirius/sirius.h.in | 2 +- tests/concurrent_queue_tests.cc | 4 +- 6 files changed, 105 insertions(+), 80 deletions(-) create mode 100644 cmake/ExtractVersion.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e5bdc43..1646f8fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,13 +20,20 @@ # cmake_minimum_required(VERSION 3.2) -project(Sirius CXX) -include(cmake/BuildType.cmake) +# path to additional cmake modules +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cmake-modules") set(SIRIUS_VERSION "" CACHE STRING "Sirius version") set(SIRIUS_REVISION_COMMIT "" CACHE STRING "Sirius revision commit") +include(cmake/BuildType.cmake) +include(cmake/ExtractVersion.cmake) + +project(Sirius VERSION ${SIRIUS_VERSION} LANGUAGES CXX) + option(ENABLE_SIRIUS_EXECUTABLE "Enable Sirius executable target" ON) option(ENABLE_CACHE_OPTIMIZATION "Enable cache optimization (FFTW plan, Filter FFT)" ON) option(ENABLE_LOGS "Enable logs" ON) @@ -41,42 +48,13 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") option(ENABLE_CLANG_LIBCPP "Force use of libc++" OFF) endif() -# path to additional cmake modules -list(APPEND CMAKE_MODULE_PATH - "${CMAKE_CURRENT_SOURCE_DIR}/cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/third_party/cmake-modules") - -find_package(Git QUIET) -if(GIT_FOUND AND "${SIRIUS_VERSION}" STREQUAL "" - AND "${SIRIUS_REVISION_COMMIT}" STREQUAL "") - # try to extract version and revision commit from Git using git describe - include(GetGitRevisionDescription) - get_git_head_revision(SIRIUS_GIT_REFSPEC SIRIUS_REVISION_COMMIT) - git_describe(SIRIUS_GIT_DESCRIBE --tags) - if (SIRIUS_GIT_DESCRIBE MATCHES ".*NOTFOUND") - message(STATUS "No recent tag in the commit history, set SIRIUS_VERSION to 0.0.0") - set(SIRIUS_VERSION "0.0.0") - else() - set(SIRIUS_VERSION "${SIRIUS_GIT_DESCRIBE}") - endif() - if (SIRIUS_REVISION_COMMIT MATCHES ".*NOTFOUND") - set(SIRIUS_REVISION_COMMIT "") - endif() -endif() - -if ("${SIRIUS_VERSION}" STREQUAL "") - set(SIRIUS_VERSION "0.0.0") -endif() -if ("${SIRIUS_REVISION_COMMIT}" STREQUAL "") - set(SIRIUS_REVISION_COMMIT "sirius-no-revision-commit") -endif() - if (CMAKE_BUILD_TYPE STREQUAL Debug) message(STATUS "Force GSL contracts on Debug build type") set(ENABLE_GSL_CONTRACTS "ON") endif() -message(STATUS "Sirius ${SIRIUS_VERSION} (${SIRIUS_REVISION_COMMIT})") +message(STATUS "CMake version: ${CMAKE_VERSION}") +message(STATUS "Sirius ${SIRIUS_EXTENDED_VERSION} (${SIRIUS_REVISION_COMMIT})") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Install directory: ${CMAKE_INSTALL_PREFIX}") message(STATUS "Enable Sirius executable: ${ENABLE_SIRIUS_EXECUTABLE}") diff --git a/cmake/ExtractVersion.cmake b/cmake/ExtractVersion.cmake new file mode 100644 index 00000000..5e6d7dec --- /dev/null +++ b/cmake/ExtractVersion.cmake @@ -0,0 +1,32 @@ +find_package(Git QUIET) +if(GIT_FOUND AND "${SIRIUS_VERSION}" STREQUAL "" + AND "${SIRIUS_REVISION_COMMIT}" STREQUAL "") + # try to extract version and revision commit from Git using git describe + include(GetGitRevisionDescription) + get_git_head_revision(SIRIUS_GIT_REFSPEC SIRIUS_REVISION_COMMIT) + git_describe(SIRIUS_GIT_DESCRIBE --tags) + if (SIRIUS_GIT_DESCRIBE MATCHES ".*NOTFOUND") + message(STATUS "No recent tag in the commit history. Specify the version using SIRIUS_VERSION variable.") + set(SIRIUS_VERSION "") + else() + set(SIRIUS_VERSION "${SIRIUS_GIT_DESCRIBE}") + endif() + if (SIRIUS_REVISION_COMMIT MATCHES ".*NOTFOUND") + message(STATUS "No revision commit in the commit history. Specify the version using SIRIUS_REVISION_COMMIT variable.") + set(SIRIUS_REVISION_COMMIT "") + endif() +endif() + +set(VERSION_REGEX "^([0-9]+)[.]([0-9]+)[.]([0-9]+)") + +if ("${SIRIUS_VERSION}" STREQUAL "") + message(STATUS "No version found. Setting project version to '0.0.0'") + set(SIRIUS_VERSION "0.0.0") +endif() +set(SIRIUS_EXTENDED_VERSION "${SIRIUS_VERSION}") +string(REGEX MATCH ${VERSION_REGEX} SIRIUS_VERSION ${SIRIUS_VERSION}) + +if ("${SIRIUS_REVISION_COMMIT}" STREQUAL "") + message(STATUS "No revision commit found. Setting project revision commit to 'sirius-no-revision-commit'") + set(SIRIUS_REVISION_COMMIT "sirius-no-revision-commit") +endif() \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13a16094..9815fbac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,7 +21,50 @@ cmake_minimum_required(VERSION 3.2) -set(LIBSIRIUS_SRC +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius) + +set(SIRIUS_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/sirius/sirius.h.in) +set(SIRIUS_CONFIG_OUT ${CMAKE_CURRENT_BINARY_DIR}/include/sirius/sirius.h) + +configure_file(${SIRIUS_CONFIG_IN} ${SIRIUS_CONFIG_OUT} @ONLY) + +# sirius shared library (use as a model for compilation options, definitions and link libraries properties of libsirius-objects and libsirius-static) +# TODO: when using cmake 3.12, add all those settings into libsirius-objects only and link libsirius and libsirius-static with libsirius-objects (https://cmake.org/cmake/help/v3.12/release/3.12.html#index-0-command:target_link_libraries) +add_library(libsirius SHARED $) +if (${ENABLE_LOGS}) + # build with logs + target_compile_definitions(libsirius PUBLIC SIRIUS_ENABLE_LOGS=1) +endif () +if (${ENABLE_CACHE_OPTIMIZATION}) + # build with cache + target_compile_definitions(libsirius PUBLIC SIRIUS_ENABLE_CACHE_OPTIMIZATION=1) +endif () + +target_include_directories(libsirius PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/include) +set_target_properties(libsirius PROPERTIES PREFIX "") +target_link_libraries(libsirius PUBLIC fftw3 spdlog gsl) +install(TARGETS libsirius DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sirius) + +# sirius static library +add_library(libsirius-static STATIC $) +set_target_properties(libsirius-static PROPERTIES PREFIX "") +target_include_directories(libsirius-static PUBLIC $) +target_compile_definitions(libsirius-static PUBLIC $) +target_compile_options(libsirius-static PUBLIC $) +target_link_libraries(libsirius-static $) +install(TARGETS libsirius-static DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sirius) + +# sirius objects library +add_library(libsirius-objects OBJECT EXCLUDE_FROM_ALL) +set_target_properties(libsirius-objects PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(libsirius-objects PUBLIC $) +target_compile_definitions(libsirius-objects PUBLIC $) +target_compile_options(libsirius-objects PUBLIC $) + +target_sources(libsirius-objects PRIVATE + sirius/sirius.h sirius/exception.h sirius/types.h @@ -73,58 +116,23 @@ set(LIBSIRIUS_SRC sirius/utils/numeric.h sirius/utils/numeric.cc) -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius) - -set(SIRIUS_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/sirius/sirius.h.in) -set(SIRIUS_CONFIG_OUT ${CMAKE_CURRENT_BINARY_DIR}/include/sirius/sirius.h) - -configure_file(${SIRIUS_CONFIG_IN} ${SIRIUS_CONFIG_OUT} @ONLY) - -list(APPEND LIBSIRIUS_INCLUDE_DIRS - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/include) -LIST(APPEND LIBSIRIUS_LINK_LIBS "fftw3" "spdlog" "gsl") - -add_library(libsirius SHARED ${LIBSIRIUS_SRC}) -set_property(TARGET libsirius PROPERTY POSITION_INDEPENDENT_CODE ON) -add_library(libsirius-static STATIC ${LIBSIRIUS_SRC}) -set_property(TARGET libsirius-static PROPERTY POSITION_INDEPENDENT_CODE ON) - -if (${ENABLE_LOGS}) - # build with logs - target_compile_definitions(libsirius PUBLIC SIRIUS_ENABLE_LOGS=1) - target_compile_definitions(libsirius-static PUBLIC SIRIUS_ENABLE_LOGS=1) -endif () - -if (${ENABLE_CACHE_OPTIMIZATION}) - # build with cache - target_compile_definitions(libsirius PUBLIC SIRIUS_ENABLE_CACHE_OPTIMIZATION=1) - target_compile_definitions(libsirius-static PUBLIC SIRIUS_ENABLE_CACHE_OPTIMIZATION=1) -endif () - -target_include_directories(libsirius PUBLIC ${LIBSIRIUS_INCLUDE_DIRS}) -target_include_directories(libsirius-static PUBLIC ${LIBSIRIUS_INCLUDE_DIRS}) - -target_link_libraries(libsirius ${LIBSIRIUS_LINK_LIBS}) -target_link_libraries(libsirius-static ${LIBSIRIUS_LINK_LIBS}) - -install(TARGETS libsirius DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sirius) -install(TARGETS libsirius-static DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sirius) # copy sirius include directory into install directory install(FILES ${SIRIUS_CONFIG_OUT} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius/sirius) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sirius DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius FILES_MATCHING PATTERN "*.h" PATTERN "*.txx") + # copy spdlog library into install directory install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/spdlog/spdlog/include/spdlog DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius) + # copy gsl library into install directory install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/gsl/gsl/include/gsl DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius) if (ENABLE_SIRIUS_EXECUTABLE OR ENABLE_UNIT_TESTS) - set(LIBSIRIUS_GDAL_SRC + add_library(libsirius-gdal-static STATIC EXCLUDE_FROM_ALL # gdal sirius/gdal/debug.h sirius/gdal/debug.cc @@ -140,21 +148,18 @@ if (ENABLE_SIRIUS_EXECUTABLE OR ENABLE_UNIT_TESTS) sirius/gdal/types.h sirius/gdal/wrapper.h sirius/gdal/wrapper.cc) - add_library(libsirius-gdal-static STATIC EXCLUDE_FROM_ALL ${LIBSIRIUS_GDAL_SRC}) - target_link_libraries(libsirius-gdal-static gdal libsirius-static) + target_link_libraries(libsirius-gdal-static PUBLIC gdal libsirius-static) endif () if (ENABLE_SIRIUS_EXECUTABLE) - set(SIRIUS_MAIN_SRC + add_executable(sirius # main sirius/image_streamer.h sirius/image_streamer.cc main.cc) - add_executable(sirius ${SIRIUS_MAIN_SRC}) target_link_libraries(sirius - libsirius-static libsirius-gdal-static cxxopts Threads::Threads) diff --git a/src/sirius/fftw/exception.cc b/src/sirius/fftw/exception.cc index 28b33a8a..dff07a4a 100644 --- a/src/sirius/fftw/exception.cc +++ b/src/sirius/fftw/exception.cc @@ -34,6 +34,16 @@ Exception::Exception(ErrorCode code) : sirius::Exception(""), err_code_(code) { const char* Exception::what() const noexcept { switch (err_code_) { + case ErrorCode::kSuccess: + return "success"; + case ErrorCode::kMemoryAllocationFailed: + return "memory allocation failed"; + case ErrorCode::kPlanCreationFailed: + return "plan creation failed"; + case ErrorCode::kComplexAllocationFailed: + return "complex allocation failed"; + case ErrorCode::kRealAllocationFailed: + return "real allocation failed"; default: return default_err_msg_.c_str(); } diff --git a/src/sirius/sirius.h.in b/src/sirius/sirius.h.in index 5c125d70..05376e42 100644 --- a/src/sirius/sirius.h.in +++ b/src/sirius/sirius.h.in @@ -24,7 +24,7 @@ namespace sirius { -static constexpr char kVersion[] = "@SIRIUS_VERSION@"; +static constexpr char kVersion[] = "@SIRIUS_EXTENDED_VERSION@"; static constexpr char kGitCommit[] = "@SIRIUS_REVISION_COMMIT@"; } // namespace sirius diff --git a/tests/concurrent_queue_tests.cc b/tests/concurrent_queue_tests.cc index 5d4dc544..43ae1289 100644 --- a/tests/concurrent_queue_tests.cc +++ b/tests/concurrent_queue_tests.cc @@ -61,7 +61,7 @@ TEST_CASE("concurrent queue - monothread push pop", "[sirius]") { TEST_CASE("concurrent queue - multithreaded push pop", "[sirius]") { sirius::utils::ConcurrentQueue queue(5); - static const std::size_t kValuesSize = 1000; + static const int kValuesSize = 1000; using Values = std::list; @@ -69,7 +69,7 @@ TEST_CASE("concurrent queue - multithreaded push pop", "[sirius]") { Values values_t2; Values values_t3; - for (std::size_t i = 0; i < kValuesSize; ++i) { + for (int i = 0; i < kValuesSize; ++i) { values_t1.push_back(kValuesSize + i); values_t2.push_back(2 * kValuesSize + i); values_t3.push_back(3 * kValuesSize + i); From 28f08548d61895008dd57e0783301c20ac1d9cc0 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Fri, 31 Aug 2018 16:08:25 +0200 Subject: [PATCH 06/21] update internal dependencies * catch2: 2.2.3 -> 2.3.0 * cxxopts: 2.1.0 -> 2.1.1 * gsl: 1.0.0 -> 2.0.0 * spdlog: 0.17.0 -> 1.1.0 --- README.md | 21 +- src/main.cc | 6 +- src/sirius/utils/log.cc | 18 +- src/sirius/utils/log.h | 2 - third_party/catch/VERSION | 1 + third_party/catch/catch/catch.hpp | 356 +- third_party/cxxopts/VERSION | 1 + .../cxxopts/cxxopts/include/cxxopts.hpp | 16 +- third_party/gsl/VERSION | 1 + third_party/gsl/gsl/include/gsl/gsl_algorithm | 8 +- third_party/gsl/gsl/include/gsl/gsl_assert | 30 +- third_party/gsl/gsl/include/gsl/gsl_byte | 45 +- third_party/gsl/gsl/include/gsl/gsl_util | 25 +- third_party/gsl/gsl/include/gsl/multi_span | 759 +- third_party/gsl/gsl/include/gsl/pointers | 13 +- third_party/gsl/gsl/include/gsl/span | 272 +- third_party/gsl/gsl/include/gsl/string_span | 178 +- third_party/spdlog/VERSION | 1 + .../spdlog/spdlog/include/spdlog/async.h | 87 + .../spdlog/include/spdlog/async_logger.h | 73 +- .../spdlog/spdlog/include/spdlog/common.h | 57 +- .../spdlog/include/spdlog/contrib/README.md | 1 - .../include/spdlog/contrib/sinks/.gitignore | 1 - .../spdlog/contrib/sinks/step_file_sink.h | 163 - .../include/spdlog/details/async_log_helper.h | 342 - .../spdlog/details/async_logger_impl.h | 117 +- .../include/spdlog/details/circular_q.h | 72 + .../include/spdlog/details/console_globals.h | 61 + .../include/spdlog/details/file_helper.h | 32 +- .../include/spdlog/details/fmt_helper.h | 130 + .../spdlog/include/spdlog/details/log_msg.h | 22 +- .../include/spdlog/details/logger_impl.h | 231 +- .../include/spdlog/details/mpmc_blocking_q.h | 87 +- .../spdlog/spdlog/include/spdlog/details/os.h | 55 +- .../spdlog/details/pattern_formatter.h | 772 ++ .../spdlog/details/pattern_formatter_impl.h | 707 -- .../include/spdlog/details/periodic_worker.h | 71 + .../spdlog/include/spdlog/details/registry.h | 275 +- .../include/spdlog/details/spdlog_impl.h | 278 - .../include/spdlog/details/thread_pool.h | 225 + .../include/spdlog/fmt/bundled/colors.h | 257 + .../spdlog/include/spdlog/fmt/bundled/core.h | 1578 ++++ .../include/spdlog/fmt/bundled/format-inl.h | 578 ++ .../include/spdlog/fmt/bundled/format.cc | 495 -- .../include/spdlog/fmt/bundled/format.h | 6610 ++++++++--------- .../include/spdlog/fmt/bundled/locale.h | 27 + .../include/spdlog/fmt/bundled/ostream.cc | 35 - .../include/spdlog/fmt/bundled/ostream.h | 152 +- .../include/spdlog/fmt/bundled/posix.cc | 241 - .../spdlog/include/spdlog/fmt/bundled/posix.h | 179 +- .../include/spdlog/fmt/bundled/printf.cc | 32 - .../include/spdlog/fmt/bundled/printf.h | 706 +- .../include/spdlog/fmt/bundled/ranges.h | 344 + .../spdlog/include/spdlog/fmt/bundled/time.h | 169 +- .../spdlog/spdlog/include/spdlog/fmt/fmt.h | 14 +- .../spdlog/spdlog/include/spdlog/formatter.h | 34 +- .../spdlog/spdlog/include/spdlog/logger.h | 118 +- .../include/spdlog/sinks/android_sink.h | 66 +- .../include/spdlog/sinks/ansicolor_sink.h | 110 +- .../spdlog/include/spdlog/sinks/base_sink.h | 50 +- .../include/spdlog/sinks/basic_file_sink.h | 66 + .../include/spdlog/sinks/daily_file_sink.h | 132 + .../spdlog/include/spdlog/sinks/dist_sink.h | 53 +- .../spdlog/include/spdlog/sinks/file_sinks.h | 255 - .../spdlog/include/spdlog/sinks/msvc_sink.h | 18 +- .../spdlog/include/spdlog/sinks/null_sink.h | 13 +- .../include/spdlog/sinks/ostream_sink.h | 30 +- .../include/spdlog/sinks/rotating_file_sink.h | 144 + .../spdlog/spdlog/include/spdlog/sinks/sink.h | 61 +- .../include/spdlog/sinks/stdout_color_sinks.h | 53 + .../include/spdlog/sinks/stdout_sinks.h | 105 +- .../spdlog/include/spdlog/sinks/syslog_sink.h | 67 +- .../include/spdlog/sinks/wincolor_sink.h | 105 +- .../include/spdlog/sinks/windebug_sink.h | 27 - .../spdlog/spdlog/include/spdlog/spdlog.h | 235 +- .../spdlog/spdlog/include/spdlog/tweakme.h | 51 +- .../spdlog/spdlog/include/spdlog/version.h | 12 + 77 files changed, 10538 insertions(+), 8296 deletions(-) create mode 100644 third_party/catch/VERSION create mode 100644 third_party/cxxopts/VERSION create mode 100644 third_party/gsl/VERSION create mode 100644 third_party/spdlog/VERSION create mode 100644 third_party/spdlog/spdlog/include/spdlog/async.h delete mode 100644 third_party/spdlog/spdlog/include/spdlog/contrib/README.md delete mode 100644 third_party/spdlog/spdlog/include/spdlog/contrib/sinks/.gitignore delete mode 100644 third_party/spdlog/spdlog/include/spdlog/contrib/sinks/step_file_sink.h delete mode 100644 third_party/spdlog/spdlog/include/spdlog/details/async_log_helper.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/details/circular_q.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/details/console_globals.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/details/fmt_helper.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/details/pattern_formatter.h delete mode 100644 third_party/spdlog/spdlog/include/spdlog/details/pattern_formatter_impl.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/details/periodic_worker.h delete mode 100644 third_party/spdlog/spdlog/include/spdlog/details/spdlog_impl.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/details/thread_pool.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/fmt/bundled/colors.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/fmt/bundled/core.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format-inl.h delete mode 100644 third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format.cc create mode 100644 third_party/spdlog/spdlog/include/spdlog/fmt/bundled/locale.h delete mode 100644 third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ostream.cc delete mode 100644 third_party/spdlog/spdlog/include/spdlog/fmt/bundled/posix.cc delete mode 100644 third_party/spdlog/spdlog/include/spdlog/fmt/bundled/printf.cc create mode 100644 third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ranges.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/sinks/basic_file_sink.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/sinks/daily_file_sink.h delete mode 100644 third_party/spdlog/spdlog/include/spdlog/sinks/file_sinks.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/sinks/rotating_file_sink.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/sinks/stdout_color_sinks.h delete mode 100644 third_party/spdlog/spdlog/include/spdlog/sinks/windebug_sink.h create mode 100644 third_party/spdlog/spdlog/include/spdlog/version.h diff --git a/README.md b/README.md index a34dd83c..b1c5c32f 100644 --- a/README.md +++ b/README.md @@ -81,9 +81,9 @@ Sirius is using [CMake] to build its libraries and executables. ### Internal dependencies -* [spdlog v0.17.0] -* [cxxopts v2.1.0] -* [GSL v1.0.0] +* [spdlog v1.1.0] +* [cxxopts v2.1.1] +* [GSL v2.0.0] * [catch v2.2.3] * [cmake-modules] @@ -466,12 +466,17 @@ Sirius developers would like to thank: [FFTW]: http://www.fftw.org/ "Fastest Fourier Transform in the West" [Doxygen]: http://www.doxygen.org "Doxygen" [FFTW3]: http://www.fftw.org/fftw-paper-ieee.pdf "Matteo Frigo and Steven G. Johnson, “The design and implementation of FFTW3,” Proc. IEEE 93 (2), 216231 (2005)" + [spdlog]: https://github.com/gabime/spdlog "spdlog" -[spdlog v0.17.0]: https://github.com/gabime/spdlog/tree/v0.17.0 "spdlog v0.17.0" +[spdlog v1.1.0]: https://github.com/gabime/spdlog/tree/v1.1.0 "spdlog v1.1.0" + [cxxopts]: https://github.com/jarro2783/cxxopts "cxxopts" -[cxxopts v2.1.0]: https://github.com/jarro2783/cxxopts/tree/v2.1.0 "cxxopts v2.1.0" +[cxxopts v2.1.1]: https://github.com/jarro2783/cxxopts/tree/v2.1.1 "cxxopts v2.1.1" + [GSL]: https://github.com/Microsoft/GSL "Guideline Support Library" -[GSL v1.0.0]: https://github.com/Microsoft/GSL/tree/v1.0.0 "Guideline Support Library v1.0.0" -[catch2]: https://github.com/catchorg/Catch2/tree/v2.2.3 "Catch2" -[catch v2.2.3]: https://github.com/catchorg/Catch2/tree/v2.2.3 "Catch v2.2.3" +[GSL v2.0.0]: https://github.com/Microsoft/GSL/tree/v2.0.0 "Guideline Support Library v2.0.0" + +[catch2]: https://github.com/catchorg/Catch2 "Catch2" +[catch v2.3.0]: https://github.com/catchorg/Catch2/tree/v2.3.0 "Catch2 v2.3.0" + [cmake-modules]: https://github.com/rpavlik/cmake-modules "CMake Modules" diff --git a/src/main.cc b/src/main.cc index ba7b92e1..06100a05 100644 --- a/src/main.cc +++ b/src/main.cc @@ -82,7 +82,7 @@ struct CliParameters { } }; -CliParameters GetCliParameters(int argc, const char* argv[]); +CliParameters GetCliParameters(int argc, char* argv[]); void RunRegularMode(const sirius::IFrequencyResampler& frequency_resampler, const sirius::Filter& filter, const sirius::ZoomRatio& zoom_ratio, @@ -92,7 +92,7 @@ void RunStreamMode(const sirius::IFrequencyResampler& frequency_resampler, const sirius::ZoomRatio& zoom_ratio, const CliParameters& params); -int main(int argc, const char* argv[]) { +int main(int argc, char* argv[]) { CliParameters params = GetCliParameters(argc, argv); if (params.help_requested || !params.parsed) { std::cerr << params.help_message; @@ -242,7 +242,7 @@ void RunStreamMode(const sirius::IFrequencyResampler& frequency_resampler, streamer.Stream(frequency_resampler, filter); } -CliParameters GetCliParameters(int argc, const char* argv[]) { +CliParameters GetCliParameters(int argc, char* argv[]) { CliParameters params; std::stringstream description; description << "Sirius " << sirius::kVersion << " (" << sirius::kGitCommit diff --git a/src/sirius/utils/log.cc b/src/sirius/utils/log.cc index 57d02303..669dda44 100644 --- a/src/sirius/utils/log.cc +++ b/src/sirius/utils/log.cc @@ -40,30 +40,22 @@ LoggerManager& LoggerManager::Instance() { } void LoggerManager::SetLogLevel(spdlog::level::level_enum level) { - log_level_ = level; spdlog::set_level(level); } LoggerManager::Logger* LoggerManager::Get(const std::string& channel) { std::lock_guard lock(loggers_mutex_); - if (loggers_.count(channel)) { - return loggers_[channel].get(); + auto logger = spdlog::get(channel); + if (logger) { + return logger.get(); } - std::vector sinks; #ifdef _WIN32 - sinks.push_back(std::make_shared()); + logger = spdlog::create(channel); #else - sinks.push_back( - std::make_shared()); + logger = spdlog::create(channel); #endif // _WIN32 - auto logger = - std::make_shared(channel, begin(sinks), end(sinks)); - logger->set_level(log_level_); - spdlog::register_logger(logger); - loggers_[channel] = logger; - return logger.get(); } diff --git a/src/sirius/utils/log.h b/src/sirius/utils/log.h index e6794080..03e94c6c 100644 --- a/src/sirius/utils/log.h +++ b/src/sirius/utils/log.h @@ -51,8 +51,6 @@ class LoggerManager { private: std::mutex loggers_mutex_; - std::map loggers_; - spdlog::level::level_enum log_level_{spdlog::level::info}; }; } // namespace utils diff --git a/third_party/catch/VERSION b/third_party/catch/VERSION new file mode 100644 index 00000000..cc6612c3 --- /dev/null +++ b/third_party/catch/VERSION @@ -0,0 +1 @@ +2.3.0 \ No newline at end of file diff --git a/third_party/catch/catch/catch.hpp b/third_party/catch/catch/catch.hpp index 28448ddb..bdc2f74a 100644 --- a/third_party/catch/catch/catch.hpp +++ b/third_party/catch/catch/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v2.2.3 - * Generated: 2018-06-06 23:11:57.601416 + * Catch v2.3.0 + * Generated: 2018-07-23 10:09:14.936841 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. @@ -14,8 +14,8 @@ #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 2 -#define CATCH_VERSION_PATCH 3 +#define CATCH_VERSION_MINOR 3 +#define CATCH_VERSION_PATCH 0 #ifdef __clang__ # pragma clang system_header @@ -30,13 +30,15 @@ # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC -# pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ + // GCC likes to warn on REQUIREs, and we cannot suppress them + // locally because g++'s support for _Pragma is lacking in older, + // still supported, versions # pragma GCC diagnostic ignored "-Wparentheses" # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-variable" @@ -55,7 +57,9 @@ # if defined(CATCH_CONFIG_DISABLE_MATCHERS) # undef CATCH_CONFIG_DISABLE_MATCHERS # endif -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif #endif #if !defined(CATCH_CONFIG_IMPL_ONLY) @@ -145,6 +149,12 @@ namespace Catch { # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic pop" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic push" ) \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic pop" ) + #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// @@ -176,6 +186,12 @@ namespace Catch { # define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #endif +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + //////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ @@ -245,6 +261,14 @@ namespace Catch { # define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS #endif +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS @@ -253,6 +277,10 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS # define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS #endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS +#endif // end catch_compiler_capabilities.h #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line @@ -551,7 +579,7 @@ struct AutoReg : NonCopyable { #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ @@ -580,7 +608,7 @@ struct AutoReg : NonCopyable { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS // end catch_test_registry.h @@ -1776,7 +1804,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( false ) @@ -1848,19 +1876,22 @@ namespace Catch { namespace Catch { struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name ); + + // Deprecated SectionInfo ( SourceLineInfo const& _lineInfo, std::string const& _name, - std::string const& _description = std::string() ); + std::string const& ) : SectionInfo( _lineInfo, _name ) {} std::string name; - std::string description; + std::string description; // !Deprecated: this will always be empty SourceLineInfo lineInfo; }; struct SectionEndInfo { - SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ); - SectionInfo sectionInfo; Counts prevAssertions; double durationInSeconds; @@ -1914,8 +1945,15 @@ namespace Catch { } // end namespace Catch - #define INTERNAL_CATCH_SECTION( ... ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) +#define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS + +#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ + CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS // end catch_section.h // start catch_benchmark.h @@ -2102,6 +2140,8 @@ namespace Detail { static Approx custom(); + Approx operator-() const; + template ::value>::type> Approx operator()( T const& value ) { Approx approx( static_cast(value) ); @@ -2197,7 +2237,12 @@ namespace Detail { double m_scale; double m_value; }; -} +} // end namespace Detail + +namespace literals { + Detail::Approx operator "" _a(long double val); + Detail::Approx operator "" _a(unsigned long long val); +} // end namespace literals template<> struct StringMaker { @@ -2962,7 +3007,7 @@ namespace Catch { std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); const char* className = class_getName( cls ); - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo("",0) ) ); + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) ); noTestMethods++; } } @@ -3586,6 +3631,7 @@ namespace Catch { struct ReporterPreferences { bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; }; template @@ -4676,6 +4722,12 @@ namespace Detail { return Approx( 0 ); } + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + std::string Approx::toString() const { ReusableStringStream rss; rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; @@ -4690,6 +4742,15 @@ namespace Detail { } // end namespace Detail +namespace literals { + Detail::Approx operator "" _a(long double val) { + return Detail::Approx(val); + } + Detail::Approx operator "" _a(unsigned long long val) { + return Detail::Approx(val); + } +} // end namespace literals + std::string StringMaker::convert(Catch::Detail::Approx const& value) { return value.toString(); } @@ -4998,9 +5059,11 @@ namespace Catch { // end catch_run_context.h namespace Catch { - auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { - expr.streamReconstructedExpression( os ); - return os; + namespace { + auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { + expr.streamReconstructedExpression( os ); + return os; + } } LazyExpression::LazyExpression( bool isNegated ) @@ -7530,8 +7593,11 @@ namespace Catch { using Reporters = std::vector; Reporters m_listeners; IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; public: + ListeningReporter(); + void addListener( IStreamingReporterPtr&& listener ); void addReporter( IStreamingReporterPtr&& reporter ); @@ -8260,6 +8326,8 @@ namespace Catch { auto str() const -> std::string; }; +#if defined(CATCH_CONFIG_NEW_CAPTURE) + // Windows's implementation of std::tmpfile is terrible (it tries // to create a file inside system folder, thus requiring elevated // privileges for the binary), so we have to use tmpnam(_s) and @@ -8303,6 +8371,8 @@ namespace Catch { std::string& m_stderrDest; }; +#endif + } // end namespace Catch #endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H @@ -8313,13 +8383,15 @@ namespace Catch { #include #include -#if defined(_MSC_VER) -#include //_dup and _dup2 -#define dup _dup -#define dup2 _dup2 -#define fileno _fileno -#else -#include // dup and dup2 +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #include //_dup and _dup2 + #define dup _dup + #define dup2 _dup2 + #define fileno _fileno + #else + #include // dup and dup2 + #endif #endif namespace Catch { @@ -8345,6 +8417,8 @@ namespace Catch { {} auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) TempFile::TempFile() { if (tmpnam_s(m_buffer)) { @@ -8417,12 +8491,16 @@ namespace Catch { m_stderrDest += m_stderrFile.getContents(); } +#endif // CATCH_CONFIG_NEW_CAPTURE + } // namespace Catch -#if defined(_MSC_VER) -#undef dup -#undef dup2 -#undef fileno +#if defined(CATCH_CONFIG_NEW_CAPTURE) + #if defined(_MSC_VER) + #undef dup + #undef dup2 + #undef fileno + #endif #endif // end catch_output_redirect.cpp // start catch_random_number_generator.cpp @@ -8430,53 +8508,36 @@ namespace Catch { // start catch_random_number_generator.h #include +#include namespace Catch { struct IConfig; + std::mt19937& rng(); void seedRng( IConfig const& config ); - unsigned int rngSeed(); - struct RandomNumberGenerator { - using result_type = unsigned int; - - static constexpr result_type (min)() { return 0; } - static constexpr result_type (max)() { return 1000000; } - - result_type operator()( result_type n ) const; - result_type operator()() const; - - template - static void shuffle( V& vector ) { - RandomNumberGenerator rng; - std::shuffle( vector.begin(), vector.end(), rng ); - } - }; - } // end catch_random_number_generator.h -#include - namespace Catch { + std::mt19937& rng() { + static std::mt19937 s_rng; + return s_rng; + } + void seedRng( IConfig const& config ) { - if( config.rngSeed() != 0 ) + if( config.rngSeed() != 0 ) { std::srand( config.rngSeed() ); + rng().seed( config.rngSeed() ); + } } + unsigned int rngSeed() { return getCurrentContext().getConfig()->rngSeed(); } - - RandomNumberGenerator::result_type RandomNumberGenerator::operator()( result_type n ) const { - return std::rand() % n; - } - RandomNumberGenerator::result_type RandomNumberGenerator::operator()() const { - return std::rand() % (max)(); - } - } // end catch_random_number_generator.cpp // start catch_registry_hub.cpp @@ -8759,7 +8820,7 @@ namespace Catch { m_config(_config), m_reporter(std::move(reporter)), m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, - m_includeSuccessfulResults( m_config->includeSuccessfulResults() ) + m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) { m_context.setRunner(this); m_context.setConfig(m_config); @@ -8949,7 +9010,7 @@ namespace Catch { // Recreate section for test case (as we will lose the one that was in scope) auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); Counts assertions; assertions.failed = 1; @@ -8987,7 +9048,7 @@ namespace Catch { void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); m_reporter->sectionStarting(testCaseSection); Counts prevAssertions = m_totals.assertions; double duration = 0; @@ -9182,7 +9243,7 @@ namespace Catch { Section::~Section() { if( m_sectionIncluded ) { - SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); + SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() }; if( uncaught_exceptions() ) getResultCapture().sectionEndedEarly( endInfo ); else @@ -9203,17 +9264,11 @@ namespace Catch { SectionInfo::SectionInfo ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description ) + std::string const& _name ) : name( _name ), - description( _description ), lineInfo( _lineInfo ) {} - SectionEndInfo::SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) - : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} - } // end namespace Catch // end catch_section_info.cpp // start catch_session.cpp @@ -9774,6 +9829,12 @@ namespace Catch { namespace Catch { + namespace { + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } + } + bool startsWith( std::string const& s, std::string const& prefix ) { return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); } @@ -9789,9 +9850,6 @@ namespace Catch { bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } - char toLowerCh(char c) { - return static_cast( std::tolower( c ) ); - } void toLowerInPlace( std::string& s ) { std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); } @@ -10033,31 +10091,33 @@ namespace Catch { namespace Catch { - TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, '.' ) || - tag == "!hide" ) - return TestCaseInfo::IsHidden; - else if( tag == "!throws" ) - return TestCaseInfo::Throws; - else if( tag == "!shouldfail" ) - return TestCaseInfo::ShouldFail; - else if( tag == "!mayfail" ) - return TestCaseInfo::MayFail; - else if( tag == "!nonportable" ) - return TestCaseInfo::NonPortable; - else if( tag == "!benchmark" ) - return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); - else - return TestCaseInfo::None; - } - bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast(tag[0]) ); - } - void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - CATCH_ENFORCE( !isReservedTag(tag), - "Tag name: [" << tag << "] is not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n" - << _lineInfo ); + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else if( tag == "!benchmark" ) + return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } + bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast(tag[0]) ); + } + void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + CATCH_ENFORCE( !isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n" + << _lineInfo ); + } } TestCase makeTestCase( ITestInvoker* _testCase, @@ -10205,7 +10265,7 @@ namespace Catch { break; case RunTests::InRandomOrder: seedRng( config ); - RandomNumberGenerator::shuffle( sorted ); + std::shuffle( sorted.begin(), sorted.end(), rng() ); break; case RunTests::InDeclarationOrder: // already in declaration order @@ -10348,8 +10408,8 @@ namespace TestCaseTracking { TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const { return - tracker->nameAndLocation().name == m_nameAndLocation.name && - tracker->nameAndLocation().location == m_nameAndLocation.location; + tracker->nameAndLocation().location == m_nameAndLocation.location && + tracker->nameAndLocation().name == m_nameAndLocation.name; } TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) @@ -10733,34 +10793,36 @@ namespace Catch { return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); } - auto estimateClockResolution() -> uint64_t { - uint64_t sum = 0; - static const uint64_t iterations = 1000000; + namespace { + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; - auto startTime = getCurrentNanosecondsSinceEpoch(); + auto startTime = getCurrentNanosecondsSinceEpoch(); - for( std::size_t i = 0; i < iterations; ++i ) { + for( std::size_t i = 0; i < iterations; ++i ) { - uint64_t ticks; - uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); - do { - ticks = getCurrentNanosecondsSinceEpoch(); - } while( ticks == baseTicks ); + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while( ticks == baseTicks ); - auto delta = ticks - baseTicks; - sum += delta; + auto delta = ticks - baseTicks; + sum += delta; - // If we have been calibrating for over 3 seconds -- the clock - // is terrible and we should move on. - // TBD: How to signal that the measured resolution is probably wrong? - if (ticks > startTime + 3 * nanosecondsInSecond) { - return sum / i; + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / i; + } } - } - // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers - // - and potentially do more iterations if there's a high variance. - return sum/iterations; + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum/iterations; + } } auto getEstimatedClockResolution() -> uint64_t { static auto s_resolution = estimateClockResolution(); @@ -11004,8 +11066,8 @@ std::string StringMaker::convert(double value) { std::string ratio_string::symbol() { return "a"; } std::string ratio_string::symbol() { return "f"; } -std::string ratio_string::symbol() { return "p"; } -std::string ratio_string::symbol() { return "n"; } +std::string ratio_string::symbol() { return "p"; } +std::string ratio_string::symbol() { return "n"; } std::string ratio_string::symbol() { return "u"; } std::string ratio_string::symbol() { return "m"; } @@ -11117,7 +11179,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 2, 2, 3, "", 0 ); + static Version version( 2, 3, 0, "", 0 ); return version; } @@ -11719,9 +11781,7 @@ class AssertionPrinter { } ReporterPreferences CompactReporter::getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; + return m_reporterPrefs; } void CompactReporter::noMatchingTestCases( std::string const& spec ) { @@ -12436,6 +12496,7 @@ namespace Catch { xml( _config.stream() ) { m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; } JunitReporter::~JunitReporter() {} @@ -12626,6 +12687,11 @@ namespace Catch { namespace Catch { + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } + void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) { m_listeners.push_back( std::move( listener ) ); } @@ -12633,10 +12699,11 @@ namespace Catch { void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) { assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); m_reporter = std::move( reporter ); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; } ReporterPreferences ListeningReporter::getPreferences() const { - return m_reporter->getPreferences(); + return m_preferences; } std::set ListeningReporter::getSupportedVerbosities() { @@ -12762,6 +12829,7 @@ namespace Catch { m_xml(_config.stream()) { m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; } XmlReporter::~XmlReporter() = default; @@ -12818,8 +12886,7 @@ namespace Catch { StreamingReporterBase::sectionStarting( sectionInfo ); if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) - .writeAttribute( "name", trim( sectionInfo.name ) ) - .writeAttribute( "description", sectionInfo.description ); + .writeAttribute( "name", trim( sectionInfo.name ) ); writeSourceInfo( sectionInfo.lineInfo ); m_xml.ensureTagClosed(); } @@ -13061,6 +13128,7 @@ int main (int argc, char * const argv[]) { #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) @@ -13070,11 +13138,11 @@ int main (int argc, char * const argv[]) { // "BDD-style" convenience wrappers #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc ) -#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) -#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) +#define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And when: " << desc ) +#define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else @@ -13119,6 +13187,7 @@ int main (int argc, char * const argv[]) { #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) +#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) @@ -13132,15 +13201,16 @@ int main (int argc, char * const argv[]) { #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc ) -#define WHEN( desc ) SECTION( std::string(" When: ") + desc ) -#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc ) -#define THEN( desc ) SECTION( std::string(" Then: ") + desc ) -#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc ) +#define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) +#define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) +#define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And when: " << desc ) +#define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) +#define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) using Catch::Detail::Approx; -#else +#else // CATCH_CONFIG_DISABLE + ////// // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL @@ -13185,6 +13255,7 @@ using Catch::Detail::Approx; #define CATCH_METHOD_AS_TEST_CASE( method, ... ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) #define CATCH_SECTION( ... ) +#define CATCH_DYNAMIC_SECTION( ... ) #define CATCH_FAIL( ... ) (void)(0) #define CATCH_FAIL_CHECK( ... ) (void)(0) #define CATCH_SUCCEED( ... ) (void)(0) @@ -13243,6 +13314,7 @@ using Catch::Detail::Approx; #define METHOD_AS_TEST_CASE( method, ... ) #define REGISTER_TEST_CASE( Function, ... ) (void)(0) #define SECTION( ... ) +#define DYNAMIC_SECTION( ... ) #define FAIL( ... ) (void)(0) #define FAIL_CHECK( ... ) (void)(0) #define SUCCEED( ... ) (void)(0) diff --git a/third_party/cxxopts/VERSION b/third_party/cxxopts/VERSION new file mode 100644 index 00000000..7c327287 --- /dev/null +++ b/third_party/cxxopts/VERSION @@ -0,0 +1 @@ +2.1.1 \ No newline at end of file diff --git a/third_party/cxxopts/cxxopts/include/cxxopts.hpp b/third_party/cxxopts/cxxopts/include/cxxopts.hpp index 6feee257..1ef1e157 100644 --- a/third_party/cxxopts/cxxopts/include/cxxopts.hpp +++ b/third_party/cxxopts/cxxopts/include/cxxopts.hpp @@ -1095,7 +1095,7 @@ namespace cxxopts ParseResult( const std::unordered_map>&, std::vector, - int&, const char**&); + int&, char**&); size_t count(const std::string& o) const @@ -1138,7 +1138,7 @@ namespace cxxopts get_option(std::shared_ptr); void - parse(int& argc, const char**& argv); + parse(int& argc, char**& argv); void add_to_option(const std::string& option, const std::string& arg); @@ -1161,7 +1161,7 @@ namespace cxxopts checked_parse_arg ( int argc, - const char* argv[], + char* argv[], int& current, std::shared_ptr value, const std::string& name @@ -1213,7 +1213,7 @@ namespace cxxopts } ParseResult - parse(int& argc, const char**& argv); + parse(int& argc, char**& argv); OptionAdder add_options(std::string group = ""); @@ -1431,7 +1431,7 @@ ParseResult::ParseResult ( const std::unordered_map>& options, std::vector positional, - int& argc, const char**& argv + int& argc, char**& argv ) : m_options(options) , m_positional(std::move(positional)) @@ -1532,7 +1532,7 @@ void ParseResult::checked_parse_arg ( int argc, - const char* argv[], + char* argv[], int& current, std::shared_ptr value, const std::string& name @@ -1639,7 +1639,7 @@ Options::parse_positional(std::initializer_list options) inline ParseResult -Options::parse(int& argc, const char**& argv) +Options::parse(int& argc, char**& argv) { ParseResult result(m_options, m_positional, argc, argv); return result; @@ -1647,7 +1647,7 @@ Options::parse(int& argc, const char**& argv) inline void -ParseResult::parse(int& argc, const char**& argv) +ParseResult::parse(int& argc, char**& argv) { int current = 1; diff --git a/third_party/gsl/VERSION b/third_party/gsl/VERSION new file mode 100644 index 00000000..359a5b95 --- /dev/null +++ b/third_party/gsl/VERSION @@ -0,0 +1 @@ +2.0.0 \ No newline at end of file diff --git a/third_party/gsl/gsl/include/gsl/gsl_algorithm b/third_party/gsl/gsl/include/gsl/gsl_algorithm index 710792fb..c2ba31f3 100644 --- a/third_party/gsl/gsl/include/gsl/gsl_algorithm +++ b/third_party/gsl/gsl/include/gsl/gsl_algorithm @@ -31,15 +31,12 @@ #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4996) // unsafe use of std::copy_n -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) #endif // _MSC_VER namespace gsl { - +// Note: this will generate faster code than std::copy using span iterator in older msvc+stl +// not necessary for msvc since VS2017 15.8 (_MSC_VER >= 1915) template void copy(span src, span dest) @@ -51,6 +48,7 @@ void copy(span src, span "Source range is longer than target range"); Expects(dest.size() >= src.size()); + GSL_SUPPRESS(stl.1) // NO-FORMAT: attribute std::copy_n(src.data(), src.size(), dest.data()); } diff --git a/third_party/gsl/gsl/include/gsl/gsl_assert b/third_party/gsl/gsl/include/gsl/gsl_assert index 2bda1493..3c952e6e 100644 --- a/third_party/gsl/gsl/include/gsl/gsl_assert +++ b/third_party/gsl/gsl/include/gsl/gsl_assert @@ -20,12 +20,28 @@ #include #include // for logic_error +// +// make suppress attributes parse for some compilers +// Hopefully temporary until suppresion standardization occurs +// +#if defined (_MSC_VER) +#define GSL_SUPPRESS(x) [[gsl::suppress(x)]] +#else +#if defined(__clang__) +#define GSL_SUPPRESS(x) [[gsl::suppress("x")]] +#else +#define GSL_SUPPRESS(x) +#endif // __clang__ +#endif // _MSC_VER + // // Temporary until MSVC STL supports no-exceptions mode. // Currently terminate is a no-op in this mode, so we add termination behavior back // #if defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS #define GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND +#include +#define RANGE_CHECKS_FAILURE 0 #endif // @@ -81,11 +97,17 @@ namespace details { #if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) - typedef void (*terminate_handler)(); + typedef void (__cdecl *terminate_handler)(); + + GSL_SUPPRESS(f.6) // NO-FORMAT: attribute + [[noreturn]] inline void __cdecl default_terminate_handler() + { + __fastfail(RANGE_CHECKS_FAILURE); + } inline gsl::details::terminate_handler& get_terminate_handler() noexcept { - static terminate_handler handler = &abort; + static terminate_handler handler = &default_terminate_handler; return handler; } @@ -103,7 +125,7 @@ namespace details #if defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) template - [[noreturn]] void throw_exception(Exception&&) + [[noreturn]] void throw_exception(Exception&&) noexcept { gsl::details::terminate(); } @@ -113,7 +135,7 @@ namespace details template [[noreturn]] void throw_exception(Exception&& exception) { - throw exception; + throw std::forward(exception); } #endif diff --git a/third_party/gsl/gsl/include/gsl/gsl_byte b/third_party/gsl/gsl/include/gsl/gsl_byte index 5da9a865..bc188860 100644 --- a/third_party/gsl/gsl/include/gsl/gsl_byte +++ b/third_party/gsl/gsl/include/gsl/gsl_byte @@ -17,14 +17,28 @@ #ifndef GSL_BYTE_H #define GSL_BYTE_H +// +// make suppress attributes work for some compilers +// Hopefully temporary until suppresion standardization occurs +// +#if defined(_MSC_VER) +#define GSL_SUPPRESS(x) [[gsl::suppress(x)]] +#else +#if defined(__clang__) +#define GSL_SUPPRESS(x) [[gsl::suppress("x")]] +#else +#define GSL_SUPPRESS(x) +#endif // __clang__ +#endif // _MSC_VER + #include #ifdef _MSC_VER #pragma warning(push) -// don't warn about function style casts in byte related operators -#pragma warning(disable : 26493) +// Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool. +#pragma warning(disable : 26493) // don't use c-style casts // TODO: MSVC suppression in templates does not always work #ifndef GSL_USE_STD_BYTE // this tests if we are under MSVC and the standard lib has std::byte and it is enabled @@ -43,25 +57,38 @@ #ifndef GSL_USE_STD_BYTE // this tests if we are under GCC or Clang with enough -std:c++1z power to get us std::byte -#if defined(__cplusplus) && (__cplusplus >= 201703L) +// also check if libc++ version is sufficient (> 5.0) or libstc++ actually contains std::byte +#if defined(__cplusplus) && (__cplusplus >= 201703L) && \ + (defined(__cpp_lib_byte) && (__cpp_lib_byte >= 201603) || \ + defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 5000)) #define GSL_USE_STD_BYTE 1 #include -#else // defined(__cplusplus) && (__cplusplus >= 201703L) +#else // defined(__cplusplus) && (__cplusplus >= 201703L) && + // (defined(__cpp_lib_byte) && (__cpp_lib_byte >= 201603) || + // defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 5000)) #define GSL_USE_STD_BYTE 0 -#endif //defined(__cplusplus) && (__cplusplus >= 201703L) +#endif //defined(__cplusplus) && (__cplusplus >= 201703L) && + // (defined(__cpp_lib_byte) && (__cpp_lib_byte >= 201603) || + // defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 5000)) #endif // GSL_USE_STD_BYTE -#endif // _MSC_VER +#endif // _MSC_VER + +// Use __may_alias__ attribute on gcc and clang +#if defined __clang__ || (__GNUC__ > 5) +#define byte_may_alias __attribute__((__may_alias__)) +#else // defined __clang__ || defined __GNUC__ +#define byte_may_alias +#endif // defined __clang__ || defined __GNUC__ namespace gsl { #if GSL_USE_STD_BYTE - using std::byte; using std::to_integer; @@ -69,7 +96,7 @@ using std::to_integer; // This is a simple definition for now that allows // use of byte within span<> to be standards-compliant -enum class byte : unsigned char +enum class byte_may_alias byte : unsigned char { }; @@ -146,6 +173,8 @@ constexpr byte to_byte_impl(T t) noexcept return static_cast(t); } template <> +// NOTE: need suppression since c++14 does not allow "return {t}" +// GSL_SUPPRESS(type.4) // NO-FORMAT: attribute // TODO: suppression does not work constexpr byte to_byte_impl(unsigned char t) noexcept { return byte(t); diff --git a/third_party/gsl/gsl/include/gsl/gsl_util b/third_party/gsl/gsl/include/gsl/gsl_util index 2ca171b6..542bbaa2 100644 --- a/third_party/gsl/gsl/include/gsl/gsl_util +++ b/third_party/gsl/gsl/include/gsl/gsl_util @@ -34,8 +34,8 @@ #if _MSC_VER < 1910 #pragma push_macro("constexpr") #define constexpr /*constexpr*/ -#endif // _MSC_VER < 1910 -#endif // _MSC_VER +#endif // _MSC_VER < 1910 +#endif // _MSC_VER namespace gsl { @@ -60,7 +60,9 @@ public: final_action(const final_action&) = delete; final_action& operator=(const final_action&) = delete; + final_action& operator=(final_action&&) = delete; + GSL_SUPPRESS(f.6) // NO-FORMAT: attribute // terminate if throws ~final_action() noexcept { if (invoke_) f_(); @@ -68,12 +70,11 @@ public: private: F f_; - bool invoke_ {true}; + bool invoke_{true}; }; // finally() - convenience function to generate a final_action template - final_action finally(const F& f) noexcept { return final_action(f); @@ -87,6 +88,7 @@ final_action finally(F&& f) noexcept // narrow_cast(): a searchable way to do narrowing casts of values template +GSL_SUPPRESS(type.1) // NO-FORMAT: attribute constexpr T narrow_cast(U&& u) noexcept { return static_cast(std::forward(u)); @@ -103,11 +105,13 @@ namespace details : public std::integral_constant::value == std::is_signed::value> { }; -} +} // namespace details // narrow() : a checked version of narrow_cast() that throws if the cast changed the value template -T narrow(U u) +GSL_SUPPRESS(type.1) // NO-FORMAT: attribute +GSL_SUPPRESS(f.6) // NO-FORMAT: attribute // TODO: MSVC /analyze does not recognise noexcept(false) +T narrow(U u) noexcept(false) { T t = narrow_cast(u); if (static_cast(t) != u) gsl::details::throw_exception(narrowing_error()); @@ -120,21 +124,26 @@ T narrow(U u) // at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector // template +GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute +GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute constexpr T& at(T (&arr)[N], const index i) { Expects(i >= 0 && i < narrow_cast(N)); - return arr[static_cast(i)]; + return arr[narrow_cast(i)]; } template +GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute +GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]) { Expects(i >= 0 && i < narrow_cast(cont.size())); using size_type = decltype(cont.size()); - return cont[static_cast(i)]; + return cont[narrow_cast(i)]; } template +GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute constexpr T at(const std::initializer_list cont, const index i) { Expects(i >= 0 && i < narrow_cast(cont.size())); diff --git a/third_party/gsl/gsl/include/gsl/multi_span b/third_party/gsl/gsl/include/gsl/multi_span index 65e65a11..c8f4aae0 100644 --- a/third_party/gsl/gsl/include/gsl/multi_span +++ b/third_party/gsl/gsl/include/gsl/multi_span @@ -44,6 +44,12 @@ #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4702) // unreachable code +// Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool. +#pragma warning(disable : 26495) // uninitalized member when constructor calls constructor +#pragma warning(disable : 26473) // in some instantiations we cast to the same type +#pragma warning(disable : 26490) // TODO: bug in parser - attributes and templates +#pragma warning(disable : 26465) // TODO: bug - suppression does not work on template functions + #if _MSC_VER < 1910 #pragma push_macro("constexpr") #define constexpr /*constexpr*/ @@ -51,11 +57,15 @@ #endif // _MSC_VER < 1910 #endif // _MSC_VER -#ifdef GSL_THROW_ON_CONTRACT_VIOLATION -#define GSL_NOEXCEPT /*noexcept*/ -#else -#define GSL_NOEXCEPT noexcept -#endif // GSL_THROW_ON_CONTRACT_VIOLATION +// GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t) +// While there is a conversion from signed to unsigned, it happens at +// compiletime, so the compiler wouldn't have to warn indiscriminently, but +// could check if the source value actually doesn't fit into the target type +// and only warn in those cases. +#if __GNUC__ > 6 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif namespace gsl { @@ -82,7 +92,7 @@ namespace details std::is_integral::value && are_integral::value> { }; -} +} // namespace details template class multi_span_index final @@ -99,24 +109,27 @@ public: using reference = std::add_lvalue_reference_t; using const_reference = std::add_lvalue_reference_t>; - constexpr multi_span_index() GSL_NOEXCEPT {} + constexpr multi_span_index() noexcept {} - constexpr multi_span_index(const value_type (&values)[Rank]) GSL_NOEXCEPT + constexpr multi_span_index(const value_type (&values)[Rank]) noexcept { + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute std::copy(values, values + Rank, elems); } template ::value>> - constexpr multi_span_index(Ts... ds) GSL_NOEXCEPT : elems{narrow_cast(ds)...} - { - } + constexpr multi_span_index(Ts... ds) noexcept : elems{narrow_cast(ds)...} + {} - constexpr multi_span_index(const multi_span_index& other) GSL_NOEXCEPT = default; + constexpr multi_span_index(const multi_span_index& other) noexcept = default; - constexpr multi_span_index& operator=(const multi_span_index& rhs) GSL_NOEXCEPT = default; + constexpr multi_span_index& operator=(const multi_span_index& rhs) noexcept = default; // Preconditions: component_idx < rank + GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute constexpr reference operator[](std::size_t component_idx) { Expects(component_idx < Rank); // Component index must be less than rank @@ -124,82 +137,98 @@ public: } // Preconditions: component_idx < rank - constexpr const_reference operator[](std::size_t component_idx) const GSL_NOEXCEPT + GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr const_reference operator[](std::size_t component_idx) const { Expects(component_idx < Rank); // Component index must be less than rank return elems[component_idx]; } - constexpr bool operator==(const multi_span_index& rhs) const GSL_NOEXCEPT + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute + constexpr bool operator==(const multi_span_index& rhs) const { return std::equal(elems, elems + rank, rhs.elems); } - constexpr bool operator!=(const multi_span_index& rhs) const GSL_NOEXCEPT { return !(*this == rhs); } + constexpr bool operator!=(const multi_span_index& rhs) const + { + return !(*this == rhs); + } - constexpr multi_span_index operator+() const GSL_NOEXCEPT { return *this; } + constexpr multi_span_index operator+() const noexcept { return *this; } - constexpr multi_span_index operator-() const GSL_NOEXCEPT + constexpr multi_span_index operator-() const { multi_span_index ret = *this; std::transform(ret, ret + rank, ret, std::negate{}); return ret; } - constexpr multi_span_index operator+(const multi_span_index& rhs) const GSL_NOEXCEPT + constexpr multi_span_index operator+(const multi_span_index& rhs) const { multi_span_index ret = *this; ret += rhs; return ret; } - constexpr multi_span_index operator-(const multi_span_index& rhs) const GSL_NOEXCEPT + constexpr multi_span_index operator-(const multi_span_index& rhs) const { multi_span_index ret = *this; ret -= rhs; return ret; } - constexpr multi_span_index& operator+=(const multi_span_index& rhs) GSL_NOEXCEPT + constexpr multi_span_index& operator+=(const multi_span_index& rhs) { - std::transform(elems, elems + rank, rhs.elems, elems, std::plus{}); + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute + std::transform(elems, elems + rank, rhs.elems, elems, + std::plus{}); return *this; } - constexpr multi_span_index& operator-=(const multi_span_index& rhs) GSL_NOEXCEPT + constexpr multi_span_index& operator-=(const multi_span_index& rhs) { + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute std::transform(elems, elems + rank, rhs.elems, elems, std::minus{}); return *this; } - constexpr multi_span_index operator*(value_type v) const GSL_NOEXCEPT + constexpr multi_span_index operator*(value_type v) const { multi_span_index ret = *this; ret *= v; return ret; } - constexpr multi_span_index operator/(value_type v) const GSL_NOEXCEPT + constexpr multi_span_index operator/(value_type v) const { multi_span_index ret = *this; ret /= v; return ret; } - friend constexpr multi_span_index operator*(value_type v, const multi_span_index& rhs) GSL_NOEXCEPT + friend constexpr multi_span_index operator*(value_type v, const multi_span_index& rhs) { return rhs * v; } - constexpr multi_span_index& operator*=(value_type v) GSL_NOEXCEPT + constexpr multi_span_index& operator*=(value_type v) { + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute std::transform(elems, elems + rank, elems, [v](value_type x) { return std::multiplies{}(x, v); }); return *this; } - constexpr multi_span_index& operator/=(value_type v) GSL_NOEXCEPT + constexpr multi_span_index& operator/=(value_type v) { + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.3) // NO-FORMAT: attribute std::transform(elems, elems + rank, elems, [v](value_type x) { return std::divides{}(x, v); }); return *this; @@ -214,42 +243,42 @@ private: struct static_bounds_dynamic_range_t { template ::value>> - constexpr operator T() const GSL_NOEXCEPT + constexpr operator T() const noexcept { return narrow_cast(-1); } }; -constexpr bool operator==(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT +constexpr bool operator==(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) noexcept { return true; } -constexpr bool operator!=(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT +constexpr bool operator!=(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) noexcept { return false; } template ::value>> -constexpr bool operator==(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT +constexpr bool operator==(static_bounds_dynamic_range_t, T other) noexcept { return narrow_cast(-1) == other; } template ::value>> -constexpr bool operator==(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT +constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept { return right == left; } template ::value>> -constexpr bool operator!=(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT +constexpr bool operator!=(static_bounds_dynamic_range_t, T other) noexcept { return narrow_cast(-1) != other; } template ::value>> -constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT +constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept { return right != left; } @@ -287,16 +316,14 @@ namespace details // TODO : following signature is for work around VS bug template BoundsRanges(const OtherRange&, bool /* firstLevel */) - { - } + {} BoundsRanges(const std::ptrdiff_t* const) {} - BoundsRanges() = default; + BoundsRanges() noexcept = default; template void serialize(T&) const - { - } + {} template size_type linearize(const T&) const @@ -310,11 +337,11 @@ namespace details return -1; } - size_type elementNum(std::size_t) const GSL_NOEXCEPT { return 0; } + size_type elementNum(std::size_t) const noexcept { return 0; } - size_type totalSize() const GSL_NOEXCEPT { return TotalSize; } + size_type totalSize() const noexcept { return TotalSize; } - bool operator==(const BoundsRanges&) const GSL_NOEXCEPT { return true; } + bool operator==(const BoundsRanges&) const noexcept { return true; } }; template @@ -331,21 +358,22 @@ namespace details size_type m_bound; public: + GSL_SUPPRESS(f.23) // NO-FORMAT: attribute // this pointer type is cannot be assigned nullptr - issue in not_null + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr + 1), m_bound(*arr * this->Base::totalSize()) { Expects(0 <= *arr); } - BoundsRanges() : m_bound(0) {} + BoundsRanges() noexcept : m_bound(0) {} template BoundsRanges(const BoundsRanges& other, bool /* firstLevel */ = true) : Base(static_cast&>(other), false) , m_bound(other.totalSize()) - { - } + {} template void serialize(T& arr) const @@ -355,6 +383,7 @@ namespace details } template + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute size_type linearize(const T& arr) const { const size_type index = this->Base::totalSize() * arr[Dim]; @@ -371,11 +400,19 @@ namespace details return cur < m_bound ? cur + last : -1; } - size_type totalSize() const GSL_NOEXCEPT { return m_bound; } + GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used + size_type totalSize() const noexcept + { + return m_bound; + } - size_type elementNum() const GSL_NOEXCEPT { return totalSize() / this->Base::totalSize(); } + GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used + size_type elementNum() const noexcept + { + return totalSize() / this->Base::totalSize(); + } - size_type elementNum(std::size_t dim) const GSL_NOEXCEPT + size_type elementNum(std::size_t dim) const noexcept { if (dim > 0) return this->Base::elementNum(dim - 1); @@ -383,7 +420,7 @@ namespace details return elementNum(); } - bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT + bool operator==(const BoundsRanges& rhs) const noexcept { return m_bound == rhs.m_bound && static_cast(*this) == static_cast(rhs); @@ -409,6 +446,7 @@ namespace details bool firstLevel = true) : Base(static_cast&>(other), false) { + GSL_SUPPRESS(type.4) // NO-FORMAT: attribute // TODO: false positive (void) firstLevel; } @@ -422,7 +460,10 @@ namespace details template size_type linearize(const T& arr) const { + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range + + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute return this->Base::totalSize() * arr[Dim] + this->Base::template linearize(arr); } @@ -436,11 +477,20 @@ namespace details return this->Base::totalSize() * arr[Dim] + last; } - size_type totalSize() const GSL_NOEXCEPT { return CurrentRange * this->Base::totalSize(); } + GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used + size_type totalSize() const noexcept + { + return CurrentRange * this->Base::totalSize(); + } - size_type elementNum() const GSL_NOEXCEPT { return CurrentRange; } + GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used + size_type elementNum() const noexcept + { + return CurrentRange; + } - size_type elementNum(std::size_t dim) const GSL_NOEXCEPT + GSL_SUPPRESS(c.128) // NO-FORMAT: attribute // no pointers to BoundsRanges should be ever used + size_type elementNum(std::size_t dim) const noexcept { if (dim > 0) return this->Base::elementNum(dim - 1); @@ -448,7 +498,7 @@ namespace details return elementNum(); } - bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT + bool operator==(const BoundsRanges& rhs) const noexcept { return static_cast(*this) == static_cast(rhs); } @@ -498,15 +548,17 @@ namespace details template 1), typename Ret = std::enable_if_t>> - constexpr Ret shift_left(const multi_span_index& other) GSL_NOEXCEPT + constexpr Ret shift_left(const multi_span_index& other) noexcept { Ret ret{}; - for (std::size_t i = 0; i < Rank - 1; ++i) { - ret[i] = other[i + 1]; + for (std::size_t i = 0; i < Rank - 1; ++i) + { + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + ret[i] = other[i + 1]; } return ret; } -} +} // namespace details template class bounds_iterator; @@ -524,7 +576,7 @@ class static_bounds using MyRanges = details::BoundsRanges; MyRanges m_ranges; - constexpr static_bounds(const MyRanges& range) : m_ranges(range) {} + constexpr static_bounds(const MyRanges& range) noexcept : m_ranges(range) {} template friend class static_bounds; @@ -543,7 +595,8 @@ public: using sliced_type = static_bounds; using mapping_type = contiguous_mapping_tag; - constexpr static_bounds(const static_bounds&) = default; + constexpr static_bounds(const static_bounds&) noexcept = default; + constexpr static_bounds() /*noexcept*/ = default; template struct BoundsRangeConvertible2; @@ -601,8 +654,7 @@ public: MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize()); } - constexpr static_bounds(std::initializer_list il) - : m_ranges(static_cast(il.begin())) + constexpr static_bounds(std::initializer_list il) : m_ranges(il.begin()) { // Size of the initializer list must match the rank of the array Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) || @@ -611,33 +663,31 @@ public: Expects(m_ranges.totalSize() <= PTRDIFF_MAX); } - constexpr static_bounds() = default; - - constexpr sliced_type slice() const GSL_NOEXCEPT + constexpr sliced_type slice() const noexcept { return sliced_type{static_cast&>(m_ranges)}; } - constexpr size_type stride() const GSL_NOEXCEPT { return rank > 1 ? slice().size() : 1; } + constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; } - constexpr size_type size() const GSL_NOEXCEPT { return m_ranges.totalSize(); } + constexpr size_type size() const noexcept { return m_ranges.totalSize(); } - constexpr size_type total_size() const GSL_NOEXCEPT { return m_ranges.totalSize(); } + constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); } constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); } - constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT + constexpr bool contains(const index_type& idx) const noexcept { return m_ranges.contains(idx) != -1; } - constexpr size_type operator[](std::size_t idx) const GSL_NOEXCEPT + constexpr size_type operator[](std::size_t idx) const noexcept { return m_ranges.elementNum(idx); } template - constexpr size_type extent() const GSL_NOEXCEPT + constexpr size_type extent() const noexcept { static_assert(Dim < rank, "dimension should be less than rank (dimension count starts from 0)"); @@ -645,7 +695,7 @@ public: } template - constexpr size_type extent(IntType dim) const GSL_NOEXCEPT + constexpr size_type extent(IntType dim) const { static_assert(std::is_integral::value, "Dimension parameter must be supplied as an integral type."); @@ -655,7 +705,7 @@ public: return m_ranges.elementNum(real_dim); } - constexpr index_type index_bounds() const GSL_NOEXCEPT + constexpr index_type index_bounds() const noexcept { size_type extents[rank] = {}; m_ranges.serialize(extents); @@ -663,31 +713,30 @@ public: } template - constexpr bool operator==(const static_bounds& rhs) const GSL_NOEXCEPT + constexpr bool operator==(const static_bounds& rhs) const noexcept { return this->size() == rhs.size(); } template - constexpr bool operator!=(const static_bounds& rhs) const GSL_NOEXCEPT + constexpr bool operator!=(const static_bounds& rhs) const noexcept { return !(*this == rhs); } - constexpr const_iterator begin() const GSL_NOEXCEPT + constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); } - constexpr const_iterator end() const GSL_NOEXCEPT + constexpr const_iterator end() const noexcept { return const_iterator(*this, this->index_bounds()); } }; template -class strided_bounds -{ +class strided_bounds { template friend class strided_bounds; @@ -707,60 +756,60 @@ public: using sliced_type = std::conditional_t, void>; using mapping_type = generalized_mapping_tag; - constexpr strided_bounds(const strided_bounds&) GSL_NOEXCEPT = default; + constexpr strided_bounds(const strided_bounds&) noexcept = default; - constexpr strided_bounds& operator=(const strided_bounds&) GSL_NOEXCEPT = default; + constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default; constexpr strided_bounds(const value_type (&values)[rank], index_type strides) : m_extents(values), m_strides(std::move(strides)) - { - } + {} - constexpr strided_bounds(const index_type& extents, const index_type& strides) GSL_NOEXCEPT - : m_extents(extents), - m_strides(strides) + constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept + : m_extents(extents), m_strides(strides) { } - constexpr index_type strides() const GSL_NOEXCEPT { return m_strides; } + constexpr index_type strides() const noexcept { return m_strides; } - constexpr size_type total_size() const GSL_NOEXCEPT + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr size_type total_size() const noexcept { size_type ret = 0; - for (std::size_t i = 0; i < rank; ++i) { - ret += (m_extents[i] - 1) * m_strides[i]; - } + for (std::size_t i = 0; i < rank; ++i) { ret += (m_extents[i] - 1) * m_strides[i]; } return ret + 1; } - constexpr size_type size() const GSL_NOEXCEPT + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr size_type size() const noexcept { size_type ret = 1; - for (std::size_t i = 0; i < rank; ++i) { - ret *= m_extents[i]; - } + for (std::size_t i = 0; i < rank; ++i) { ret *= m_extents[i]; } return ret; } - constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT + constexpr bool contains(const index_type& idx) const noexcept { - for (std::size_t i = 0; i < rank; ++i) { + for (std::size_t i = 0; i < rank; ++i) + { if (idx[i] < 0 || idx[i] >= m_extents[i]) return false; } return true; } - constexpr size_type linearize(const index_type& idx) const GSL_NOEXCEPT + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr size_type linearize(const index_type& idx) const { size_type ret = 0; - for (std::size_t i = 0; i < rank; i++) { + for (std::size_t i = 0; i < rank; i++) + { Expects(idx[i] < m_extents[i]); // index is out of bounds of the array ret += idx[i] * m_strides[i]; } return ret; } - constexpr size_type stride() const GSL_NOEXCEPT { return m_strides[0]; } + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr size_type stride() const noexcept { return m_strides[0]; } template 1), typename Ret = std::enable_if_t> constexpr sliced_type slice() const @@ -769,23 +818,20 @@ public: } template - constexpr size_type extent() const GSL_NOEXCEPT + + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr size_type extent() const noexcept { static_assert(Dim < Rank, "dimension should be less than rank (dimension count starts from 0)"); return m_extents[Dim]; } - constexpr index_type index_bounds() const GSL_NOEXCEPT { return m_extents; } - constexpr const_iterator begin() const GSL_NOEXCEPT - { - return const_iterator{*this, index_type{}}; - } + constexpr index_type index_bounds() const noexcept { return m_extents; } - constexpr const_iterator end() const GSL_NOEXCEPT - { - return const_iterator{*this, index_bounds()}; - } + constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; } + + constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; } private: index_type m_extents; @@ -818,21 +864,25 @@ public: using index_type = value_type; using index_size_type = typename IndexType::value_type; template - explicit bounds_iterator(const Bounds& bnd, value_type curr) GSL_NOEXCEPT - : boundary_(bnd.index_bounds()), - curr_(std::move(curr)) + explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept + : boundary_(bnd.index_bounds()), curr_(std::move(curr)) { static_assert(is_bounds::value, "Bounds type must be provided"); } - constexpr reference operator*() const GSL_NOEXCEPT { return curr_; } + constexpr reference operator*() const noexcept { return curr_; } + + constexpr pointer operator->() const noexcept { return &curr_; } - constexpr pointer operator->() const GSL_NOEXCEPT { return &curr_; } + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute + constexpr bounds_iterator& operator++() noexcept - constexpr bounds_iterator& operator++() GSL_NOEXCEPT { - for (std::size_t i = rank; i-- > 0;) { - if (curr_[i] < boundary_[i] - 1) { + for (std::size_t i = rank; i-- > 0;) + { + if (curr_[i] < boundary_[i] - 1) + { curr_[i]++; return *this; } @@ -843,24 +893,26 @@ public: return *this; } - constexpr bounds_iterator operator++(int) GSL_NOEXCEPT + constexpr bounds_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - constexpr bounds_iterator& operator--() GSL_NOEXCEPT + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr bounds_iterator& operator--() { - if (!less(curr_, boundary_)) { + if (!less(curr_, boundary_)) + { // if at the past-the-end, set to last element - for (std::size_t i = 0; i < rank; ++i) { - curr_[i] = boundary_[i] - 1; - } + for (std::size_t i = 0; i < rank; ++i) { curr_[i] = boundary_[i] - 1; } return *this; } - for (std::size_t i = rank; i-- > 0;) { - if (curr_[i] >= 1) { + for (std::size_t i = rank; i-- > 0;) + { + if (curr_[i] >= 1) + { curr_[i]--; return *this; } @@ -872,28 +924,28 @@ public: return *this; } - constexpr bounds_iterator operator--(int) GSL_NOEXCEPT + constexpr bounds_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - constexpr bounds_iterator operator+(difference_type n) const GSL_NOEXCEPT + constexpr bounds_iterator operator+(difference_type n) const noexcept { bounds_iterator ret{*this}; return ret += n; } - constexpr bounds_iterator& operator+=(difference_type n) GSL_NOEXCEPT + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr bounds_iterator& operator+=(difference_type n) { auto linear_idx = linearize(curr_) + n; std::remove_const_t stride = 0; stride[rank - 1] = 1; - for (std::size_t i = rank - 1; i-- > 0;) { - stride[i] = stride[i + 1] * boundary_[i + 1]; - } - for (std::size_t i = 0; i < rank; ++i) { + for (std::size_t i = rank - 1; i-- > 0;) { stride[i] = stride[i + 1] * boundary_[i + 1]; } + for (std::size_t i = 0; i < rank; ++i) + { curr_[i] = linear_idx / stride[i]; linear_idx = linear_idx % stride[i]; } @@ -902,79 +954,78 @@ public: return *this; } - constexpr bounds_iterator operator-(difference_type n) const GSL_NOEXCEPT + constexpr bounds_iterator operator-(difference_type n) const noexcept { bounds_iterator ret{*this}; return ret -= n; } - constexpr bounds_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } + constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; } - constexpr difference_type operator-(const bounds_iterator& rhs) const GSL_NOEXCEPT + constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept { return linearize(curr_) - linearize(rhs.curr_); } - constexpr value_type operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); } + constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); } - constexpr bool operator==(const bounds_iterator& rhs) const GSL_NOEXCEPT + constexpr bool operator==(const bounds_iterator& rhs) const noexcept { return curr_ == rhs.curr_; } - constexpr bool operator!=(const bounds_iterator& rhs) const GSL_NOEXCEPT - { - return !(*this == rhs); - } - constexpr bool operator<(const bounds_iterator& rhs) const GSL_NOEXCEPT + constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); } + + constexpr bool operator<(const bounds_iterator& rhs) const noexcept { return less(curr_, rhs.curr_); } - constexpr bool operator<=(const bounds_iterator& rhs) const GSL_NOEXCEPT - { - return !(rhs < *this); - } + constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); } - constexpr bool operator>(const bounds_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } + constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; } - constexpr bool operator>=(const bounds_iterator& rhs) const GSL_NOEXCEPT - { - return !(rhs > *this); - } + constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); } - void swap(bounds_iterator& rhs) GSL_NOEXCEPT + void swap(bounds_iterator& rhs) noexcept { std::swap(boundary_, rhs.boundary_); std::swap(curr_, rhs.curr_); } private: - constexpr bool less(index_type& one, index_type& other) const GSL_NOEXCEPT + + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr bool less(index_type& one, index_type& other) const noexcept { - for (std::size_t i = 0; i < rank; ++i) { + for (std::size_t i = 0; i < rank; ++i) + { if (one[i] < other[i]) return true; } return false; } - constexpr index_size_type linearize(const value_type& idx) const GSL_NOEXCEPT + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + constexpr index_size_type linearize(const value_type& idx) const noexcept { // TODO: Smarter impl. // Check if past-the-end index_size_type multiplier = 1; index_size_type res = 0; - if (!less(idx, boundary_)) { + if (!less(idx, boundary_)) + { res = 1; - for (std::size_t i = rank; i-- > 0;) { + for (std::size_t i = rank; i-- > 0;) + { res += (idx[i] - 1) * multiplier; multiplier *= boundary_[i]; } } else { - for (std::size_t i = rank; i-- > 0;) { + for (std::size_t i = rank; i-- > 0;) + { res += idx[i] * multiplier; multiplier *= boundary_[i]; } @@ -988,7 +1039,7 @@ private: template bounds_iterator operator+(typename bounds_iterator::difference_type n, - const bounds_iterator& rhs) GSL_NOEXCEPT + const bounds_iterator& rhs) noexcept { return rhs + n; } @@ -999,7 +1050,7 @@ namespace details constexpr std::enable_if_t< std::is_same::value, typename Bounds::index_type> - make_stride(const Bounds& bnd) GSL_NOEXCEPT + make_stride(const Bounds& bnd) noexcept { return bnd.strides(); } @@ -1009,13 +1060,16 @@ namespace details constexpr std::enable_if_t< std::is_same::value, typename Bounds::index_type> - make_stride(const Bounds& bnd) GSL_NOEXCEPT + make_stride(const Bounds& bnd) noexcept { auto extents = bnd.index_bounds(); typename Bounds::size_type stride[Bounds::rank] = {}; stride[Bounds::rank - 1] = 1; - for (std::size_t i = 1; i < Bounds::rank; ++i) { + for (std::size_t i = 1; i < Bounds::rank; ++i) + { + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i]; } return {stride}; @@ -1052,17 +1106,17 @@ struct dim_t { static const std::ptrdiff_t value = dynamic_range; const std::ptrdiff_t dvalue; - constexpr dim_t(std::ptrdiff_t size) GSL_NOEXCEPT : dvalue(size) {} + constexpr dim_t(std::ptrdiff_t size) noexcept : dvalue(size) {} }; template = 0)>> -constexpr dim_t dim() GSL_NOEXCEPT +constexpr dim_t dim() noexcept { return dim_t(); } template > -constexpr dim_t dim(std::ptrdiff_t n) GSL_NOEXCEPT +constexpr dim_t dim(std::ptrdiff_t n) noexcept { return dim_t<>(n); } @@ -1171,7 +1225,7 @@ namespace details struct is_multi_span : is_multi_span_oracle> { }; -} +} // namespace details template class multi_span @@ -1207,7 +1261,9 @@ private: public: // default constructor - same as constructing from nullptr_t - constexpr multi_span() GSL_NOEXCEPT : multi_span(nullptr, bounds_type{}) + GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive + constexpr multi_span() noexcept + : multi_span(nullptr, bounds_type{}) { static_assert(bounds_type::dynamic_rank != 0 || (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), @@ -1216,7 +1272,9 @@ public: } // construct from nullptr - get an empty multi_span - constexpr multi_span(std::nullptr_t) GSL_NOEXCEPT : multi_span(nullptr, bounds_type{}) + GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive + constexpr multi_span(std::nullptr_t) noexcept + : multi_span(nullptr, bounds_type{}) { static_assert(bounds_type::dynamic_rank != 0 || (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), @@ -1226,8 +1284,9 @@ public: // construct from nullptr with size of 0 (helps with template function calls) template ::value>> - constexpr multi_span(std::nullptr_t, IntType size) GSL_NOEXCEPT - : multi_span(nullptr, bounds_type{}) + + // GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive // TODO: parser bug + constexpr multi_span(std::nullptr_t, IntType size) : multi_span(nullptr, bounds_type{}) { static_assert(bounds_type::dynamic_rank != 0 || (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0), @@ -1237,7 +1296,10 @@ public: } // construct from a single element - constexpr multi_span(reference data) GSL_NOEXCEPT : multi_span(&data, bounds_type{1}) + + GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive + constexpr multi_span(reference data) noexcept + : multi_span(&data, bounds_type{1}) { static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 || bounds_type::static_size == 1, @@ -1249,14 +1311,14 @@ public: constexpr multi_span(value_type&&) = delete; // construct from pointer + length - constexpr multi_span(pointer ptr, size_type size) GSL_NOEXCEPT + GSL_SUPPRESS(type.6) // NO-FORMAT: attribute // TODO: false positive + constexpr multi_span(pointer ptr, size_type size) : multi_span(ptr, bounds_type{size}) - { - } + {} // construct from pointer + length - multidimensional - constexpr multi_span(pointer data, bounds_type bounds) GSL_NOEXCEPT : data_(data), - bounds_(std::move(bounds)) + constexpr multi_span(pointer data, bounds_type bounds) + : data_(data), bounds_(std::move(bounds)) { Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0); } @@ -1310,8 +1372,9 @@ public: constexpr multi_span(const std::array& arr) : multi_span(arr.data(), bounds_type{static_bounds{}}) { - static_assert(std::is_convertible(*)[]>::value, - "Cannot convert from source type to target multi_span type."); + static_assert( + std::is_convertible(*)[]>::value, + "Cannot convert from source type to target multi_span type."); static_assert(std::is_convertible, bounds_type>::value, "You cannot construct a multi_span from a std::array of smaller size."); } @@ -1333,8 +1396,7 @@ public: constexpr multi_span(Cont& cont) : multi_span(static_cast(cont.data()), details::newBoundsHelper(narrow_cast(cont.size()))) - { - } + {} // prevent constructing from temporary containers template , typename = std::enable_if_t::value && std::is_convertible::value>> - constexpr multi_span(multi_span other) GSL_NOEXCEPT - : data_(other.data_), - bounds_(other.bounds_) - { - } + constexpr multi_span(multi_span other) + : data_(other.data_), bounds_(other.bounds_) + {} // trivial copy and move constexpr multi_span(const multi_span&) = default; @@ -1367,7 +1427,8 @@ public: // first() - extract the first Count elements into a new multi_span template - constexpr multi_span first() const GSL_NOEXCEPT + + constexpr multi_span first() const { static_assert(Count >= 0, "Count must be >= 0."); static_assert(bounds_type::static_size == dynamic_range || @@ -1379,7 +1440,7 @@ public: } // first() - extract the first count elements into a new multi_span - constexpr multi_span first(size_type count) const GSL_NOEXCEPT + constexpr multi_span first(size_type count) const { Expects(count >= 0 && count <= this->size()); return {this->data(), count}; @@ -1387,7 +1448,7 @@ public: // last() - extract the last Count elements into a new multi_span template - constexpr multi_span last() const GSL_NOEXCEPT + constexpr multi_span last() const { static_assert(Count >= 0, "Count must be >= 0."); static_assert(bounds_type::static_size == dynamic_range || @@ -1399,7 +1460,7 @@ public: } // last() - extract the last count elements into a new multi_span - constexpr multi_span last(size_type count) const GSL_NOEXCEPT + constexpr multi_span last(size_type count) const { Expects(count >= 0 && count <= this->size()); return {this->data() + this->size() - count, count}; @@ -1407,7 +1468,7 @@ public: // subspan() - create a subview of Count elements starting at Offset template - constexpr multi_span subspan() const GSL_NOEXCEPT + constexpr multi_span subspan() const { static_assert(Count >= 0, "Count must be >= 0."); static_assert(Offset >= 0, "Offset must be >= 0."); @@ -1423,8 +1484,8 @@ public: // subspan() - create a subview of count elements starting at offset // supplying dynamic_range for count will consume all available elements from offset - constexpr multi_span - subspan(size_type offset, size_type count = dynamic_range) const GSL_NOEXCEPT + constexpr multi_span subspan(size_type offset, + size_type count = dynamic_range) const { Expects((offset >= 0 && offset <= this->size()) && (count == dynamic_range || (count <= this->size() - offset))); @@ -1433,34 +1494,34 @@ public: // section - creates a non-contiguous, strided multi_span from a contiguous one constexpr strided_span section(index_type origin, - index_type extents) const GSL_NOEXCEPT + index_type extents) const { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + const size_type size = this->bounds().total_size() - this->bounds().linearize(origin); return {&this->operator[](origin), size, strided_bounds{extents, details::make_stride(bounds())}}; } // length of the multi_span in elements - constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); } + constexpr size_type size() const noexcept { return bounds_.size(); } // length of the multi_span in elements - constexpr size_type length() const GSL_NOEXCEPT { return this->size(); } + constexpr size_type length() const noexcept { return this->size(); } // length of the multi_span in bytes - constexpr size_type size_bytes() const GSL_NOEXCEPT + constexpr size_type size_bytes() const noexcept { return narrow_cast(sizeof(value_type)) * this->size(); } // length of the multi_span in bytes - constexpr size_type length_bytes() const GSL_NOEXCEPT { return this->size_bytes(); } + constexpr size_type length_bytes() const noexcept { return this->size_bytes(); } - constexpr bool empty() const GSL_NOEXCEPT { return this->size() == 0; } + constexpr bool empty() const noexcept { return this->size() == 0; } static constexpr std::size_t rank() { return Rank; } template - constexpr size_type extent() const GSL_NOEXCEPT + constexpr size_type extent() const noexcept { static_assert(Dim < Rank, "Dimension should be less than rank (dimension count starts from 0)."); @@ -1468,14 +1529,14 @@ public: } template - constexpr size_type extent(IntType dim) const GSL_NOEXCEPT + constexpr size_type extent(IntType dim) const { return bounds_.extent(dim); } - constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; } + constexpr bounds_type bounds() const noexcept { return bounds_; } - constexpr pointer data() const GSL_NOEXCEPT { return data_; } + constexpr pointer data() const noexcept { return data_; } template constexpr reference operator()(FirstIndex idx) @@ -1486,18 +1547,21 @@ public: template constexpr reference operator()(FirstIndex firstIndex, OtherIndices... indices) { - index_type idx = {narrow_cast(firstIndex), - narrow_cast(indices)...}; + const index_type idx = {narrow_cast(firstIndex), + narrow_cast(indices)...}; return this->operator[](idx); } - constexpr reference operator[](const index_type& idx) const GSL_NOEXCEPT + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + constexpr reference operator[](const index_type& idx) const { return data_[bounds_.linearize(idx)]; } template 1), typename Ret = std::enable_if_t> - constexpr Ret operator[](size_type idx) const GSL_NOEXCEPT + + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + constexpr Ret operator[](size_type idx) const { Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array const size_type ridx = idx * bounds_.stride(); @@ -1507,85 +1571,81 @@ public: return Ret{data_ + ridx, bounds_.slice()}; } - constexpr iterator begin() const GSL_NOEXCEPT { return iterator{this, true}; } + constexpr iterator begin() const noexcept { return iterator{this, true}; } - constexpr iterator end() const GSL_NOEXCEPT { return iterator{this, false}; } + constexpr iterator end() const noexcept { return iterator{this, false}; } - constexpr const_iterator cbegin() const GSL_NOEXCEPT + GSL_SUPPRESS(type.1) // NO-FORMAT: attribute + constexpr const_iterator cbegin() const noexcept { return const_iterator{reinterpret_cast(this), true}; } - constexpr const_iterator cend() const GSL_NOEXCEPT + constexpr const_iterator cend() const noexcept { return const_iterator{reinterpret_cast(this), false}; } - constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return reverse_iterator{end()}; } + constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const GSL_NOEXCEPT { return reverse_iterator{begin()}; } + constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT + constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const GSL_NOEXCEPT + constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } template , std::remove_cv_t>::value>> - constexpr bool - operator==(const multi_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator==(const multi_span& other) const { return bounds_.size() == other.bounds_.size() && (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); } template , std::remove_cv_t>::value>> - constexpr bool - operator!=(const multi_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator!=(const multi_span& other) const { return !(*this == other); } template , std::remove_cv_t>::value>> - constexpr bool - operator<(const multi_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator<(const multi_span& other) const { return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); } template , std::remove_cv_t>::value>> - constexpr bool - operator<=(const multi_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator<=(const multi_span& other) const { return !(other < *this); } template , std::remove_cv_t>::value>> - constexpr bool - operator>(const multi_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator>(const multi_span& other) const + noexcept { return (other < *this); } template , std::remove_cv_t>::value>> - constexpr bool - operator>=(const multi_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator>=(const multi_span& other) const { return !(*this < other); } @@ -1606,14 +1666,14 @@ constexpr auto as_multi_span(SpanType s, Dimensions2... dims) "Variadic as_multi_span() is for reshaping existing spans."); using BoundsType = typename multi_span::bounds_type; - auto tobounds = details::static_as_multi_span_helper(dims..., details::Sep{}); + const auto tobounds = details::static_as_multi_span_helper(dims..., details::Sep{}); details::verifyBoundsReshape(s.bounds(), tobounds); return {s.data(), tobounds}; } // convert a multi_span to a multi_span template -multi_span as_bytes(multi_span s) GSL_NOEXCEPT +multi_span as_bytes(multi_span s) noexcept { static_assert(std::is_trivial>::value, "The value_type of multi_span must be a trivial type."); @@ -1625,7 +1685,7 @@ multi_span as_bytes(multi_span s) G // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -multi_span as_writeable_bytes(multi_span s) GSL_NOEXCEPT +multi_span as_writeable_bytes(multi_span s) noexcept { static_assert(std::is_trivial>::value, "The value_type of multi_span must be a trivial type."); @@ -1637,8 +1697,7 @@ multi_span as_writeable_bytes(multi_span s) GSL_NOEXCEPT // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -constexpr auto -as_multi_span(multi_span s) GSL_NOEXCEPT -> multi_span< +constexpr auto as_multi_span(multi_span s) -> multi_span< const U, static_cast( multi_span::bounds_type::static_size != dynamic_range ? (static_cast( @@ -1664,7 +1723,7 @@ as_multi_span(multi_span s) GSL_NOEXCEPT -> multi_spa // on all implementations. It should be considered an experimental extension // to the standard GSL interface. template -constexpr auto as_multi_span(multi_span s) GSL_NOEXCEPT +constexpr auto as_multi_span(multi_span s) -> multi_span( multi_span::bounds_type::static_size != dynamic_range ? static_cast( @@ -1673,11 +1732,10 @@ constexpr auto as_multi_span(multi_span s) GSL_NOEXCEPT : dynamic_range)> { using ByteSpan = multi_span; - static_assert( - std::is_trivial>::value && - (ByteSpan::bounds_type::static_size == dynamic_range || - ByteSpan::bounds_type::static_size % static_cast(sizeof(U)) == 0), - "Target type must be a trivial type and its size must match the byte array size"); + static_assert(std::is_trivial>::value && + (ByteSpan::bounds_type::static_size == dynamic_range || + ByteSpan::bounds_type::static_size % sizeof(U) == 0), + "Target type must be a trivial type and its size must match the byte array size"); Expects((s.size_bytes() % sizeof(U)) == 0); return {reinterpret_cast(s.data()), @@ -1743,6 +1801,7 @@ constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t< // from basic_string which doesn't have nonconst .data() member like other contiguous containers template +GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute constexpr auto as_multi_span(std::basic_string& str) -> multi_span { @@ -1788,6 +1847,7 @@ public: Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0); // Bounds cross data boundaries Expects(this->bounds().total_size() <= size); + GSL_SUPPRESS(type.4) // NO-FORMAT: attribute // TODO: false positive (void) size; } @@ -1795,8 +1855,7 @@ public: template constexpr strided_span(value_type (&values)[N], bounds_type bounds) : strided_span(values, N, std::move(bounds)) - { - } + {} // from array view template > constexpr strided_span(multi_span av, bounds_type bounds) : strided_span(av.data(), av.bounds().total_size(), std::move(bounds)) - { - } + {} // convertible template ::value>> constexpr strided_span(const strided_span& other) : data_(other.data_), bounds_(other.bounds_) - { - } + {} // convert from bytes template @@ -1828,7 +1885,7 @@ public: "OtherValueType should have a size to contain a multiple of ValueTypes"); auto d = narrow_cast(sizeof(OtherValueType) / sizeof(value_type)); - size_type size = this->bounds().total_size() / d; + const size_type size = this->bounds().total_size() / d; return {const_cast(reinterpret_cast(this->data())), size, bounds_type{resize_extent(this->bounds().index_bounds(), d), @@ -1837,17 +1894,19 @@ public: constexpr strided_span section(index_type origin, index_type extents) const { - size_type size = this->bounds().total_size() - this->bounds().linearize(origin); + const size_type size = this->bounds().total_size() - this->bounds().linearize(origin); return {&this->operator[](origin), size, bounds_type{extents, details::make_stride(bounds())}}; } + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute constexpr reference operator[](const index_type& idx) const { return data_[bounds_.linearize(idx)]; } template 1), typename Ret = std::enable_if_t> + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute constexpr Ret operator[](size_type idx) const { Expects(idx < bounds_.size()); // index is out of bounds of the array @@ -1858,21 +1917,23 @@ public: return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()}; } - constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; } + constexpr bounds_type bounds() const noexcept { return bounds_; } template - constexpr size_type extent() const GSL_NOEXCEPT + constexpr size_type extent() const noexcept { static_assert(Dim < Rank, "dimension should be less than Rank (dimension count starts from 0)"); return bounds_.template extent(); } - constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); } + constexpr size_type size() const noexcept { return bounds_.size(); } + + constexpr pointer data() const noexcept { return data_; } - constexpr pointer data() const GSL_NOEXCEPT { return data_; } + constexpr bool empty() const noexcept { return this->size() == 0; } - constexpr explicit operator bool() const GSL_NOEXCEPT { return data_ != nullptr; } + constexpr explicit operator bool() const noexcept { return data_ != nullptr; } constexpr iterator begin() const { return iterator{this, true}; } @@ -1897,56 +1958,50 @@ public: constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; } template , std::remove_cv_t>::value>> - constexpr bool - operator==(const strided_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator==(const strided_span& other) const { return bounds_.size() == other.bounds_.size() && (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin())); } template , std::remove_cv_t>::value>> - constexpr bool - operator!=(const strided_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator!=(const strided_span& other) const { return !(*this == other); } template , std::remove_cv_t>::value>> - constexpr bool - operator<(const strided_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator<(const strided_span& other) const { return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end()); } template , std::remove_cv_t>::value>> - constexpr bool - operator<=(const strided_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator<=(const strided_span& other) const { return !(other < *this); } template , std::remove_cv_t>::value>> - constexpr bool - operator>(const strided_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator>(const strided_span& other) const { return (other < *this); } template , std::remove_cv_t>::value>> - constexpr bool - operator>=(const strided_span& other) const GSL_NOEXCEPT + typename = std::enable_if_t, + std::remove_cv_t>::value>> + constexpr bool operator>=(const strided_span& other) const { return !(*this < other); } @@ -1955,6 +2010,7 @@ private: static index_type resize_extent(const index_type& extent, std::ptrdiff_t d) { // The last dimension of the array needs to contain a multiple of new type elements + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0)); index_type ret = extent; @@ -1967,12 +2023,14 @@ private: static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = nullptr) { // Only strided arrays with regular strides can be resized + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute Expects(strides[Rank - 1] == 1); return strides; } template 1), typename = std::enable_if_t> + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute static index_type resize_stride(const index_type& strides, std::ptrdiff_t d) { // Only strided arrays with regular strides can be resized @@ -1981,7 +2039,8 @@ private: // memory that can contain a multiple of new type elements Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0)); - for (std::size_t i = Rank - 1; i > 0; --i) { + for (std::size_t i = Rank - 1; i > 0; --i) + { // Only strided arrays with regular strides can be resized Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0)); } @@ -2009,96 +2068,98 @@ private: pointer data_; const Span* m_validator; - void validateThis() const - { + + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + void validateThis() const { // iterator is out of range of the array Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size()); } + + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute contiguous_span_iterator(const Span* container, bool isbegin) : data_(isbegin ? container->data_ : container->data_ + container->size()) , m_validator(container) - { - } + {} public: - reference operator*() const GSL_NOEXCEPT + reference operator*() const { validateThis(); return *data_; } - pointer operator->() const GSL_NOEXCEPT + pointer operator->() const { validateThis(); return data_; } - contiguous_span_iterator& operator++() GSL_NOEXCEPT + + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + contiguous_span_iterator& operator++() noexcept { ++data_; return *this; } - contiguous_span_iterator operator++(int) GSL_NOEXCEPT + contiguous_span_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - contiguous_span_iterator& operator--() GSL_NOEXCEPT + + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + contiguous_span_iterator& operator--() noexcept { --data_; return *this; } - contiguous_span_iterator operator--(int) GSL_NOEXCEPT + contiguous_span_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - contiguous_span_iterator operator+(difference_type n) const GSL_NOEXCEPT + contiguous_span_iterator operator+(difference_type n) const noexcept { contiguous_span_iterator ret{*this}; return ret += n; } - contiguous_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT + contiguous_span_iterator& operator+=(difference_type n) noexcept { data_ += n; return *this; } - contiguous_span_iterator operator-(difference_type n) const GSL_NOEXCEPT + contiguous_span_iterator operator-(difference_type n) const noexcept { contiguous_span_iterator ret{*this}; return ret -= n; } - contiguous_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } - difference_type operator-(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT + + contiguous_span_iterator& operator-=(difference_type n) { return *this += -n; } + difference_type operator-(const contiguous_span_iterator& rhs) const { Expects(m_validator == rhs.m_validator); return data_ - rhs.data_; } - reference operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); } - bool operator==(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT + reference operator[](difference_type n) const { return *(*this + n); } + bool operator==(const contiguous_span_iterator& rhs) const { Expects(m_validator == rhs.m_validator); return data_ == rhs.data_; } - bool operator!=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT - { - return !(*this == rhs); - } - bool operator<(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT + + bool operator!=(const contiguous_span_iterator& rhs) const { return !(*this == rhs); } + + bool operator<(const contiguous_span_iterator& rhs) const { Expects(m_validator == rhs.m_validator); return data_ < rhs.data_; } - bool operator<=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT - { - return !(rhs < *this); - } - bool operator>(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } - bool operator>=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT - { - return !(rhs > *this); - } - void swap(contiguous_span_iterator& rhs) GSL_NOEXCEPT + + bool operator<=(const contiguous_span_iterator& rhs) const { return !(rhs < *this); } + bool operator>(const contiguous_span_iterator& rhs) const { return rhs < *this; } + bool operator>=(const contiguous_span_iterator& rhs) const { return !(rhs > *this); } + + void swap(contiguous_span_iterator& rhs) noexcept { std::swap(data_, rhs.data_); std::swap(m_validator, rhs.m_validator); @@ -2107,14 +2168,13 @@ public: template contiguous_span_iterator operator+(typename contiguous_span_iterator::difference_type n, - const contiguous_span_iterator& rhs) GSL_NOEXCEPT + const contiguous_span_iterator& rhs) noexcept { return rhs + n; } template -class general_span_iterator -{ +class general_span_iterator { public: using iterator_category = std::random_access_iterator_tag; using value_type = typename Span::value_type; @@ -2131,72 +2191,73 @@ private: general_span_iterator(const Span* container, bool isbegin) : m_container(container) , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end()) - { - } + {} public: - reference operator*() GSL_NOEXCEPT { return (*m_container)[*m_itr]; } - pointer operator->() GSL_NOEXCEPT { return &(*m_container)[*m_itr]; } - general_span_iterator& operator++() GSL_NOEXCEPT + reference operator*() noexcept { return (*m_container)[*m_itr]; } + pointer operator->() noexcept { return &(*m_container)[*m_itr]; } + general_span_iterator& operator++() noexcept { ++m_itr; return *this; } - general_span_iterator operator++(int) GSL_NOEXCEPT + general_span_iterator operator++(int) noexcept { auto ret = *this; ++(*this); return ret; } - general_span_iterator& operator--() GSL_NOEXCEPT + general_span_iterator& operator--() noexcept { --m_itr; return *this; } - general_span_iterator operator--(int) GSL_NOEXCEPT + general_span_iterator operator--(int) noexcept { auto ret = *this; --(*this); return ret; } - general_span_iterator operator+(difference_type n) const GSL_NOEXCEPT + general_span_iterator operator+(difference_type n) const noexcept { general_span_iterator ret{*this}; return ret += n; } - general_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT + general_span_iterator& operator+=(difference_type n) noexcept { m_itr += n; return *this; } - general_span_iterator operator-(difference_type n) const GSL_NOEXCEPT + general_span_iterator operator-(difference_type n) const noexcept { general_span_iterator ret{*this}; return ret -= n; } - general_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; } - difference_type operator-(const general_span_iterator& rhs) const GSL_NOEXCEPT + general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; } + difference_type operator-(const general_span_iterator& rhs) const { Expects(m_container == rhs.m_container); return m_itr - rhs.m_itr; } - value_type operator[](difference_type n) const GSL_NOEXCEPT { return (*m_container)[m_itr[n]]; } - bool operator==(const general_span_iterator& rhs) const GSL_NOEXCEPT + GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute + value_type operator[](difference_type n) const { return (*m_container)[m_itr[n]]; } + + bool operator==(const general_span_iterator& rhs) const { Expects(m_container == rhs.m_container); return m_itr == rhs.m_itr; } - bool operator!=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(*this == rhs); } - bool operator<(const general_span_iterator& rhs) const GSL_NOEXCEPT + bool operator!=(const general_span_iterator& rhs) const { return !(*this == rhs); } + bool operator<(const general_span_iterator& rhs) const { Expects(m_container == rhs.m_container); return m_itr < rhs.m_itr; } - bool operator<=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs < *this); } - bool operator>(const general_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; } - bool operator>=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs > *this); } - void swap(general_span_iterator& rhs) GSL_NOEXCEPT + bool operator<=(const general_span_iterator& rhs) const { return !(rhs < *this); } + bool operator>(const general_span_iterator& rhs) const { return rhs < *this; } + bool operator>=(const general_span_iterator& rhs) const { return !(rhs > *this); } + void swap(general_span_iterator& rhs) noexcept { std::swap(m_itr, rhs.m_itr); std::swap(m_container, rhs.m_container); @@ -2205,15 +2266,13 @@ public: template general_span_iterator operator+(typename general_span_iterator::difference_type n, - const general_span_iterator& rhs) GSL_NOEXCEPT + const general_span_iterator& rhs) noexcept { return rhs + n; } } // namespace gsl -#undef GSL_NOEXCEPT - #ifdef _MSC_VER #if _MSC_VER < 1910 @@ -2225,4 +2284,8 @@ general_span_iterator operator+(typename general_span_iterator::diff #endif // _MSC_VER +#if __GNUC__ > 6 +#pragma GCC diagnostic pop +#endif // __GNUC__ > 6 + #endif // GSL_MULTI_SPAN_H diff --git a/third_party/gsl/gsl/include/gsl/pointers b/third_party/gsl/gsl/include/gsl/pointers index b2804a31..a3388561 100644 --- a/third_party/gsl/gsl/include/gsl/pointers +++ b/third_party/gsl/gsl/include/gsl/pointers @@ -72,7 +72,13 @@ public: static_assert(std::is_assignable::value, "T cannot be assigned nullptr."); template ::value>> - constexpr not_null(U&& u) : ptr_(std::forward(u)) + constexpr explicit not_null(U&& u) : ptr_(std::forward(u)) + { + Expects(ptr_ != nullptr); + } + + template ::value>> + constexpr explicit not_null(T u) : ptr_(u) { Expects(ptr_ != nullptr); } @@ -113,6 +119,11 @@ private: T ptr_; }; +template +auto make_not_null(T&& t) { + return gsl::not_null>>{std::forward(t)}; +} + template std::ostream& operator<<(std::ostream& os, const not_null& val) { diff --git a/third_party/gsl/gsl/include/gsl/span b/third_party/gsl/gsl/include/gsl/span index 6dd1c1f7..8cb3dbec 100644 --- a/third_party/gsl/gsl/include/gsl/span +++ b/third_party/gsl/gsl/include/gsl/span @@ -29,6 +29,7 @@ #include #include // for enable_if_t, declval, is_convertible, inte... #include +#include // for std::addressof #ifdef _MSC_VER #pragma warning(push) @@ -37,18 +38,17 @@ #pragma warning(disable : 4127) // conditional expression is constant #pragma warning(disable : 4702) // unreachable code -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) +// Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool. +#pragma warning(disable : 26495) // uninitalized member when constructor calls constructor +#pragma warning(disable : 26446) // parser bug does not allow attributes on some templates #if _MSC_VER < 1910 #pragma push_macro("constexpr") #define constexpr /*constexpr*/ #define GSL_USE_STATIC_CONSTEXPR_WORKAROUND -#endif // _MSC_VER < 1910 -#else // _MSC_VER +#endif // _MSC_VER < 1910 +#endif // _MSC_VER // See if we have enough C++17 power to use a static constexpr data member // without needing an out-of-line definition @@ -56,7 +56,15 @@ #define GSL_USE_STATIC_CONSTEXPR_WORKAROUND #endif // !(defined(__cplusplus) && (__cplusplus >= 201703L)) -#endif // _MSC_VER +// GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t) +// While there is a conversion from signed to unsigned, it happens at +// compiletime, so the compiler wouldn't have to warn indiscriminently, but +// could check if the source value actually doesn't fit into the target type +// and only warn in those cases. +#if __GNUC__ > 6 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif namespace gsl { @@ -119,7 +127,6 @@ namespace details using element_type_ = typename Span::element_type; public: - #ifdef _MSC_VER // Tell Microsoft standard library that span_iterators are checked. using _Unchecked_type = typename Span::pointer; @@ -139,12 +146,12 @@ namespace details {} friend span_iterator; - template* = nullptr> + template * = nullptr> constexpr span_iterator(const span_iterator& other) noexcept : span_iterator(other.span_, other.index_) - { - } + {} + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute constexpr reference operator*() const { Expects(index_ != span_->size()); @@ -191,6 +198,11 @@ namespace details return ret += n; } + friend constexpr span_iterator operator+(difference_type n, span_iterator const& rhs) + { + return rhs + n; + } + constexpr span_iterator& operator+=(difference_type n) { Expects((index_ + n) >= 0 && (index_ + n) <= span_->size()); @@ -212,68 +224,82 @@ namespace details return index_ - rhs.index_; } - constexpr reference operator[](difference_type n) const - { - return *(*this + n); - } + constexpr reference operator[](difference_type n) const { return *(*this + n); } - constexpr friend bool operator==(span_iterator lhs, - span_iterator rhs) noexcept + constexpr friend bool operator==(span_iterator lhs, span_iterator rhs) noexcept { return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_; } - constexpr friend bool operator!=(span_iterator lhs, - span_iterator rhs) noexcept + constexpr friend bool operator!=(span_iterator lhs, span_iterator rhs) noexcept { return !(lhs == rhs); } - constexpr friend bool operator<(span_iterator lhs, - span_iterator rhs) noexcept + constexpr friend bool operator<(span_iterator lhs, span_iterator rhs) noexcept { return lhs.index_ < rhs.index_; } - constexpr friend bool operator<=(span_iterator lhs, - span_iterator rhs) noexcept + constexpr friend bool operator<=(span_iterator lhs, span_iterator rhs) noexcept { return !(rhs < lhs); } - constexpr friend bool operator>(span_iterator lhs, - span_iterator rhs) noexcept + constexpr friend bool operator>(span_iterator lhs, span_iterator rhs) noexcept { return rhs < lhs; } - constexpr friend bool operator>=(span_iterator lhs, - span_iterator rhs) noexcept + constexpr friend bool operator>=(span_iterator lhs, span_iterator rhs) noexcept { return !(rhs > lhs); } +#ifdef _MSC_VER + // MSVC++ iterator debugging support; allows STL algorithms in 15.8+ + // to unwrap span_iterator to a pointer type after a range check in STL + // algorithm calls + friend constexpr void _Verify_range(span_iterator lhs, span_iterator rhs) noexcept + { // test that [lhs, rhs) forms a valid range inside an STL algorithm + Expects(lhs.span_ == rhs.span_ // range spans have to match + && lhs.index_ <= rhs.index_); // range must not be transposed + } + + constexpr void _Verify_offset(const difference_type n) const noexcept + { // test that the iterator *this + n is a valid range in an STL + // algorithm call + Expects((index_ + n) >= 0 && (index_ + n) <= span_->size()); + } + + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + constexpr pointer _Unwrapped() const noexcept + { // after seeking *this to a high water mark, or using one of the + // _Verify_xxx functions above, unwrap this span_iterator to a raw + // pointer + return span_->data() + index_; + } + + // Tell the STL that span_iterator should not be unwrapped if it can't + // validate in advance, even in release / optimized builds: +#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) + static constexpr const bool _Unwrap_when_unverified = false; +#else + static constexpr bool _Unwrap_when_unverified = false; +#endif + GSL_SUPPRESS(con.3) // NO-FORMAT: attribute // TODO: false positive + constexpr void _Seek_to(const pointer p) noexcept + { // adjust the position of *this to previously verified location p + // after _Unwrapped + index_ = p - span_->data(); + } +#endif + protected: const Span* span_ = nullptr; std::ptrdiff_t index_ = 0; }; - template - constexpr span_iterator - operator+(typename span_iterator::difference_type n, - span_iterator rhs) - { - return rhs + n; - } - - template - constexpr span_iterator - operator-(typename span_iterator::difference_type n, - span_iterator rhs) - { - return rhs - n; - } - template class extent_type { @@ -305,8 +331,7 @@ namespace details template explicit constexpr extent_type(extent_type ext) : size_(ext.size()) - { - } + {} explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } @@ -319,7 +344,9 @@ namespace details template struct calculate_subspan_type { - using type = span; + using type = span; }; } // namespace details @@ -343,9 +370,9 @@ public: using size_type = index_type; #if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) - static constexpr const index_type extent { Extent }; + static constexpr const index_type extent{Extent}; #else - static constexpr index_type extent { Extent }; + static constexpr index_type extent{Extent}; #endif // [span.cons], span constructors, copy, assignment, and destructor @@ -354,33 +381,30 @@ public: // since "std::enable_if_t" is ill-formed when Extent is greater than 0. class = std::enable_if_t<(Dependent || Extent <= 0)>> constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) - { - } + {} constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} constexpr span(pointer firstElem, pointer lastElem) : storage_(firstElem, std::distance(firstElem, lastElem)) - { - } + {} template constexpr span(element_type (&arr)[N]) noexcept - : storage_(KnownNotNull{&arr[0]}, details::extent_type()) - { - } + : storage_(KnownNotNull{std::addressof(arr[0])}, details::extent_type()) + {} template > + // GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug constexpr span(std::array& arr) noexcept - : storage_(&arr[0], details::extent_type()) - { - } + : storage_(arr.data(), details::extent_type()) + {} template + // GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug constexpr span(const std::array, N>& arr) noexcept - : storage_(&arr[0], details::extent_type()) - { - } + : storage_(arr.data(), details::extent_type()) + {} // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement // on Container to be a contiguous sequence container. @@ -391,8 +415,7 @@ public: std::is_convertible().data())>::value>> constexpr span(Container& cont) : span(cont.data(), narrow(cont.size())) - { - } + {} template ().data())>::value>> constexpr span(const Container& cont) : span(cont.data(), narrow(cont.size())) - { - } + {} constexpr span(const span& other) noexcept = default; @@ -413,12 +435,11 @@ public: details::is_allowed_element_type_conversion::value>> constexpr span(const span& other) : storage_(other.data(), details::extent_type(other.size())) - { - } + {} ~span() noexcept = default; constexpr span& operator=(const span& other) noexcept = default; - + // [span.sub], span subviews template constexpr span first() const @@ -428,6 +449,7 @@ public: } template + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute constexpr span last() const { Expects(Count >= 0 && size() - Count >= 0); @@ -435,7 +457,9 @@ public: } template - constexpr auto subspan() const -> typename details::calculate_subspan_type::type + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + constexpr auto subspan() const -> + typename details::calculate_subspan_type::type { Expects((Offset >= 0 && size() - Offset >= 0) && (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size()))); @@ -460,7 +484,6 @@ public: return make_subspan(offset, count, subspan_selector{}); } - // [span.obs], span observers constexpr index_type size() const noexcept { return storage_.size(); } constexpr index_type size_bytes() const noexcept @@ -470,9 +493,10 @@ public: constexpr bool empty() const noexcept { return size() == 0; } // [span.elem], span element access + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute constexpr reference operator[](index_type idx) const { - Expects(idx >= 0 && idx < storage_.size()); + Expects(CheckRange(idx, storage_.size())); return data()[idx]; } @@ -490,13 +514,50 @@ public: constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; } + constexpr const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator{cend()}; + } + constexpr const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator{cbegin()}; + } + +#ifdef _MSC_VER + // Tell MSVC how to unwrap spans in range-based-for + constexpr pointer _Unchecked_begin() const noexcept { return data(); } + constexpr pointer _Unchecked_end() const noexcept + { + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + return data() + size(); + } +#endif // _MSC_VER private: + static bool CheckRange(index_type idx, index_type size) + { + // Optimization: + // + // idx >= 0 && idx < size + // => + // static_cast(idx) < static_cast(size) + // + // because size >=0 by span construction, and negative idx will + // wrap around to a value always greater than size when casted. + + // check if we have enough space to wrap around + if (sizeof(index_type) <= sizeof(size_t)) + { + return narrow_cast(idx) < narrow_cast(size); + } + else + { + return idx >= 0 && idx < size; + } + } // Needed to remove unnecessary null check in subspans - struct KnownNotNull + struct KnownNotNull { pointer p; }; @@ -508,20 +569,20 @@ private: class storage_type : public ExtentType { public: - // KnownNotNull parameter is needed to remove unnecessary null check + // KnownNotNull parameter is needed to remove unnecessary null check // in subspans and constructors from arrays template - constexpr storage_type(KnownNotNull data, OtherExtentType ext) : ExtentType(ext), data_(data.p) + constexpr storage_type(KnownNotNull data, OtherExtentType ext) + : ExtentType(ext), data_(data.p) { - Expects(ExtentType::size() >= 0); + Expects(ExtentType::size() >= 0); } - template constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) { - Expects(ExtentType::size() >= 0); - Expects(data || ExtentType::size() == 0); + Expects(ExtentType::size() >= 0); + Expects(data || ExtentType::size() == 0); } constexpr pointer data() const noexcept { return data_; } @@ -537,29 +598,28 @@ private: constexpr span(KnownNotNull ptr, index_type count) : storage_(ptr, count) {} template - class subspan_selector {}; + class subspan_selector + { + }; template - span make_subspan(index_type offset, - index_type count, + span make_subspan(index_type offset, index_type count, subspan_selector) const { - span tmp(*this); + const span tmp(*this); return tmp.subspan(offset, count); } - span make_subspan(index_type offset, - index_type count, + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute + span make_subspan(index_type offset, index_type count, subspan_selector) const { Expects(offset >= 0 && size() - offset >= 0); - if (count == dynamic_extent) - { - return { KnownNotNull{ data() + offset }, size() - offset }; - } + + if (count == dynamic_extent) { return {KnownNotNull{data() + offset}, size() - offset}; } Expects(count >= 0 && size() - offset >= count); - return { KnownNotNull{ data() + offset }, count }; + return {KnownNotNull{data() + offset}, count}; } }; @@ -568,46 +628,39 @@ template constexpr const typename span::index_type span::extent; #endif - // [span.comparison], span comparison operators template -constexpr bool operator==(span l, - span r) +constexpr bool operator==(span l, span r) { return std::equal(l.begin(), l.end(), r.begin(), r.end()); } template -constexpr bool operator!=(span l, - span r) +constexpr bool operator!=(span l, span r) { return !(l == r); } template -constexpr bool operator<(span l, - span r) +constexpr bool operator<(span l, span r) { return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); } template -constexpr bool operator<=(span l, - span r) +constexpr bool operator<=(span l, span r) { return !(l > r); } template -constexpr bool operator>(span l, - span r) +constexpr bool operator>(span l, span r) { return r < l; } template -constexpr bool operator>=(span l, - span r) +constexpr bool operator>=(span l, span r) { return !(l < r); } @@ -633,13 +686,14 @@ namespace details : std::integral_constant { }; -} +} // namespace details // [span.objectrep], views of object representation template span::value> as_bytes(span s) noexcept { + GSL_SUPPRESS(type.1) // NO-FORMAT: attribute return {reinterpret_cast(s.data()), s.size_bytes()}; } @@ -648,6 +702,7 @@ template ::value> as_writeable_bytes(span s) noexcept { + GSL_SUPPRESS(type.1) // NO-FORMAT: attribute return {reinterpret_cast(s.data()), s.size_bytes()}; } @@ -655,7 +710,8 @@ as_writeable_bytes(span s) noexcept // make_span() - Utility functions for creating spans // template -constexpr span make_span(ElementType* ptr, typename span::index_type count) +constexpr span make_span(ElementType* ptr, + typename span::index_type count) { return span(ptr, count); } @@ -716,4 +772,8 @@ constexpr ElementType& at(span s, index i) #pragma warning(pop) #endif // _MSC_VER +#if __GNUC__ > 6 +#pragma GCC diagnostic pop +#endif // __GNUC__ > 6 + #endif // GSL_SPAN_H diff --git a/third_party/gsl/gsl/include/gsl/string_span b/third_party/gsl/gsl/include/gsl/string_span index 0bb98d21..d298039c 100644 --- a/third_party/gsl/gsl/include/gsl/string_span +++ b/third_party/gsl/gsl/include/gsl/string_span @@ -20,6 +20,7 @@ #include // for Ensures, Expects #include // for narrow_cast #include // for operator!=, operator==, dynamic_extent +#include // for not_null #include // for equal, lexicographical_compare #include // for array @@ -32,10 +33,9 @@ #ifdef _MSC_VER #pragma warning(push) -// blanket turn off warnings from CppCoreCheck for now -// so people aren't annoyed by them when running the tool. -// more targeted suppressions will be added in a future update to the GSL -#pragma warning(disable : 26481 26482 26483 26485 26490 26491 26492 26493 26495) +// Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool. +#pragma warning(disable : 26446) // TODO: bug in parser - attributes and templates +#pragma warning(disable : 26481) // TODO: suppress does not work inside templates sometimes #if _MSC_VER < 1910 #pragma push_macro("constexpr") @@ -44,13 +44,6 @@ #endif // _MSC_VER < 1910 #endif // _MSC_VER -// In order to test the library, we need it to throw exceptions that we can catch -#ifdef GSL_THROW_ON_CONTRACT_VIOLATION -#define GSL_NOEXCEPT /*noexcept*/ -#else -#define GSL_NOEXCEPT noexcept -#endif // GSL_THROW_ON_CONTRACT_VIOLATION - namespace gsl { // @@ -105,7 +98,7 @@ namespace details return len; } -} +} // namespace details // // ensure_sentinel() @@ -118,7 +111,13 @@ namespace details template span ensure_sentinel(T* seq, std::ptrdiff_t max = PTRDIFF_MAX) { + Ensures(seq != nullptr); + + GSL_SUPPRESS(f.23) // NO-FORMAT: attribute // TODO: false positive // TODO: suppress does not work auto cur = seq; + Ensures(cur != nullptr); // workaround for removing the warning + + GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute // TODO: suppress does not work while ((cur - seq) < max && *cur != Sentinel) ++cur; Ensures(*cur == Sentinel); return {seq, cur - seq}; @@ -138,21 +137,20 @@ span ensure_z(CharT* const& sz, std::ptrdiff_t max = PTRD template span ensure_z(CharT (&sz)[N]) { - return ensure_z(&sz[0], static_cast(N)); + return ensure_z(&sz[0], narrow_cast(N)); } template span::type, dynamic_extent> ensure_z(Cont& cont) { - return ensure_z(cont.data(), static_cast(cont.size())); + return ensure_z(cont.data(), narrow_cast(cont.size())); } template class basic_string_span; -namespace details -{ +namespace details { template struct is_basic_string_span_oracle : std::false_type { @@ -167,7 +165,7 @@ namespace details struct is_basic_string_span : is_basic_string_span_oracle> { }; -} +} // namespace details // // string_span and relatives @@ -189,13 +187,13 @@ public: using const_reverse_iterator = typename impl_type::const_reverse_iterator; // default (empty) - constexpr basic_string_span() GSL_NOEXCEPT = default; + constexpr basic_string_span() noexcept = default; // copy - constexpr basic_string_span(const basic_string_span& other) GSL_NOEXCEPT = default; + constexpr basic_string_span(const basic_string_span& other) noexcept = default; // assign - constexpr basic_string_span& operator=(const basic_string_span& other) GSL_NOEXCEPT = default; + constexpr basic_string_span& operator=(const basic_string_span& other) noexcept = default; constexpr basic_string_span(pointer ptr, index_type length) : span_(ptr, length) {} constexpr basic_string_span(pointer firstElem, pointer lastElem) : span_(firstElem, lastElem) {} @@ -204,32 +202,27 @@ public: // All other containers allow 0s within the length, so we do not remove them template constexpr basic_string_span(element_type (&arr)[N]) : span_(remove_z(arr)) - { - } + {} template > - constexpr basic_string_span(std::array& arr) GSL_NOEXCEPT : span_(arr) - { - } + constexpr basic_string_span(std::array& arr) noexcept : span_(arr) + {} template > - constexpr basic_string_span(const std::array& arr) GSL_NOEXCEPT - : span_(arr) - { - } + constexpr basic_string_span(const std::array& arr) noexcept : span_(arr) + {} // Container signature should work for basic_string after C++17 version exists template + // GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug constexpr basic_string_span(std::basic_string& str) : span_(&str[0], narrow_cast(str.length())) - { - } + {} template constexpr basic_string_span(const std::basic_string& str) : span_(&str[0], str.length()) - { - } + {} // from containers. Containers must have a pointer type and data() function signatures template ().data())>::value>> constexpr basic_string_span(Container& cont) : span_(cont) - { - } + {} template ().data())>::value>> constexpr basic_string_span(const Container& cont) : span_(cont) - { - } + {} // from string_span template < @@ -259,8 +250,7 @@ public: typename basic_string_span::impl_type, impl_type>::value>> constexpr basic_string_span(basic_string_span other) : span_(other.data(), other.length()) - { - } + {} template constexpr basic_string_span first() const @@ -301,23 +291,23 @@ public: constexpr pointer data() const { return span_.data(); } - constexpr index_type length() const GSL_NOEXCEPT { return span_.size(); } - constexpr index_type size() const GSL_NOEXCEPT { return span_.size(); } - constexpr index_type size_bytes() const GSL_NOEXCEPT { return span_.size_bytes(); } - constexpr index_type length_bytes() const GSL_NOEXCEPT { return span_.length_bytes(); } - constexpr bool empty() const GSL_NOEXCEPT { return size() == 0; } + constexpr index_type length() const noexcept { return span_.size(); } + constexpr index_type size() const noexcept { return span_.size(); } + constexpr index_type size_bytes() const noexcept { return span_.size_bytes(); } + constexpr index_type length_bytes() const noexcept { return span_.length_bytes(); } + constexpr bool empty() const noexcept { return size() == 0; } - constexpr iterator begin() const GSL_NOEXCEPT { return span_.begin(); } - constexpr iterator end() const GSL_NOEXCEPT { return span_.end(); } + constexpr iterator begin() const noexcept { return span_.begin(); } + constexpr iterator end() const noexcept { return span_.end(); } - constexpr const_iterator cbegin() const GSL_NOEXCEPT { return span_.cbegin(); } - constexpr const_iterator cend() const GSL_NOEXCEPT { return span_.cend(); } + constexpr const_iterator cbegin() const noexcept { return span_.cbegin(); } + constexpr const_iterator cend() const noexcept { return span_.cend(); } - constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return span_.rbegin(); } - constexpr reverse_iterator rend() const GSL_NOEXCEPT { return span_.rend(); } + constexpr reverse_iterator rbegin() const noexcept { return span_.rbegin(); } + constexpr reverse_iterator rend() const noexcept { return span_.rend(); } - constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT { return span_.crbegin(); } - constexpr const_reverse_iterator crend() const GSL_NOEXCEPT { return span_.crend(); } + constexpr const_reverse_iterator crbegin() const noexcept { return span_.crbegin(); } + constexpr const_reverse_iterator crend() const noexcept { return span_.crend(); } private: static impl_type remove_z(pointer const& sz, std::ptrdiff_t max) @@ -366,21 +356,37 @@ template std::basic_string::type> to_string(basic_string_span view) { - return {view.data(), static_cast(view.length())}; + return {view.data(), narrow_cast(view.length())}; } template , typename Allocator = std::allocator, typename gCharT, std::ptrdiff_t Extent> std::basic_string to_basic_string(basic_string_span view) { - return {view.data(), static_cast(view.length())}; + return {view.data(), narrow_cast(view.length())}; +} + +template +basic_string_span::value> +as_bytes(basic_string_span s) noexcept +{ + GSL_SUPPRESS(type.1) // NO-FORMAT: attribute + return {reinterpret_cast(s.data()), s.size_bytes()}; +} + +template ::value>> +basic_string_span::value> +as_writeable_bytes(basic_string_span s) noexcept +{ + GSL_SUPPRESS(type.1) // NO-FORMAT: attribute + return {reinterpret_cast(s.data()), s.size_bytes()}; } // zero-terminated string span, used to convert // zero-terminated spans to legacy strings template -class basic_zstring_span -{ +class basic_zstring_span { public: using value_type = CharT; using const_value_type = std::add_const_t; @@ -394,7 +400,7 @@ public: using impl_type = span; using string_span_type = basic_string_span; - constexpr basic_zstring_span(impl_type s) GSL_NOEXCEPT : span_(s) + constexpr basic_zstring_span(impl_type s) : span_(s) { // expects a zero-terminated span Expects(s[s.size() - 1] == '\0'); @@ -412,16 +418,16 @@ public: // move assign constexpr basic_zstring_span& operator=(basic_zstring_span&& other) = default; - constexpr bool empty() const GSL_NOEXCEPT { return span_.size() == 0; } + constexpr bool empty() const noexcept { return span_.size() == 0; } - constexpr string_span_type as_string_span() const GSL_NOEXCEPT + constexpr string_span_type as_string_span() const noexcept { - auto sz = span_.size(); - return { span_.data(), sz > 1 ? sz - 1 : 0 }; + const auto sz = span_.size(); + return {span_.data(), sz > 1 ? sz - 1 : 0}; } - constexpr string_span_type ensure_z() const GSL_NOEXCEPT { return gsl::ensure_z(span_); } + constexpr string_span_type ensure_z() const { return gsl::ensure_z(span_); } - constexpr const_zstring_type assume_z() const GSL_NOEXCEPT { return span_.data(); } + constexpr const_zstring_type assume_z() const noexcept { return span_.data(); } private: impl_type span_; @@ -456,7 +462,7 @@ template ::value || std::is_convertible>>::value>> -bool operator==(const gsl::basic_string_span& one, const T& other) GSL_NOEXCEPT +bool operator==(const gsl::basic_string_span& one, const T& other) { const gsl::basic_string_span> tmp(other); return std::equal(one.begin(), one.end(), tmp.begin(), tmp.end()); @@ -466,9 +472,9 @@ template ::value && std::is_convertible>>::value>> -bool operator==(const T& one, const gsl::basic_string_span& other) GSL_NOEXCEPT +bool operator==(const T& one, const gsl::basic_string_span& other) { - gsl::basic_string_span> tmp(one); + const gsl::basic_string_span> tmp(one); return std::equal(tmp.begin(), tmp.end(), other.begin(), other.end()); } @@ -476,7 +482,7 @@ bool operator==(const T& one, const gsl::basic_string_span& other template , Extent>>::value>> -bool operator!=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +bool operator!=(gsl::basic_string_span one, const T& other) { return !(one == other); } @@ -486,7 +492,7 @@ template < typename = std::enable_if_t< std::is_convertible, Extent>>::value && !gsl::details::is_basic_string_span::value>> -bool operator!=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +bool operator!=(const T& one, gsl::basic_string_span other) { return !(one == other); } @@ -495,7 +501,7 @@ bool operator!=(const T& one, gsl::basic_string_span other) GSL_N template , Extent>>::value>> -bool operator<(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +bool operator<(gsl::basic_string_span one, const T& other) { const gsl::basic_string_span, Extent> tmp(other); return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); @@ -506,7 +512,7 @@ template < typename = std::enable_if_t< std::is_convertible, Extent>>::value && !gsl::details::is_basic_string_span::value>> -bool operator<(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +bool operator<(const T& one, gsl::basic_string_span other) { gsl::basic_string_span, Extent> tmp(one); return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); @@ -525,7 +531,7 @@ template < std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value>> -bool operator<(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +bool operator<(gsl::basic_string_span one, const T& other) { gsl::basic_string_span, Extent> tmp(other); return std::lexicographical_compare(one.begin(), one.end(), tmp.begin(), tmp.end()); @@ -539,7 +545,7 @@ template < std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value>> -bool operator<(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +bool operator<(const T& one, gsl::basic_string_span other) { gsl::basic_string_span, Extent> tmp(one); return std::lexicographical_compare(tmp.begin(), tmp.end(), other.begin(), other.end()); @@ -550,7 +556,7 @@ bool operator<(const T& one, gsl::basic_string_span other) GSL_NO template , Extent>>::value>> -bool operator<=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +bool operator<=(gsl::basic_string_span one, const T& other) { return !(other < one); } @@ -560,7 +566,7 @@ template < typename = std::enable_if_t< std::is_convertible, Extent>>::value && !gsl::details::is_basic_string_span::value>> -bool operator<=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +bool operator<=(const T& one, gsl::basic_string_span other) { return !(other < one); } @@ -578,7 +584,7 @@ template < std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value>> -bool operator<=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +bool operator<=(gsl::basic_string_span one, const T& other) { return !(other < one); } @@ -591,7 +597,7 @@ template < std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value>> -bool operator<=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +bool operator<=(const T& one, gsl::basic_string_span other) { return !(other < one); } @@ -601,7 +607,7 @@ bool operator<=(const T& one, gsl::basic_string_span other) GSL_N template , Extent>>::value>> -bool operator>(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +bool operator>(gsl::basic_string_span one, const T& other) { return other < one; } @@ -611,7 +617,7 @@ template < typename = std::enable_if_t< std::is_convertible, Extent>>::value && !gsl::details::is_basic_string_span::value>> -bool operator>(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +bool operator>(const T& one, gsl::basic_string_span other) { return other < one; } @@ -629,7 +635,7 @@ template < std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value>> -bool operator>(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +bool operator>(gsl::basic_string_span one, const T& other) { return other < one; } @@ -642,7 +648,7 @@ template < std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value>> -bool operator>(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +bool operator>(const T& one, gsl::basic_string_span other) { return other < one; } @@ -652,7 +658,7 @@ bool operator>(const T& one, gsl::basic_string_span other) GSL_NO template , Extent>>::value>> -bool operator>=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +bool operator>=(gsl::basic_string_span one, const T& other) { return !(one < other); } @@ -662,7 +668,7 @@ template < typename = std::enable_if_t< std::is_convertible, Extent>>::value && !gsl::details::is_basic_string_span::value>> -bool operator>=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +bool operator>=(const T& one, gsl::basic_string_span other) { return !(one < other); } @@ -680,7 +686,7 @@ template < std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value>> -bool operator>=(gsl::basic_string_span one, const T& other) GSL_NOEXCEPT +bool operator>=(gsl::basic_string_span one, const T& other) { return !(one < other); } @@ -693,14 +699,12 @@ template < std::is_convertible::value && std::is_same().size(), *std::declval().data())>, DataType>::value>> -bool operator>=(const T& one, gsl::basic_string_span other) GSL_NOEXCEPT +bool operator>=(const T& one, gsl::basic_string_span other) { return !(one < other); } #endif -} // namespace GSL - -#undef GSL_NOEXCEPT +} // namespace gsl #ifdef _MSC_VER #pragma warning(pop) diff --git a/third_party/spdlog/VERSION b/third_party/spdlog/VERSION new file mode 100644 index 00000000..1cc5f657 --- /dev/null +++ b/third_party/spdlog/VERSION @@ -0,0 +1 @@ +1.1.0 \ No newline at end of file diff --git a/third_party/spdlog/spdlog/include/spdlog/async.h b/third_party/spdlog/spdlog/include/spdlog/async.h new file mode 100644 index 00000000..9c3e9551 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/async.h @@ -0,0 +1,87 @@ + +// +// Copyright(c) 2018 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// +// Async logging using global thread pool +// All loggers created here share same global thread pool. +// Each log message is pushed to a queue along withe a shared pointer to the +// logger. +// If a logger deleted while having pending messages in the queue, it's actual +// destruction will defer +// until all its messages are processed by the thread pool. +// This is because each message in the queue holds a shared_ptr to the +// originating logger. + +#include "spdlog/async_logger.h" +#include "spdlog/details/registry.h" +#include "spdlog/details/thread_pool.h" + +#include +#include + +namespace spdlog { + +namespace details { +static const size_t default_async_q_size = 8192; +} + +// async logger factory - creates async loggers backed with thread pool. +// if a global thread pool doesn't already exist, create it with default queue +// size of 8192 items and single thread. +template +struct async_factory_impl +{ + template + static std::shared_ptr create(const std::string &logger_name, SinkArgs &&... args) + { + auto ®istry_inst = details::registry::instance(); + + // create global thread pool if not already exists.. + std::lock_guard tp_lock(registry_inst.tp_mutex()); + auto tp = registry_inst.get_tp(); + if (tp == nullptr) + { + tp = std::make_shared(details::default_async_q_size, 1); + registry_inst.set_tp(tp); + } + + auto sink = std::make_shared(std::forward(args)...); + auto new_logger = std::make_shared(logger_name, std::move(sink), std::move(tp), OverflowPolicy); + registry_inst.register_and_init(new_logger); + return new_logger; + } +}; + +using async_factory = async_factory_impl; +using async_factory_nonblock = async_factory_impl; + +template +inline std::shared_ptr create_async(const std::string &logger_name, SinkArgs &&... sink_args) +{ + return async_factory::create(logger_name, std::forward(sink_args)...); +} + +template +inline std::shared_ptr create_async_nb(const std::string &logger_name, SinkArgs &&... sink_args) +{ + return async_factory_nonblock::create(logger_name, std::forward(sink_args)...); +} + +// set global thread pool. +inline void init_thread_pool(size_t q_size, size_t thread_count) +{ + auto tp = std::make_shared(q_size, thread_count); + details::registry::instance().set_tp(std::move(tp)); +} + +// get the global thread pool. +inline std::shared_ptr thread_pool() +{ + return details::registry::instance().get_tp(); +} +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/async_logger.h b/third_party/spdlog/spdlog/include/spdlog/async_logger.h index 75641d26..3250f4ad 100644 --- a/third_party/spdlog/spdlog/include/spdlog/async_logger.h +++ b/third_party/spdlog/spdlog/include/spdlog/async_logger.h @@ -5,67 +5,66 @@ #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. +// 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) +// 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.. +// Upon destruction, logs all remaining messages in the queue before +// destructing.. -#include "common.h" -#include "logger.h" +#include "spdlog/common.h" +#include "spdlog/logger.h" #include -#include #include #include namespace spdlog { +// Async overflow policy - block by default. +enum class async_overflow_policy +{ + block, // Block until message can be enqueued + overrun_oldest // Discard oldest message in the queue if full when trying to + // add new item. +}; + namespace details { -class async_log_helper; +class thread_pool; } -class async_logger SPDLOG_FINAL : public logger +class async_logger SPDLOG_FINAL : public std::enable_shared_from_this, public logger { -public: - template - async_logger(const std::string &logger_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); + friend class details::thread_pool; - 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); +public: + template + async_logger(std::string logger_name, const It &begin, const It &end, std::weak_ptr tp, + async_overflow_policy overflow_policy = async_overflow_policy::block); - // Wait for the queue to be empty, and flush synchronously - // Warning: this can potentially last forever as we wait it to complete - void flush() override; + async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, + async_overflow_policy overflow_policy = async_overflow_policy::block); - // Error handler - void set_error_handler(log_err_handler) override; - log_err_handler error_handler() override; + async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, + async_overflow_policy overflow_policy = async_overflow_policy::block); 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, pattern_time_type pattern_time) override; + void sink_it_(details::log_msg &msg) override; + void flush_() override; + + void backend_log_(details::log_msg &incoming_log_msg); + void backend_flush_(); private: - std::unique_ptr _async_log_helper; + std::weak_ptr thread_pool_; + async_overflow_policy overflow_policy_; }; } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/common.h b/third_party/spdlog/spdlog/include/spdlog/common.h index 88c2335e..1542fa92 100644 --- a/third_party/spdlog/spdlog/include/spdlog/common.h +++ b/third_party/spdlog/spdlog/include/spdlog/common.h @@ -5,25 +5,23 @@ #pragma once -#define SPDLOG_VERSION "0.17.0" - -#include "tweakme.h" +#include "spdlog/tweakme.h" #include #include -#include #include #include #include +#include #include #include -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) #include #include #endif -#include "details/null_mutex.h" +#include "spdlog/details/null_mutex.h" // visual studio upto 2013 does not support noexcept nor constexpr #if defined(_MSC_VER) && (_MSC_VER < 1900) @@ -49,7 +47,7 @@ #define SPDLOG_DEPRECATED #endif -#include "fmt/fmt.h" +#include "spdlog/fmt/fmt.h" namespace spdlog { @@ -62,15 +60,14 @@ class sink; using log_clock = std::chrono::system_clock; using sink_ptr = std::shared_ptr; using sinks_init_list = std::initializer_list; -using formatter_ptr = std::shared_ptr; +using log_err_handler = std::function; + #if defined(SPDLOG_NO_ATOMIC_LEVELS) using level_t = details::null_atomic_int; #else using level_t = std::atomic; #endif -using log_err_handler = std::function; - // Log level enum namespace level { enum level_enum @@ -94,15 +91,16 @@ static const char *level_names[] SPDLOG_LEVEL_NAMES; static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"}; -inline const char *to_str(spdlog::level::level_enum l) +inline const char *to_c_str(spdlog::level::level_enum l) { return level_names[l]; } -inline const char *to_short_str(spdlog::level::level_enum l) +inline const char *to_short_c_str(spdlog::level::level_enum l) { return short_level_names[l]; } + inline spdlog::level::level_enum from_str(const std::string &name) { static std::unordered_map name_to_level = // map string->level @@ -121,15 +119,6 @@ inline spdlog::level::level_enum from_str(const std::string &name) using level_hasher = std::hash; } // namespace 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 -}; - // // Pattern time - specific time getting to use for pattern_formatter. // local time by default @@ -143,31 +132,28 @@ enum class pattern_time_type // // Log exception // -namespace details { -namespace os { -std::string errno_str(int err_num); -} -} // namespace details class spdlog_ex : public std::exception { public: - explicit spdlog_ex(std::string msg) - : _msg(std::move(msg)) + explicit 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); + fmt::memory_buffer outbuf; + fmt::format_system_error(outbuf, last_errno, msg); + msg_ = fmt::to_string(outbuf); } const char *what() const SPDLOG_NOEXCEPT override { - return _msg.c_str(); + return msg_.c_str(); } private: - std::string _msg; + std::string msg_; }; // @@ -179,4 +165,13 @@ using filename_t = std::wstring; using filename_t = std::string; #endif +#define SPDLOG_CATCH_AND_HANDLE \ + catch (const std::exception &ex) \ + { \ + err_handler_(ex.what()); \ + } \ + catch (...) \ + { \ + err_handler_("Unknown exeption in logger"); \ + } } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/contrib/README.md b/third_party/spdlog/spdlog/include/spdlog/contrib/README.md deleted file mode 100644 index e3abc34d..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/contrib/README.md +++ /dev/null @@ -1 +0,0 @@ -Please put here your contribs. Popular contribs will be moved to main tree after stablization diff --git a/third_party/spdlog/spdlog/include/spdlog/contrib/sinks/.gitignore b/third_party/spdlog/spdlog/include/spdlog/contrib/sinks/.gitignore deleted file mode 100644 index 8b137891..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/contrib/sinks/.gitignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/third_party/spdlog/spdlog/include/spdlog/contrib/sinks/step_file_sink.h b/third_party/spdlog/spdlog/include/spdlog/contrib/sinks/step_file_sink.h deleted file mode 100644 index 7e90e0ad..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/contrib/sinks/step_file_sink.h +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once - -#include "../../details/file_helper.h" -#include "../../details/null_mutex.h" -#include "../../fmt/fmt.h" -#include "../../sinks/base_sink.h" - -#include -#include -#include -#include -#include -#include -#include - -// Example for spdlog.h -// -// Create a file logger which creates new files with a specified time step and fixed file size: -// -// std::shared_ptr step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const -// filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits::max()); std::shared_ptr -// step_logger_st(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const filename_t &tmp_ext = ".tmp", -// unsigned max_file_size = std::numeric_limits::max()); - -// Example for spdlog_impl.h -// Create a file logger that creates new files with a specified increment -// inline std::shared_ptr spdlog::step_logger_mt( -// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size) -// { -// return create(logger_name, filename_fmt, seconds, tmp_ext, max_file_size); -// } - -// inline std::shared_ptr spdlog::step_logger_st( -// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size) -// { -// return create(logger_name, filename_fmt, seconds, tmp_ext, max_file_size); -// } - -namespace spdlog { -namespace sinks { - -/* - * Default generator of step log file names. - */ -struct default_step_file_name_calculator -{ - // Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext - static std::tuple calc_filename(const filename_t &filename, const filename_t &tmp_ext) - { - std::tm tm = spdlog::details::os::localtime(); - filename_t basename, ext; - std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); - std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; - w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, tmp_ext); - return std::make_tuple(w.str(), ext); - } -}; - -/* - * Rotating file sink based on size and a specified time step - */ -template -class step_file_sink SPDLOG_FINAL : public base_sink -{ -public: - step_file_sink(filename_t base_filename, unsigned step_seconds, filename_t tmp_ext, unsigned max_size) - : _base_filename(std::move(base_filename)) - , _tmp_ext(std::move(tmp_ext)) - , _step_seconds(step_seconds) - , _max_size(max_size) - { - if (step_seconds == 0) - { - throw spdlog_ex("step_file_sink: Invalid time step in ctor"); - } - if (max_size == 0) - { - throw spdlog_ex("step_file_sink: Invalid max log size in ctor"); - } - - _tp = _next_tp(); - std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename, _tmp_ext); - - if (_tmp_ext == _ext) - { - throw spdlog_ex("step_file_sink: The temporary extension matches the specified in ctor"); - } - - _file_helper.open(_current_filename); - _current_size = _file_helper.size(); // expensive. called only once - } - - ~step_file_sink() - { - try - { - close_current_file(); - } - catch (...) - { - } - } - -protected: - void _sink_it(const details::log_msg &msg) override - { - _current_size += msg.formatted.size(); - if (std::chrono::system_clock::now() >= _tp || _current_size > _max_size) - { - close_current_file(); - - std::tie(_current_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename, _tmp_ext); - _file_helper.open(_current_filename); - _tp = _next_tp(); - _current_size = msg.formatted.size(); - } - _file_helper.write(msg); - } - - void _flush() override - { - _file_helper.flush(); - } - -private: - std::chrono::system_clock::time_point _next_tp() - { - return std::chrono::system_clock::now() + _step_seconds; - } - - void close_current_file() - { - using details::os::filename_to_str; - - filename_t src = _current_filename, target; - std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(src); - target += _ext; - - if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0) - { - throw spdlog_ex("step_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); - } - } - - const filename_t _base_filename; - const filename_t _tmp_ext; - const std::chrono::seconds _step_seconds; - const unsigned _max_size; - - std::chrono::system_clock::time_point _tp; - filename_t _current_filename; - filename_t _ext; - unsigned _current_size; - - details::file_helper _file_helper; -}; - -using step_file_sink_mt = step_file_sink; -using step_file_sink_st = step_file_sink; - -} // namespace sinks -} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/details/async_log_helper.h b/third_party/spdlog/spdlog/include/spdlog/details/async_log_helper.h deleted file mode 100644 index a927d9f2..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/details/async_log_helper.h +++ /dev/null @@ -1,342 +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. -// - -#pragma once - -#include "../common.h" -#include "../details/log_msg.h" -#include "../details/mpmc_blocking_q.h" -#include "../details/os.h" -#include "../formatter.h" -#include "../sinks/sink.h" - -#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 - { - level::level_enum level; - log_clock::time_point time; - size_t thread_id; - std::string txt; - async_msg_type msg_type; - size_t msg_id; - - async_msg() = default; - ~async_msg() = default; - - explicit async_msg(async_msg_type m_type) - : level(level::info) - , thread_id(0) - , msg_type(m_type) - , msg_id(0) - { - } - - async_msg(async_msg &&other) = default; - async_msg &operator=(async_msg &&other) = default; - - // never copy or assign. should only be moved.. - async_msg(const async_msg &) = delete; - async_msg &operator=(const async_msg &other) = delete; - - // construct from log_msg - explicit 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) - , msg_id(m.msg_id) - { - } - - // copy into log_msg - void fill_log_msg(log_msg &msg, std::string* logger_name) - { - msg.logger_name = logger_name; - msg.level = level; - msg.time = time; - msg.thread_id = thread_id; - msg.raw.clear(); - msg.raw << txt; - msg.msg_id = msg_id; - } - }; - -public: - using item_type = async_msg; - using q_type = details::mpmc_bounded_queue; - - using clock = std::chrono::steady_clock; - - async_log_helper(std::string logger_name, - formatter_ptr formatter, - std::vector sinks, - size_t queue_size, - const log_err_handler err_handler, - const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, - std::function worker_warmup_cb = nullptr, - const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), - std::function worker_teardown_cb = nullptr); - - void log(const details::log_msg &msg); - - // stop logging and join the back thread - ~async_log_helper(); - - async_log_helper(const async_log_helper &) = delete; - async_log_helper &operator=(const async_log_helper &) = delete; - - void set_formatter(formatter_ptr msg_formatter); - - void flush(); - - void set_error_handler(spdlog::log_err_handler err_handler); - -private: - std::string _logger_name; - formatter_ptr _formatter; - std::vector> _sinks; - - // queue of messages to log - q_type _q; - - log_err_handler _err_handler; - - std::chrono::time_point _last_flush; - - // 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; - - std::mutex null_mutex_; - // null_mutex null_mutex_; - std::condition_variable_any not_empty_cv_; - std::condition_variable_any not_full_cv_; - - // worker thread - std::thread _worker_thread; - - void enqueue_msg(async_msg &&new_msg, async_overflow_policy policy); - - // worker thread main loop - void worker_loop(); - - // dequeue next message from the queue and process it. - // return false if termination of the queue is required - bool process_next_msg(); - - void handle_flush_interval(); - - void flush_sinks(); -}; -} // namespace details -} // namespace spdlog - -/////////////////////////////////////////////////////////////////////////////// -// async_sink class implementation -/////////////////////////////////////////////////////////////////////////////// -inline spdlog::details::async_log_helper::async_log_helper(std::string logger_name, - formatter_ptr formatter, - std::vector sinks, - size_t queue_size, - log_err_handler err_handler, - const async_overflow_policy overflow_policy, - std::function worker_warmup_cb, - const std::chrono::milliseconds &flush_interval_ms, - std::function worker_teardown_cb) - : _logger_name(std::move(logger_name)) - , _formatter(std::move(formatter)) - , _sinks(std::move(sinks)) - , _q(queue_size) - , _err_handler(std::move(err_handler)) - , _last_flush(os::now()) - , _overflow_policy(overflow_policy) - , _worker_warmup_cb(std::move(worker_warmup_cb)) - , _flush_interval_ms(flush_interval_ms) - , _worker_teardown_cb(std::move(worker_teardown_cb)) -{ - _worker_thread = std::thread(&async_log_helper::worker_loop, this); -} - -// send to the worker thread terminate message, and join it. -inline spdlog::details::async_log_helper::~async_log_helper() -{ - try - { - enqueue_msg(async_msg(async_msg_type::terminate), async_overflow_policy::block_retry); - _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) -{ - enqueue_msg(async_msg(msg), _overflow_policy); -} - -inline void spdlog::details::async_log_helper::enqueue_msg(details::async_log_helper::async_msg &&new_msg, async_overflow_policy policy) -{ - - // block until succeeded pushing to the queue - if (policy == async_overflow_policy::block_retry) - { - _q.enqueue(std::move(new_msg)); - } - else - { - _q.enqueue_nowait(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() -{ - enqueue_msg(async_msg(async_msg_type::flush), _overflow_policy); -} - -inline void spdlog::details::async_log_helper::worker_loop() -{ - if (_worker_warmup_cb) - { - _worker_warmup_cb(); - } - auto active = true; - while (active) - { - try - { - active = process_next_msg(); - } - catch (const std::exception &ex) - { - _err_handler(ex.what()); - } - catch (...) - { - _err_handler("Unknown exeption in async logger worker loop."); - } - } - if (_worker_teardown_cb) - { - _worker_teardown_cb(); - } -} - -// 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() -{ - async_msg incoming_async_msg; - bool dequeued = _q.dequeue_for(incoming_async_msg, std::chrono::seconds(2)); - if (!dequeued) - { - handle_flush_interval(); - return true; - } - - switch (incoming_async_msg.msg_type) - { - case async_msg_type::flush: - flush_sinks(); - return true; - - case async_msg_type::terminate: - flush_sinks(); - return false; - - default: - log_msg incoming_log_msg; - incoming_async_msg.fill_log_msg(incoming_log_msg, &_logger_name); - _formatter->format(incoming_log_msg); - for (auto &s : _sinks) - { - if (s->should_log(incoming_log_msg.level)) - { - s->log(incoming_log_msg); - } - } - handle_flush_interval(); - return true; - } - assert(false); - return true; // should not be reached -} - -inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) -{ - _formatter = std::move(msg_formatter); -} - -inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler) -{ - _err_handler = std::move(err_handler); -} - -// flush all sinks if _flush_interval_ms has expired. -inline void spdlog::details::async_log_helper::handle_flush_interval() -{ - if (_flush_interval_ms == std::chrono::milliseconds::zero()) - { - return; - } - auto delta = details::os::now() - _last_flush; - ; - if (delta >= _flush_interval_ms) - { - flush_sinks(); - } -} - -// flush all sinks if _flush_interval_ms has expired. only called if queue is empty -inline void spdlog::details::async_log_helper::flush_sinks() -{ - for (auto &s : _sinks) - { - s->flush(); - } - _last_flush = os::now(); -} diff --git a/third_party/spdlog/spdlog/include/spdlog/details/async_logger_impl.h b/third_party/spdlog/spdlog/include/spdlog/details/async_logger_impl.h index e80b770a..47659ddc 100644 --- a/third_party/spdlog/spdlog/include/spdlog/details/async_logger_impl.h +++ b/third_party/spdlog/spdlog/include/spdlog/details/async_logger_impl.h @@ -5,91 +5,96 @@ #pragma once -// Async Logger implementation -// Use an async_sink (queue per logger) to perform the logging in a worker thread +// async logger implementation +// uses a thread pool to perform the actual logging -#include "../async_logger.h" -#include "../details/async_log_helper.h" +#include "spdlog/details/thread_pool.h" #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( - logger_name, _formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) +template +inline spdlog::async_logger::async_logger( + std::string logger_name, const It &begin, const It &end, std::weak_ptr tp, async_overflow_policy overflow_policy) + : logger(std::move(logger_name), begin, end) + , thread_pool_(tp) + , overflow_policy_(overflow_policy) { } -inline spdlog::async_logger::async_logger(const std::string &logger_name, sinks_init_list sinks_list, 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_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, - worker_teardown_cb) +inline spdlog::async_logger::async_logger( + std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, async_overflow_policy overflow_policy) + : async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), tp, overflow_policy) { } -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, {std::move(single_sink)}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) +inline spdlog::async_logger::async_logger( + std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, async_overflow_policy overflow_policy) + : async_logger(std::move(logger_name), {single_sink}, tp, overflow_policy) { } -inline void spdlog::async_logger::flush() +// send the log message to the thread pool +inline void spdlog::async_logger::sink_it_(details::log_msg &msg) { - _async_log_helper->flush(); -} - -// Error handler -inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler) -{ - _err_handler = err_handler; - _async_log_helper->set_error_handler(err_handler); -} -inline spdlog::log_err_handler spdlog::async_logger::error_handler() -{ - return _err_handler; -} - -inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) -{ - _formatter = msg_formatter; - _async_log_helper->set_formatter(_formatter); +#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) + incr_msg_counter_(msg); +#endif + if (auto pool_ptr = thread_pool_.lock()) + { + pool_ptr->post_log(shared_from_this(), std::move(msg), overflow_policy_); + } + else + { + throw spdlog_ex("async log: thread pool doesn't exist anymore"); + } } -inline void spdlog::async_logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time) +// send flush request to the thread pool +inline void spdlog::async_logger::flush_() { - _formatter = std::make_shared(pattern, pattern_time); - _async_log_helper->set_formatter(_formatter); + if (auto pool_ptr = thread_pool_.lock()) + { + pool_ptr->post_flush(shared_from_this(), overflow_policy_); + } + else + { + throw spdlog_ex("async flush: thread pool doesn't exist anymore"); + } } -inline void spdlog::async_logger::_sink_it(details::log_msg &msg) +// +// backend functions - called from the thread pool to do the actual job +// +inline void spdlog::async_logger::backend_log_(details::log_msg &incoming_log_msg) { try { -#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) - _incr_msg_counter(msg); -#endif - _async_log_helper->log(msg); - if (_should_flush_on(msg)) + for (auto &s : sinks_) { - _async_log_helper->flush(); // do async flush + if (s->should_log(incoming_log_msg.level)) + { + s->log(incoming_log_msg); + } } } - catch (const std::exception &ex) + SPDLOG_CATCH_AND_HANDLE + + if (should_flush_(incoming_log_msg)) { - _err_handler(ex.what()); + backend_flush_(); } - catch (...) +} + +inline void spdlog::async_logger::backend_flush_() +{ + try { - _err_handler("Unknown exception in logger " + _name); - throw; + for (auto &sink : sinks_) + { + sink->flush(); + } } + SPDLOG_CATCH_AND_HANDLE } diff --git a/third_party/spdlog/spdlog/include/spdlog/details/circular_q.h b/third_party/spdlog/spdlog/include/spdlog/details/circular_q.h new file mode 100644 index 00000000..b01325bb --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/details/circular_q.h @@ -0,0 +1,72 @@ +// +// Copyright(c) 2018 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +// cirucal q view of std::vector. +#pragma once + +#include + +namespace spdlog { +namespace details { +template +class circular_q +{ +public: + using item_type = T; + + explicit circular_q(size_t max_items) + : max_items_(max_items + 1) // one item is reserved as marker for full q + , v_(max_items_) + { + } + + // push back, overrun (oldest) item if no room left + void push_back(T &&item) + { + v_[tail_] = std::move(item); + tail_ = (tail_ + 1) % max_items_; + + if (tail_ == head_) // overrun last item if full + { + head_ = (head_ + 1) % max_items_; + ++overrun_counter_; + } + } + + // Pop item from front. + // If there are no elements in the container, the behavior is undefined. + void pop_front(T &popped_item) + { + popped_item = std::move(v_[head_]); + head_ = (head_ + 1) % max_items_; + } + + bool empty() + { + return tail_ == head_; + } + + bool full() + { + // head is ahead of the tail by 1 + return ((tail_ + 1) % max_items_) == head_; + } + + size_t overrun_counter() const + { + return overrun_counter_; + } + +private: + size_t max_items_; + typename std::vector::size_type head_ = 0; + typename std::vector::size_type tail_ = 0; + + std::vector v_; + + size_t overrun_counter_ = 0; +}; +} // namespace details +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/details/console_globals.h b/third_party/spdlog/spdlog/include/spdlog/details/console_globals.h new file mode 100644 index 00000000..4ac7f743 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/details/console_globals.h @@ -0,0 +1,61 @@ +#pragma once +// +// Copyright(c) 2018 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#include "spdlog/details/null_mutex.h" +#include +#include + +namespace spdlog { +namespace details { +struct console_stdout +{ + static std::FILE *stream() + { + return stdout; + } +#ifdef _WIN32 + static HANDLE handle() + { + return ::GetStdHandle(STD_OUTPUT_HANDLE); + } +#endif +}; + +struct console_stderr +{ + static std::FILE *stream() + { + return stderr; + } +#ifdef _WIN32 + static HANDLE handle() + { + return ::GetStdHandle(STD_ERROR_HANDLE); + } +#endif +}; + +struct console_mutex +{ + using mutex_t = std::mutex; + static mutex_t &mutex() + { + static mutex_t s_mutex; + return s_mutex; + } +}; + +struct console_nullmutex +{ + using mutex_t = null_mutex; + static mutex_t &mutex() + { + static mutex_t s_mutex; + return s_mutex; + } +}; +} // namespace details +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/details/file_helper.h b/third_party/spdlog/spdlog/include/spdlog/details/file_helper.h index d30a79b7..65b3560f 100644 --- a/third_party/spdlog/spdlog/include/spdlog/details/file_helper.h +++ b/third_party/spdlog/spdlog/include/spdlog/details/file_helper.h @@ -6,7 +6,8 @@ #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) +// When failing to open a file, retry several times(5) with small delay between +// the tries(10 ms) // Throw spdlog_ex exception on errors #include "../details/log_msg.h" @@ -46,7 +47,7 @@ class file_helper _filename = fname; for (int tries = 0; tries < open_tries; ++tries) { - if (!os::fopen_s(&_fd, fname, mode)) + if (!os::fopen_s(&fd_, fname, mode)) { return; } @@ -68,23 +69,23 @@ class file_helper void flush() { - std::fflush(_fd); + std::fflush(fd_); } void close() { - if (_fd != nullptr) + if (fd_ != nullptr) { - std::fclose(_fd); - _fd = nullptr; + std::fclose(fd_); + fd_ = nullptr; } } - void write(const log_msg &msg) + void write(const fmt::memory_buffer &buf) { - size_t msg_size = msg.formatted.size(); - auto data = msg.formatted.data(); - if (std::fwrite(data, 1, msg_size, _fd) != msg_size) + size_t msg_size = buf.size(); + auto data = buf.data(); + if (std::fwrite(data, 1, msg_size, fd_) != msg_size) { throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); } @@ -92,11 +93,11 @@ class file_helper size_t size() const { - if (_fd == nullptr) + if (fd_ == nullptr) { throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); } - return os::filesize(_fd); + return os::filesize(fd_); } const filename_t &filename() const @@ -126,7 +127,8 @@ class file_helper { auto ext_index = fname.rfind('.'); - // no valid extension found - return whole path and empty string as extension + // no valid extension found - return whole path and empty string as + // extension if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) { return std::make_tuple(fname, spdlog::filename_t()); @@ -134,7 +136,7 @@ class file_helper // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile" auto folder_index = fname.rfind(details::os::folder_sep); - if (folder_index != fname.npos && folder_index >= ext_index - 1) + if (folder_index != filename_t::npos && folder_index >= ext_index - 1) { return std::make_tuple(fname, spdlog::filename_t()); } @@ -144,7 +146,7 @@ class file_helper } private: - FILE *_fd{nullptr}; + FILE *fd_{nullptr}; filename_t _filename; }; } // namespace details diff --git a/third_party/spdlog/spdlog/include/spdlog/details/fmt_helper.h b/third_party/spdlog/spdlog/include/spdlog/details/fmt_helper.h new file mode 100644 index 00000000..eabf93b5 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/details/fmt_helper.h @@ -0,0 +1,130 @@ +// +// Created by gabi on 6/15/18. +// + +#pragma once + +#include "chrono" +#include "spdlog/fmt/fmt.h" + +// Some fmt helpers to efficiently format and pad ints and strings +namespace spdlog { +namespace details { +namespace fmt_helper { + +template +inline void append_str(const std::string &str, fmt::basic_memory_buffer &dest) +{ + auto *str_ptr = str.data(); + dest.append(str_ptr, str_ptr + str.size()); +} + +template +inline void append_c_str(const char *c_str, fmt::basic_memory_buffer &dest) +{ + char ch; + while ((ch = *c_str) != '\0') + { + dest.push_back(ch); + ++c_str; + } +} + +template +inline void append_buf(const fmt::basic_memory_buffer &buf, fmt::basic_memory_buffer &dest) +{ + auto *buf_ptr = buf.data(); + dest.append(buf_ptr, buf_ptr + buf.size()); +} + +template +inline void append_int(T n, fmt::basic_memory_buffer &dest) +{ + fmt::format_int i(n); + dest.append(i.data(), i.data() + i.size()); +} + +template +inline void pad2(int n, fmt::basic_memory_buffer &dest) +{ + if (n > 99) + { + append_int(n, dest); + return; + } + if (n > 9) // 10-99 + { + dest.push_back('0' + static_cast(n / 10)); + dest.push_back('0' + static_cast(n % 10)); + return; + } + if (n >= 0) // 0-9 + { + dest.push_back('0'); + dest.push_back('0' + static_cast(n)); + return; + } + // negatives (unlikely, but just in case, let fmt deal with it) + fmt::format_to(dest, "{:02}", n); +} + +template +inline void pad3(int n, fmt::basic_memory_buffer &dest) +{ + if (n > 999) + { + append_int(n, dest); + return; + } + + if (n > 99) // 100-999 + { + append_int(n / 100, dest); + pad2(n % 100, dest); + return; + } + if (n > 9) // 10-99 + { + dest.push_back('0'); + dest.push_back('0' + static_cast(n / 10)); + dest.push_back('0' + static_cast(n % 10)); + return; + } + if (n >= 0) + { + dest.push_back('0'); + dest.push_back('0'); + dest.push_back('0' + static_cast(n)); + return; + } + // negatives (unlikely, but just in case let fmt deal with it) + fmt::format_to(dest, "{:03}", n); +} + +template +inline void pad6(size_t n, fmt::basic_memory_buffer &dest) +{ + if (n > 99999) + { + append_int(n, dest); + return; + } + pad3(static_cast(n / 1000), dest); + pad3(static_cast(n % 1000), dest); +} + +// return fraction of a second of the given time_point. +// e.g. +// fraction(tp) -> will return the millis part of the second +template +inline ToDuration time_fraction(const log_clock::time_point &tp) +{ + using namespace std::chrono; + auto duration = tp.time_since_epoch(); + auto secs = duration_cast(duration); + return duration_cast(duration) - duration_cast(secs); +} + +} // namespace fmt_helper +} // namespace details +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/details/log_msg.h b/third_party/spdlog/spdlog/include/spdlog/details/log_msg.h index 1d079aaf..3272dd5d 100644 --- a/third_party/spdlog/spdlog/include/spdlog/details/log_msg.h +++ b/third_party/spdlog/spdlog/include/spdlog/details/log_msg.h @@ -5,8 +5,8 @@ #pragma once -#include "../common.h" -#include "../details/os.h" +#include "spdlog/common.h" +#include "spdlog/details/os.h" #include #include @@ -16,33 +16,33 @@ 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(); + , time(os::now()) #endif #ifndef SPDLOG_NO_THREAD_ID - thread_id = os::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; + log_msg &operator=(log_msg &&other) = delete; const std::string *logger_name{nullptr}; level::level_enum level; log_clock::time_point time; size_t thread_id; - fmt::MemoryWriter raw; - fmt::MemoryWriter formatted; + fmt::memory_buffer raw; size_t msg_id{0}; - // wrap this range with color codes - size_t color_range_start{0}; - size_t color_range_end{0}; + // info about wrapping the formatted text with color + mutable size_t color_range_start{0}; + mutable size_t color_range_end{0}; }; } // namespace details } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/details/logger_impl.h b/third_party/spdlog/spdlog/include/spdlog/details/logger_impl.h index 213fdeda..0a7c5b6b 100644 --- a/third_party/spdlog/spdlog/include/spdlog/details/logger_impl.h +++ b/third_party/spdlog/spdlog/include/spdlog/details/logger_impl.h @@ -5,48 +5,49 @@ #pragma once -#include "../logger.h" - #include #include // create logger with given name, sinks and the default pattern formatter // all other ctors will call this one -template +template inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end) - : _name(std::move(logger_name)) - , _sinks(begin, end) - , _formatter(std::make_shared("%+")) - , _level(level::info) - , _flush_level(level::off) - , _last_err_time(0) - , _msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages + : name_(std::move(logger_name)) + , sinks_(begin, end) + , level_(level::info) + , flush_level_(level::off) + , last_err_time_(0) + , msg_counter_(1) // message counter will start from 1. 0-message id will be + // reserved for controll messages { - _err_handler = [this](const std::string &msg) { this->_default_err_handler(msg); }; + 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()) +inline spdlog::logger::logger(std::string logger_name, sinks_init_list sinks_list) + : logger(std::move(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, {std::move(single_sink)}) +inline spdlog::logger::logger(std::string logger_name, spdlog::sink_ptr single_sink) + : logger(std::move(logger_name), {std::move(single_sink)}) { } inline spdlog::logger::~logger() = default; -inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter) +inline void spdlog::logger::set_formatter(std::unique_ptr f) { - _set_formatter(std::move(msg_formatter)); + for (auto &sink : sinks_) + { + sink->set_formatter(f->clone()); + } } -inline void spdlog::logger::set_pattern(const std::string &pattern, pattern_time_type pattern_time) +inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type) { - _set_pattern(pattern, pattern_time); + set_formatter(std::unique_ptr(new pattern_formatter(std::move(pattern), time_type))); } template @@ -59,24 +60,11 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Ar try { - details::log_msg log_msg(&_name, lvl); - -#if defined(SPDLOG_FMT_PRINTF) - fmt::printf(log_msg.raw, fmt, args...); -#else - log_msg.raw.write(fmt, args...); -#endif - _sink_it(log_msg); - } - catch (const std::exception &ex) - { - _err_handler(ex.what()); - } - catch (...) - { - _err_handler("Unknown exception in logger " + _name); - throw; + details::log_msg log_msg(&name_, lvl); + fmt::format_to(log_msg.raw, fmt, args...); + sink_it_(log_msg); } + SPDLOG_CATCH_AND_HANDLE } template @@ -88,19 +76,11 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *msg) } 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 in logger " + _name); - throw; + details::log_msg log_msg(&name_, lvl); + fmt::format_to(log_msg.raw, "{}", msg); + sink_it_(log_msg); } + SPDLOG_CATCH_AND_HANDLE } template @@ -112,55 +92,47 @@ inline void spdlog::logger::log(level::level_enum lvl, const T &msg) } 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 in logger " + _name); - throw; + details::log_msg log_msg(&name_, lvl); + fmt::format_to(log_msg.raw, "{}", msg); + sink_it_(log_msg); } + SPDLOG_CATCH_AND_HANDLE } -template -inline void spdlog::logger::trace(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::trace(const char *fmt, const Args &... args) { - log(level::trace, fmt, arg1, args...); + log(level::trace, fmt, args...); } -template -inline void spdlog::logger::debug(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::debug(const char *fmt, const Args &... args) { - log(level::debug, fmt, arg1, args...); + log(level::debug, fmt, args...); } -template -inline void spdlog::logger::info(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::info(const char *fmt, const Args &... args) { - log(level::info, fmt, arg1, args...); + log(level::info, fmt, args...); } -template -inline void spdlog::logger::warn(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::warn(const char *fmt, const Args &... args) { - log(level::warn, fmt, arg1, args...); + log(level::warn, fmt, args...); } -template -inline void spdlog::logger::error(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::error(const char *fmt, const Args &... args) { - log(level::err, fmt, arg1, args...); + log(level::err, fmt, args...); } -template -inline void spdlog::logger::critical(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::critical(const char *fmt, const Args &... args) { - log(level::critical, fmt, arg1, args...); + log(level::critical, fmt, args...); } template @@ -200,24 +172,25 @@ inline void spdlog::logger::critical(const T &msg) } #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT -#include -#include - -template -inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *msg) -{ - std::wstring_convert> conv; - - log(lvl, conv.to_bytes(msg)); -} - template inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) { - fmt::WMemoryWriter wWriter; + if (!should_log(lvl)) + { + return; + } + + decltype(wstring_converter_)::byte_string utf8_string; - wWriter.write(fmt, args...); - log(lvl, wWriter.c_str()); + try + { + { + std::lock_guard lock(wstring_converter_mutex_); + utf8_string = wstring_converter_.to_bytes(fmt); + } + log(lvl, utf8_string.c_str(), args...); + } + SPDLOG_CATCH_AND_HANDLE } template @@ -263,49 +236,64 @@ inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args) // inline const std::string &spdlog::logger::name() const { - return _name; + return name_; } inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) { - _level.store(log_level); + level_.store(log_level); } inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) { - _err_handler = std::move(err_handler); + err_handler_ = std::move(err_handler); } inline spdlog::log_err_handler spdlog::logger::error_handler() { - return _err_handler; + return err_handler_; +} + +inline void spdlog::logger::flush() +{ + try + { + flush_(); + } + SPDLOG_CATCH_AND_HANDLE } inline void spdlog::logger::flush_on(level::level_enum log_level) { - _flush_level.store(log_level); + flush_level_.store(log_level); +} + +inline bool spdlog::logger::should_flush_(const details::log_msg &msg) +{ + auto flush_level = flush_level_.load(std::memory_order_relaxed); + return (msg.level >= flush_level) && (msg.level != level::off); } inline spdlog::level::level_enum spdlog::logger::level() const { - return static_cast(_level.load(std::memory_order_relaxed)); + 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); + 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 +// 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) +inline void spdlog::logger::sink_it_(details::log_msg &msg) { #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) - _incr_msg_counter(msg); + incr_msg_counter_(msg); #endif - _formatter->format(msg); - for (auto &sink : _sinks) + for (auto &sink : sinks_) { if (sink->should_log(msg.level)) { @@ -313,56 +301,45 @@ inline void spdlog::logger::_sink_it(details::log_msg &msg) } } - if (_should_flush_on(msg)) + if (should_flush_(msg)) { flush(); } } -inline void spdlog::logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time) -{ - _formatter = std::make_shared(pattern, pattern_time); -} - -inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) -{ - _formatter = std::move(msg_formatter); -} - -inline void spdlog::logger::flush() +inline void spdlog::logger::flush_() { - for (auto &sink : _sinks) + for (auto &sink : sinks_) { sink->flush(); } } -inline void spdlog::logger::_default_err_handler(const std::string &msg) +inline void spdlog::logger::default_err_handler_(const std::string &msg) { auto now = time(nullptr); - if (now - _last_err_time < 60) + if (now - last_err_time_ < 60) { return; } - _last_err_time = now; + last_err_time_ = now; 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); fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg); } -inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg) +inline void spdlog::logger::incr_msg_counter_(details::log_msg &msg) { - const auto flush_level = _flush_level.load(std::memory_order_relaxed); - return (msg.level >= flush_level) && (msg.level != level::off); + msg.msg_id = msg_counter_.fetch_add(1, std::memory_order_relaxed); } -inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg) +inline const std::vector &spdlog::logger::sinks() const { - msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed); + return sinks_; } -inline const std::vector &spdlog::logger::sinks() const +inline std::vector &spdlog::logger::sinks() { - return _sinks; + return sinks_; } diff --git a/third_party/spdlog/spdlog/include/spdlog/details/mpmc_blocking_q.h b/third_party/spdlog/spdlog/include/spdlog/details/mpmc_blocking_q.h index d4a8a41c..ca789fc6 100644 --- a/third_party/spdlog/spdlog/include/spdlog/details/mpmc_blocking_q.h +++ b/third_party/spdlog/spdlog/include/spdlog/details/mpmc_blocking_q.h @@ -5,53 +5,51 @@ // Distributed under the MIT License (http://opensource.org/licenses/MIT) // -// async log helper : -// multi producer-multi consumer blocking queue -// enqueue(..) - will block until room found to put the new message -// enqueue_nowait(..) - will return immediatly with false if no room left in the queue -// dequeue_for(..) - will block until the queue is not empty or timeout passed +// multi producer-multi consumer blocking queue. +// enqueue(..) - will block until room found to put the new message. +// enqueue_nowait(..) - will return immediately with false if no room left in +// the queue. +// dequeue_for(..) - will block until the queue is not empty or timeout have +// passed. + +#include "spdlog/details/circular_q.h" #include #include -#include namespace spdlog { namespace details { template -class mpmc_bounded_queue +class mpmc_blocking_queue { public: using item_type = T; - explicit mpmc_bounded_queue(size_t max_items) - : max_items_(max_items) + explicit mpmc_blocking_queue(size_t max_items) + : q_(max_items) { } +#ifndef __MINGW32__ // try to enqueue and block if no room left void enqueue(T &&item) { { std::unique_lock lock(queue_mutex_); - pop_cv_.wait(lock, [this] { return this->q_.size() < this->max_items_; }); - q_.push(std::move(item)); + pop_cv_.wait(lock, [this] { return !this->q_.full(); }); + q_.push_back(std::move(item)); } push_cv_.notify_one(); } - // try to enqueue and return immdeialty false if no room left - bool enqueue_nowait(T &&item) + // enqueue immediately. overrun oldest message in the queue if no room left. + void enqueue_nowait(T &&item) { { std::unique_lock lock(queue_mutex_); - if (q_.size() == this->max_items_) - { - return false; - } - q_.push(std::forward(item)); + q_.push_back(std::move(item)); } push_cv_.notify_one(); - return true; } // try to dequeue item. if no item found. wait upto timeout and try again @@ -60,25 +58,64 @@ class mpmc_bounded_queue { { std::unique_lock lock(queue_mutex_); - if (!push_cv_.wait_for(lock, wait_duration, [this] { return this->q_.size() > 0; })) + if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) { return false; } + q_.pop_front(popped_item); + } + pop_cv_.notify_one(); + return true; + } + +#else + // apparently mingw deadlocks if the mutex is released before cv.notify_one(), + // so release the mutex at the very end each function. + + // try to enqueue and block if no room left + void enqueue(T &&item) + { + std::unique_lock lock(queue_mutex_); + pop_cv_.wait(lock, [this] { return !this->q_.full(); }); + q_.push_back(std::move(item)); + push_cv_.notify_one(); + } + + // enqueue immediately. overrun oldest message in the queue if no room left. + void enqueue_nowait(T &&item) + { + std::unique_lock lock(queue_mutex_); + q_.push_back(std::move(item)); + push_cv_.notify_one(); + } - popped_item = std::move(q_.front()); - q_.pop(); + // try to dequeue item. if no item found. wait upto timeout and try again + // Return true, if succeeded dequeue item, false otherwise + bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) + { + std::unique_lock lock(queue_mutex_); + if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) + { + return false; } + q_.pop_front(popped_item); pop_cv_.notify_one(); return true; } +#endif + + size_t overrun_counter() + { + std::unique_lock lock(queue_mutex_); + return q_.overrun_counter(); + } + private: - size_t max_items_; std::mutex queue_mutex_; std::condition_variable push_cv_; std::condition_variable pop_cv_; - - std::queue q_; + spdlog::details::circular_q q_; }; } // namespace details } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/details/os.h b/third_party/spdlog/spdlog/include/spdlog/details/os.h index e48f8272..d7f7430f 100644 --- a/third_party/spdlog/spdlog/include/spdlog/details/os.h +++ b/third_party/spdlog/spdlog/include/spdlog/details/os.h @@ -236,7 +236,7 @@ inline size_t filesize(FILE *f) #else // unix int fd = fileno(f); - // 64 bits(but not in osx or cygwin, where fstat64 is deprecated) +// 64 bits(but not in osx or cygwin, where fstat64 is deprecated) #if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__) struct stat64 st; if (fstat64(fd, &st) == 0) @@ -282,7 +282,7 @@ inline int utc_minutes_offset(const std::tm &tm = details::os::localtime()) return offset; #else -#if defined(sun) || defined(__sun) +#if defined(sun) || defined(__sun) || defined(_AIX) // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris struct helper { @@ -321,7 +321,8 @@ inline int utc_minutes_offset(const std::tm &tm = details::os::localtime()) } // Return current thread id as size_t -// It exists because the std::this_thread::get_id() is much slower(especially under VS 2013) +// It exists because the std::this_thread::get_id() is much slower(especially +// under VS 2013) inline size_t _thread_id() { #ifdef _WIN32 @@ -383,54 +384,6 @@ inline std::string filename_to_str(const filename_t &filename) } #endif -inline std::string errno_to_string(char[256], char *res) -{ - return std::string(res); -} - -inline std::string errno_to_string(char buf[256], int res) -{ - if (res == 0) - { - return std::string(buf); - } - return "Unknown error"; -} - -// 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 "Unknown error"; - } - -#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \ - ((_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE)) // posix version - - if (strerror_r(err_num, buf, buf_size) == 0) - { - return std::string(buf); - } - else - { - return "Unknown error"; - } - -#else // gnu version (might not use the given buf, so its retval pointer must be used) - auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type - return errno_to_string(buf, err); // use overloading to select correct stringify function -#endif -} - inline int pid() { diff --git a/third_party/spdlog/spdlog/include/spdlog/details/pattern_formatter.h b/third_party/spdlog/spdlog/include/spdlog/details/pattern_formatter.h new file mode 100644 index 00000000..dd352ae5 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/details/pattern_formatter.h @@ -0,0 +1,772 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "spdlog/details/fmt_helper.h" +#include "spdlog/details/log_msg.h" +#include "spdlog/details/os.h" +#include "spdlog/fmt/fmt.h" +#include "spdlog/formatter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spdlog { +namespace details { + +class flag_formatter +{ +public: + virtual ~flag_formatter() = default; + virtual void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) = 0; +}; + +/////////////////////////////////////////////////////////////////////// +// name & level pattern appenders +/////////////////////////////////////////////////////////////////////// +class name_formatter : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::append_str(*msg.logger_name, dest); + } +}; + +// log level appender +class level_formatter : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::append_c_str(level::to_c_str(msg.level), dest); + } +}; + +// short log level appender +class short_level_formatter : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::append_c_str(level::to_short_c_str(msg.level), dest); + } +}; + +/////////////////////////////////////////////////////////////////////// +// 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 char *days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +class a_formatter : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::append_c_str(days[tm_time.tm_wday], dest); + } +}; + +// Full weekday name +static const char *full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; +class A_formatter : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::append_c_str(full_days[tm_time.tm_wday], dest); + } +}; + +// Abbreviated month +static const char *months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}; +class b_formatter : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::append_c_str(months[tm_time.tm_mon], dest); + } +}; + +// Full month name +static const char *full_months[]{ + "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; +class B_formatter : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::append_c_str(full_months[tm_time.tm_mon], dest); + } +}; + +// Date and time representation (Thu Aug 23 15:35:46 2014) +class c_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + // fmt::format_to(dest, "{} {} {} ", days[tm_time.tm_wday], + // months[tm_time.tm_mon], tm_time.tm_mday); + // date + fmt_helper::append_str(days[tm_time.tm_wday], dest); + dest.push_back(' '); + fmt_helper::append_str(months[tm_time.tm_mon], dest); + dest.push_back(' '); + fmt_helper::append_int(tm_time.tm_mday, dest); + dest.push_back(' '); + // time + + fmt_helper::pad2(tm_time.tm_hour, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_sec, dest); + dest.push_back(' '); + fmt_helper::append_int(tm_time.tm_year + 1900, dest); + } +}; + +// year - 2 digit +class C_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(tm_time.tm_year % 100, dest); + } +}; + +// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 +class D_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(tm_time.tm_mon + 1, dest); + dest.push_back('/'); + fmt_helper::pad2(tm_time.tm_mday, dest); + dest.push_back('/'); + fmt_helper::pad2(tm_time.tm_year % 100, dest); + } +}; + +// year - 4 digit +class Y_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::append_int(tm_time.tm_year + 1900, dest); + } +}; + +// month 1-12 +class m_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(tm_time.tm_mon + 1, dest); + } +}; + +// day of month 1-31 +class d_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(tm_time.tm_mday, dest); + } +}; + +// hours in 24 format 0-23 +class H_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(tm_time.tm_hour, dest); + } +}; + +// hours in 12 format 1-12 +class I_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(to12h(tm_time), dest); + } +}; + +// minutes 0-59 +class M_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(tm_time.tm_min, dest); + } +}; + +// seconds 0-59 +class S_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(tm_time.tm_sec, dest); + } +}; + +// milliseconds +class e_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto millis = fmt_helper::time_fraction(msg.time); + fmt_helper::pad3(static_cast(millis.count()), dest); + } +}; + +// microseconds +class f_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto micros = fmt_helper::time_fraction(msg.time); + fmt_helper::pad6(static_cast(micros.count()), dest); + } +}; + +// nanoseconds +class F_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto ns = fmt_helper::time_fraction(msg.time); + fmt::format_to(dest, "{:09}", ns.count()); + } +}; + +// seconds since epoch +class E_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto duration = msg.time.time_since_epoch(); + auto seconds = std::chrono::duration_cast(duration).count(); + fmt_helper::append_int(seconds, dest); + } +}; + +// AM/PM +class p_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::append_c_str(ampm(tm_time), dest); + } +}; + +// 12 hour clock 02:55:02 pm +class r_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(to12h(tm_time), dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_sec, dest); + dest.push_back(' '); + fmt_helper::append_c_str(ampm(tm_time), dest); + } +}; + +// 24-hour HH:MM time, equivalent to %H:%M +class R_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + fmt_helper::pad2(tm_time.tm_hour, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + } +}; + +// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S +class T_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + // fmt::format_to(dest, "{:02}:{:02}:{:02}", tm_time.tm_hour, + // tm_time.tm_min, tm_time.tm_sec); + fmt_helper::pad2(tm_time.tm_hour, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_sec, dest); + } +}; + +// ISO 8601 offset from UTC in timezone (+-HH:MM) +class z_formatter SPDLOG_FINAL : public flag_formatter +{ +public: + const std::chrono::seconds cache_refresh = std::chrono::seconds(5); + + z_formatter() = default; + z_formatter(const z_formatter &) = delete; + z_formatter &operator=(const z_formatter &) = delete; + + void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) 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) + (void)(msg); + int total_minutes = os::utc_minutes_offset(tm_time); +#endif + bool is_negative = total_minutes < 0; + if (is_negative) + { + total_minutes = -total_minutes; + dest.push_back('-'); + } + else + { + dest.push_back('+'); + } + + fmt_helper::pad2(total_minutes / 60, dest); // hours + dest.push_back(':'); + fmt_helper::pad2(total_minutes % 60, dest); // minutes + } + +private: + log_clock::time_point last_update_{std::chrono::seconds(0)}; +#ifdef _WIN32 + int offset_minutes_{0}; + + int get_cached_offset(const log_msg &msg, const std::tm &tm_time) + { + if (msg.time - last_update_ >= cache_refresh) + { + offset_minutes_ = os::utc_minutes_offset(tm_time); + last_update_ = msg.time; + } + return offset_minutes_; + } +#endif +}; + +// Thread id +class t_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::pad6(msg.thread_id, dest); + } +}; + +// Current pid +class pid_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::append_int(details::os::pid(), dest); + } +}; + +// message counter formatter +class i_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::pad6(msg.msg_id, dest); + } +}; + +class v_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::append_buf(msg.raw, dest); + } +}; + +class ch_formatter SPDLOG_FINAL : public flag_formatter +{ +public: + explicit ch_formatter(char ch) + : ch_(ch) + { + } + void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override + { + dest.push_back(ch_); + } + +private: + char ch_; +}; + +// aggregate user chars to display as is +class aggregate_formatter SPDLOG_FINAL : public flag_formatter +{ +public: + aggregate_formatter() = default; + + void add_ch(char ch) + { + str_ += ch; + } + void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::append_str(str_, dest); + } + +private: + std::string str_; +}; + +// mark the color range. expect it to be in the form of "%^colored text%$" +class color_start_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + msg.color_range_start = dest.size(); + } +}; +class color_stop_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + msg.color_range_end = dest.size(); + } +}; + +// Full info formatter +// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v +class full_formatter SPDLOG_FINAL : public flag_formatter +{ + void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + using namespace std::chrono; +#ifndef SPDLOG_NO_DATETIME + + // cache the date/time part for the next second. + auto duration = msg.time.time_since_epoch(); + auto secs = duration_cast(duration); + + if (cache_timestamp_ != secs || cached_datetime_.size() == 0) + { + cached_datetime_.resize(0); + cached_datetime_.push_back('['); + fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_); + cached_datetime_.push_back('-'); + + fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_); + cached_datetime_.push_back('-'); + + fmt_helper::pad2(tm_time.tm_mday, cached_datetime_); + cached_datetime_.push_back(' '); + + fmt_helper::pad2(tm_time.tm_hour, cached_datetime_); + cached_datetime_.push_back(':'); + + fmt_helper::pad2(tm_time.tm_min, cached_datetime_); + cached_datetime_.push_back(':'); + + fmt_helper::pad2(tm_time.tm_sec, cached_datetime_); + cached_datetime_.push_back('.'); + + cache_timestamp_ = secs; + } + fmt_helper::append_buf(cached_datetime_, dest); + + auto millis = fmt_helper::time_fraction(msg.time); + fmt_helper::pad3(static_cast(millis.count()), dest); + dest.push_back(']'); + dest.push_back(' '); + +#else // no datetime needed + (void)tm_time; +#endif + +#ifndef SPDLOG_NO_NAME + dest.push_back('['); + fmt_helper::append_str(*msg.logger_name, dest); + dest.push_back(']'); + dest.push_back(' '); +#endif + + dest.push_back('['); + // wrap the level name with color + msg.color_range_start = dest.size(); + fmt_helper::append_c_str(level::to_c_str(msg.level), dest); + msg.color_range_end = dest.size(); + dest.push_back(']'); + dest.push_back(' '); + fmt_helper::append_buf(msg.raw, dest); + } + +private: + std::chrono::seconds cache_timestamp_{0}; + fmt::basic_memory_buffer cached_datetime_; +}; + +} // namespace details + +class pattern_formatter SPDLOG_FINAL : public formatter +{ +public: + explicit pattern_formatter( + std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol) + : pattern_(std::move(pattern)) + , eol_(std::move(eol)) + , pattern_time_type_(time_type) + , last_log_secs_(0) + { + std::memset(&cached_tm_, 0, sizeof(cached_tm_)); + compile_pattern_(pattern_); + } + + pattern_formatter(const pattern_formatter &other) = delete; + pattern_formatter &operator=(const pattern_formatter &other) = delete; + + std::unique_ptr clone() const override + { + return std::unique_ptr(new pattern_formatter(pattern_, pattern_time_type_, eol_)); + } + + void format(const details::log_msg &msg, fmt::memory_buffer &dest) override + { +#ifndef SPDLOG_NO_DATETIME + auto secs = std::chrono::duration_cast(msg.time.time_since_epoch()); + if (secs != last_log_secs_) + { + cached_tm_ = get_time_(msg); + last_log_secs_ = secs; + } +#endif + for (auto &f : formatters_) + { + f->format(msg, cached_tm_, dest); + } + // write eol + details::fmt_helper::append_str(eol_, dest); + } + +private: + std::string pattern_; + std::string eol_; + pattern_time_type pattern_time_type_; + std::tm cached_tm_; + std::chrono::seconds last_log_secs_; + + std::vector> formatters_; + + std::tm get_time_(const details::log_msg &msg) + { + if (pattern_time_type_ == pattern_time_type::local) + { + return details::os::localtime(log_clock::to_time_t(msg.time)); + } + return details::os::gmtime(log_clock::to_time_t(msg.time)); + } + + void handle_flag_(char flag) + { + using flag_formatter_ptr = std::unique_ptr; + switch (flag) + { + // logger name + case 'n': + formatters_.push_back(flag_formatter_ptr(new details::name_formatter())); + break; + + case 'l': + formatters_.push_back(flag_formatter_ptr(new details::level_formatter())); + break; + + case 'L': + formatters_.push_back(flag_formatter_ptr(new details::short_level_formatter())); + break; + + case ('t'): + formatters_.push_back(flag_formatter_ptr(new details::t_formatter())); + break; + + case ('v'): + formatters_.push_back(flag_formatter_ptr(new details::v_formatter())); + break; + + case ('a'): + formatters_.push_back(flag_formatter_ptr(new details::a_formatter())); + break; + + case ('A'): + formatters_.push_back(flag_formatter_ptr(new details::A_formatter())); + break; + + case ('b'): + case ('h'): + formatters_.push_back(flag_formatter_ptr(new details::b_formatter())); + break; + + case ('B'): + formatters_.push_back(flag_formatter_ptr(new details::B_formatter())); + break; + case ('c'): + formatters_.push_back(flag_formatter_ptr(new details::c_formatter())); + break; + + case ('C'): + formatters_.push_back(flag_formatter_ptr(new details::C_formatter())); + break; + + case ('Y'): + formatters_.push_back(flag_formatter_ptr(new details::Y_formatter())); + break; + + case ('D'): + case ('x'): + formatters_.push_back(flag_formatter_ptr(new details::D_formatter())); + break; + + case ('m'): + formatters_.push_back(flag_formatter_ptr(new details::m_formatter())); + break; + + case ('d'): + formatters_.push_back(flag_formatter_ptr(new details::d_formatter())); + break; + + case ('H'): + formatters_.push_back(flag_formatter_ptr(new details::H_formatter())); + break; + + case ('I'): + formatters_.push_back(flag_formatter_ptr(new details::I_formatter())); + break; + + case ('M'): + formatters_.push_back(flag_formatter_ptr(new details::M_formatter())); + break; + + case ('S'): + formatters_.push_back(flag_formatter_ptr(new details::S_formatter())); + break; + + case ('e'): + formatters_.push_back(flag_formatter_ptr(new details::e_formatter())); + break; + + case ('f'): + formatters_.push_back(flag_formatter_ptr(new details::f_formatter())); + break; + case ('F'): + formatters_.push_back(flag_formatter_ptr(new details::F_formatter())); + break; + + case ('E'): + formatters_.push_back(flag_formatter_ptr(new details::E_formatter())); + break; + + case ('p'): + formatters_.push_back(flag_formatter_ptr(new details::p_formatter())); + break; + + case ('r'): + formatters_.push_back(flag_formatter_ptr(new details::r_formatter())); + break; + + case ('R'): + formatters_.push_back(flag_formatter_ptr(new details::R_formatter())); + break; + + case ('T'): + case ('X'): + formatters_.push_back(flag_formatter_ptr(new details::T_formatter())); + break; + + case ('z'): + formatters_.push_back(flag_formatter_ptr(new details::z_formatter())); + break; + + case ('+'): + formatters_.push_back(flag_formatter_ptr(new details::full_formatter())); + break; + + case ('P'): + formatters_.push_back(flag_formatter_ptr(new details::pid_formatter())); + break; + + case ('i'): + formatters_.push_back(flag_formatter_ptr(new details::i_formatter())); + break; + + case ('^'): + formatters_.push_back(flag_formatter_ptr(new details::color_start_formatter())); + break; + + case ('$'): + formatters_.push_back(flag_formatter_ptr(new details::color_stop_formatter())); + break; + + default: // Unknown flag appears as is + formatters_.push_back(flag_formatter_ptr(new details::ch_formatter('%'))); + formatters_.push_back(flag_formatter_ptr(new details::ch_formatter(flag))); + break; + } + } + + void compile_pattern_(const std::string &pattern) + { + auto end = pattern.end(); + std::unique_ptr user_chars; + formatters_.clear(); + 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)); + } + } +}; +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/details/pattern_formatter_impl.h b/third_party/spdlog/spdlog/include/spdlog/details/pattern_formatter_impl.h deleted file mode 100644 index e3d70878..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/details/pattern_formatter_impl.h +++ /dev/null @@ -1,707 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include "../details/log_msg.h" -#include "../details/os.h" -#include "../fmt/fmt.h" -#include "../formatter.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace spdlog { -namespace details { -class flag_formatter -{ -public: - virtual ~flag_formatter() = default; - virtual void format(details::log_msg &msg, const std::tm &tm_time) = 0; -}; - -/////////////////////////////////////////////////////////////////////// -// name & level pattern appenders -/////////////////////////////////////////////////////////////////////// -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", "Jun", "Jul", "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 separated 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 separated 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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'); - } -}; - -class E_formatter SPDLOG_FINAL : public flag_formatter -{ - void format(details::log_msg &msg, const std::tm &) override - { - auto duration = msg.time.time_since_epoch(); - auto seconds = std::chrono::duration_cast(duration).count(); - msg.formatted << seconds; - } -}; - -// AM/PM -class p_formatter SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : public flag_formatter -{ -public: - const std::chrono::seconds cache_refresh = std::chrono::seconds(5); - - z_formatter() = default; - 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{std::chrono::seconds(0)}; - int _offset_minutes{0}; - std::mutex _mutex; - - int get_cached_offset(const log_msg &msg, const std::tm &tm_time) - { - 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 SPDLOG_FINAL : public flag_formatter -{ - void format(details::log_msg &msg, const std::tm &) override - { - msg.formatted << msg.thread_id; - } -}; - -// Current pid -class pid_formatter SPDLOG_FINAL : public flag_formatter -{ - void format(details::log_msg &msg, const std::tm &) override - { - msg.formatted << details::os::pid(); - } -}; - -// message counter formatter -class i_formatter SPDLOG_FINAL : public flag_formatter -{ - void format(details::log_msg &msg, const std::tm &) override - { - msg.formatted << fmt::pad(msg.msg_id, 6, '0'); - } -}; - -class v_formatter SPDLOG_FINAL : 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 SPDLOG_FINAL : 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 SPDLOG_FINAL : public flag_formatter -{ -public: - aggregate_formatter() = default; - - void add_ch(char ch) - { - _str += ch; - } - void format(details::log_msg &msg, const std::tm &) override - { - msg.formatted << _str; - } - -private: - std::string _str; -}; - -// mark the color range. expect it to be in the form of "%^colored text%$" -class color_start_formatter SPDLOG_FINAL : public flag_formatter -{ - void format(details::log_msg &msg, const std::tm &) override - { - msg.color_range_start = msg.formatted.size(); - } -}; -class color_stop_formatter SPDLOG_FINAL : public flag_formatter -{ - void format(details::log_msg &msg, const std::tm &) override - { - msg.color_range_end = msg.formatted.size(); - } -}; - -// Full info formatter -// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v -class full_formatter SPDLOG_FINAL : 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 << '['; - // wrap the level name with color - msg.color_range_start = msg.formatted.size(); - msg.formatted << level::to_str(msg.level); - msg.color_range_end = msg.formatted.size(); - msg.formatted << "] " << fmt::StringRef(msg.raw.data(), msg.raw.size()); - } -}; - -} // namespace details -} // namespace spdlog -/////////////////////////////////////////////////////////////////////////////// -// pattern_formatter inline impl -/////////////////////////////////////////////////////////////////////////////// -inline spdlog::pattern_formatter::pattern_formatter(const std::string &pattern, pattern_time_type pattern_time, std::string eol) - : _eol(std::move(eol)) - , _pattern_time(pattern_time) -{ - 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( - 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.emplace_back(new details::name_formatter()); - break; - - case 'l': - _formatters.emplace_back(new details::level_formatter()); - break; - - case 'L': - _formatters.emplace_back(new details::short_level_formatter()); - break; - - case ('t'): - _formatters.emplace_back(new details::t_formatter()); - break; - - case ('v'): - _formatters.emplace_back(new details::v_formatter()); - break; - - case ('a'): - _formatters.emplace_back(new details::a_formatter()); - break; - - case ('A'): - _formatters.emplace_back(new details::A_formatter()); - break; - - case ('b'): - case ('h'): - _formatters.emplace_back(new details::b_formatter()); - break; - - case ('B'): - _formatters.emplace_back(new details::B_formatter()); - break; - case ('c'): - _formatters.emplace_back(new details::c_formatter()); - break; - - case ('C'): - _formatters.emplace_back(new details::C_formatter()); - break; - - case ('Y'): - _formatters.emplace_back(new details::Y_formatter()); - break; - - case ('D'): - case ('x'): - - _formatters.emplace_back(new details::D_formatter()); - break; - - case ('m'): - _formatters.emplace_back(new details::m_formatter()); - break; - - case ('d'): - _formatters.emplace_back(new details::d_formatter()); - break; - - case ('H'): - _formatters.emplace_back(new details::H_formatter()); - break; - - case ('I'): - _formatters.emplace_back(new details::I_formatter()); - break; - - case ('M'): - _formatters.emplace_back(new details::M_formatter()); - break; - - case ('S'): - _formatters.emplace_back(new details::S_formatter()); - break; - - case ('e'): - _formatters.emplace_back(new details::e_formatter()); - break; - - case ('f'): - _formatters.emplace_back(new details::f_formatter()); - break; - case ('F'): - _formatters.emplace_back(new details::F_formatter()); - break; - - case ('E'): - _formatters.emplace_back(new details::E_formatter()); - break; - - case ('p'): - _formatters.emplace_back(new details::p_formatter()); - break; - - case ('r'): - _formatters.emplace_back(new details::r_formatter()); - break; - - case ('R'): - _formatters.emplace_back(new details::R_formatter()); - break; - - case ('T'): - case ('X'): - _formatters.emplace_back(new details::T_formatter()); - break; - - case ('z'): - _formatters.emplace_back(new details::z_formatter()); - break; - - case ('+'): - _formatters.emplace_back(new details::full_formatter()); - break; - - case ('P'): - _formatters.emplace_back(new details::pid_formatter()); - break; - - case ('i'): - _formatters.emplace_back(new details::i_formatter()); - break; - - case ('^'): - _formatters.emplace_back(new details::color_start_formatter()); - break; - - case ('$'): - _formatters.emplace_back(new details::color_stop_formatter()); - break; - - default: // Unknown flag appears as is - _formatters.emplace_back(new details::ch_formatter('%')); - _formatters.emplace_back(new details::ch_formatter(flag)); - break; - } -} - -inline std::tm spdlog::pattern_formatter::get_time(details::log_msg &msg) -{ - if (_pattern_time == pattern_time_type::local) - { - return details::os::localtime(log_clock::to_time_t(msg.time)); - } - return details::os::gmtime(log_clock::to_time_t(msg.time)); -} - -inline void spdlog::pattern_formatter::format(details::log_msg &msg) -{ - -#ifndef SPDLOG_NO_DATETIME - auto tm_time = get_time(msg); -#else - std::tm tm_time; -#endif - for (auto &f : _formatters) - { - f->format(msg, tm_time); - } - // write eol - msg.formatted << _eol; -} diff --git a/third_party/spdlog/spdlog/include/spdlog/details/periodic_worker.h b/third_party/spdlog/spdlog/include/spdlog/details/periodic_worker.h new file mode 100644 index 00000000..57e5fa77 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/details/periodic_worker.h @@ -0,0 +1,71 @@ + +// +// Copyright(c) 2018 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// periodic worker thread - periodically executes the given callback function. +// +// RAII over the owned thread: +// creates the thread on construction. +// stops and joins the thread on destruction. + +#include +#include +#include +#include +#include +namespace spdlog { +namespace details { + +class periodic_worker +{ +public: + periodic_worker(const std::function &callback_fun, std::chrono::seconds interval) + { + active_ = (interval > std::chrono::seconds::zero()); + if (!active_) + { + return; + } + + worker_thread_ = std::thread([this, callback_fun, interval]() { + for (;;) + { + std::unique_lock lock(this->mutex_); + if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) + { + return; // active_ == false, so exit this thread + } + callback_fun(); + } + }); + } + + periodic_worker(const periodic_worker &) = delete; + periodic_worker &operator=(const periodic_worker &) = delete; + + // stop the worker thread and join it + ~periodic_worker() + { + if (worker_thread_.joinable()) + { + { + std::lock_guard lock(mutex_); + active_ = false; + } + cv_.notify_one(); + worker_thread_.join(); + } + } + +private: + bool active_; + std::thread worker_thread_; + std::mutex mutex_; + std::condition_variable cv_; +}; +} // namespace details +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/details/registry.h b/third_party/spdlog/spdlog/include/spdlog/details/registry.h index 614220d5..3988552a 100644 --- a/third_party/spdlog/spdlog/include/spdlog/details/registry.h +++ b/third_party/spdlog/spdlog/include/spdlog/details/registry.h @@ -10,254 +10,205 @@ // If user requests a non existing logger, nullptr will be returned // This class is thread safe -#include "../async_logger.h" -#include "../common.h" -#include "../details/null_mutex.h" -#include "../logger.h" +#include "spdlog/common.h" +#include "spdlog/details/periodic_worker.h" +#include "spdlog/logger.h" #include #include #include -#include #include #include namespace spdlog { namespace details { -template -class registry_t +class thread_pool; + +class registry { public: - registry_t(const registry_t &) = delete; - registry_t &operator=(const registry_t &) = delete; - - 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; - } + registry(const registry &) = delete; + registry &operator=(const registry &) = delete; - std::shared_ptr get(const std::string &logger_name) + void register_logger(std::shared_ptr new_logger) { - std::lock_guard lock(_mutex); - auto found = _loggers.find(logger_name); - return found == _loggers.end() ? nullptr : found->second; + std::lock_guard lock(logger_map_mutex_); + auto logger_name = new_logger->name(); + throw_if_exists_(logger_name); + loggers_[logger_name] = std::move(new_logger); } - template - std::shared_ptr create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end) + void register_and_init(std::shared_ptr new_logger) { - 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); - } + std::lock_guard lock(logger_map_mutex_); + auto logger_name = new_logger->name(); + throw_if_exists_(logger_name); - if (_formatter) - { - new_logger->set_formatter(_formatter); - } + // set the global formatter pattern + new_logger->set_formatter(formatter_->clone()); - if (_err_handler) + if (err_handler_) { - new_logger->set_error_handler(_err_handler); + new_logger->set_error_handler(err_handler_); } - new_logger->set_level(_level); - new_logger->flush_on(_flush_level); + new_logger->set_level(level_); + new_logger->flush_on(flush_level_); - // Add to registry - _loggers[logger_name] = new_logger; - return new_logger; + // add to registry + loggers_[logger_name] = std::move(new_logger); } - template - std::shared_ptr create_async(const std::string &logger_name, 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, const It &sinks_begin, - const It &sinks_end) + std::shared_ptr get(const std::string &logger_name) { - std::lock_guard lock(_mutex); - throw_if_exists(logger_name); - auto new_logger = std::make_shared( - logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb); - - if (_formatter) - { - new_logger->set_formatter(_formatter); - } - - if (_err_handler) - { - new_logger->set_error_handler(_err_handler); - } - - new_logger->set_level(_level); - new_logger->flush_on(_flush_level); - - // Add to registry - _loggers[logger_name] = new_logger; - return new_logger; + std::lock_guard lock(logger_map_mutex_); + auto found = loggers_.find(logger_name); + return found == loggers_.end() ? nullptr : found->second; } - void apply_all(std::function)> fun) + void set_tp(std::shared_ptr tp) { - std::lock_guard lock(_mutex); - for (auto &l : _loggers) - { - fun(l.second); - } + std::lock_guard lock(tp_mutex_); + tp_ = std::move(tp); } - void drop(const std::string &logger_name) + std::shared_ptr get_tp() { - std::lock_guard lock(_mutex); - _loggers.erase(logger_name); + std::lock_guard lock(tp_mutex_); + return tp_; } - void drop_all() + // Set global formatter. Each sink in each logger will get a clone of this object + void set_formatter(std::unique_ptr formatter) { - std::lock_guard lock(_mutex); - _loggers.clear(); + std::lock_guard lock(logger_map_mutex_); + formatter_ = std::move(formatter); + for (auto &l : loggers_) + { + l.second->set_formatter(formatter_->clone()); + } } - std::shared_ptr create(const std::string &logger_name, sinks_init_list sinks) + void set_level(level::level_enum log_level) { - return create(logger_name, sinks.begin(), sinks.end()); + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) + { + l.second->set_level(log_level); + } + level_ = log_level; } - std::shared_ptr create(const std::string &logger_name, sink_ptr sink) + void flush_on(level::level_enum log_level) { - return create(logger_name, {sink}); + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) + { + l.second->flush_on(log_level); + } + flush_level_ = log_level; } - std::shared_ptr create_async(const std::string &logger_name, 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, sinks_init_list sinks) + void flush_every(std::chrono::seconds interval) { - return create_async( - logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end()); + std::lock_guard lock(flusher_mutex_); + std::function clbk = std::bind(®istry::flush_all, this); + periodic_flusher_.reset(new periodic_worker(clbk, interval)); } - std::shared_ptr create_async(const std::string &logger_name, 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, sink_ptr sink) + void set_error_handler(log_err_handler handler) { - return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, {sink}); + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) + { + l.second->set_error_handler(handler); + } + err_handler_ = handler; } - void formatter(formatter_ptr f) + void apply_all(const std::function)> &fun) { - std::lock_guard lock(_mutex); - _formatter = f; - for (auto &l : _loggers) + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) { - l.second->set_formatter(_formatter); + fun(l.second); } } - void set_pattern(const std::string &pattern) + void flush_all() { - std::lock_guard lock(_mutex); - _formatter = std::make_shared(pattern); - for (auto &l : _loggers) + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) { - l.second->set_formatter(_formatter); + l.second->flush(); } } - void set_level(level::level_enum log_level) + void drop(const std::string &logger_name) { - std::lock_guard lock(_mutex); - for (auto &l : _loggers) - { - l.second->set_level(log_level); - } - _level = log_level; + std::lock_guard lock(logger_map_mutex_); + loggers_.erase(logger_name); } - void flush_on(level::level_enum log_level) + void drop_all() { - std::lock_guard lock(_mutex); - for (auto &l : _loggers) - { - l.second->flush_on(log_level); - } - _flush_level = log_level; + std::lock_guard lock(logger_map_mutex_); + loggers_.clear(); } - void set_error_handler(log_err_handler handler) + // clean all reasources and threads started by the registry + void shutdown() { - for (auto &l : _loggers) { - l.second->set_error_handler(handler); + std::lock_guard lock(flusher_mutex_); + periodic_flusher_.reset(); } - _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; + drop_all(); + + { + std::lock_guard lock(tp_mutex_); + tp_.reset(); + } } - void set_sync_mode() + std::recursive_mutex &tp_mutex() { - std::lock_guard lock(_mutex); - _async_mode = false; + return tp_mutex_; } - static registry_t &instance() + static registry &instance() { - static registry_t s_instance; + static registry s_instance; return s_instance; } private: - registry_t() = default; + registry() + : formatter_(new pattern_formatter("%+")) + { + } + + ~registry() = default; - void throw_if_exists(const std::string &logger_name) + void throw_if_exists_(const std::string &logger_name) { - if (_loggers.find(logger_name) != _loggers.end()) + 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; - level::level_enum _flush_level = level::off; - 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; - std::chrono::milliseconds _flush_interval_ms{std::chrono::milliseconds::zero()}; - std::function _worker_teardown_cb; + std::mutex logger_map_mutex_, flusher_mutex_; + std::recursive_mutex tp_mutex_; + std::unordered_map> loggers_; + std::unique_ptr formatter_; + level::level_enum level_ = level::info; + level::level_enum flush_level_ = level::off; + log_err_handler err_handler_; + std::shared_ptr tp_; + std::unique_ptr periodic_flusher_; }; -#ifdef SPDLOG_NO_REGISTRY_MUTEX -using registry = registry_t; -#else -using registry = registry_t; -#endif - } // namespace details } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/details/spdlog_impl.h b/third_party/spdlog/spdlog/include/spdlog/details/spdlog_impl.h deleted file mode 100644 index 4c363834..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/details/spdlog_impl.h +++ /dev/null @@ -1,278 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -// -// Global registry functions -// -#include "../details/registry.h" -#include "../sinks/file_sinks.h" -#include "../sinks/stdout_sinks.h" -#include "../spdlog.h" -#ifdef SPDLOG_ENABLE_SYSLOG -#include "../sinks/syslog_sink.h" -#endif - -#if defined _WIN32 && !defined(__cplusplus_winrt) -#include "../sinks/wincolor_sink.h" -#else -#include "../sinks/ansicolor_sink.h" -#endif - -#ifdef __ANDROID__ -#include "../sinks/android_sink.h" -#endif - -#include -#include -#include -#include - -inline void spdlog::register_logger(std::shared_ptr logger) -{ - return details::registry::instance().register_logger(std::move(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, 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, 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, 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, hour, minute); -} - -// -// stdout/stderr loggers -// -inline std::shared_ptr spdlog::stdout_logger_mt(const std::string &logger_name) -{ - return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance()); -} - -inline std::shared_ptr spdlog::stdout_logger_st(const std::string &logger_name) -{ - return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance()); -} - -inline std::shared_ptr spdlog::stderr_logger_mt(const std::string &logger_name) -{ - return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance()); -} - -inline std::shared_ptr spdlog::stderr_logger_st(const std::string &logger_name) -{ - return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance()); -} - -// -// stdout/stderr color loggers -// -#if defined _WIN32 && !defined(__cplusplus_winrt) - -inline std::shared_ptr spdlog::stdout_color_mt(const std::string &logger_name) -{ - auto sink = std::make_shared(); - return spdlog::details::registry::instance().create(logger_name, sink); -} - -inline std::shared_ptr spdlog::stdout_color_st(const std::string &logger_name) -{ - auto sink = std::make_shared(); - return spdlog::details::registry::instance().create(logger_name, sink); -} - -inline std::shared_ptr spdlog::stderr_color_mt(const std::string &logger_name) -{ - auto sink = std::make_shared(); - return spdlog::details::registry::instance().create(logger_name, sink); -} - -inline std::shared_ptr spdlog::stderr_color_st(const std::string &logger_name) -{ - auto sink = std::make_shared(); - return spdlog::details::registry::instance().create(logger_name, sink); -} - -#else // ansi terminal colors - -inline std::shared_ptr spdlog::stdout_color_mt(const std::string &logger_name) -{ - auto sink = std::make_shared(); - return spdlog::details::registry::instance().create(logger_name, sink); -} - -inline std::shared_ptr spdlog::stdout_color_st(const std::string &logger_name) -{ - auto sink = std::make_shared(); - return spdlog::details::registry::instance().create(logger_name, sink); -} - -inline std::shared_ptr spdlog::stderr_color_mt(const std::string &logger_name) -{ - auto sink = std::make_shared(); - return spdlog::details::registry::instance().create(logger_name, sink); -} - -inline std::shared_ptr spdlog::stderr_color_st(const std::string &logger_name) -{ - auto sink = std::make_shared(); - return spdlog::details::registry::instance().create(logger_name, sink); -} -#endif - -#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, int syslog_facility) -{ - return create(logger_name, syslog_ident, syslog_option, syslog_facility); -} -#endif - -#ifdef __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); -} - -// Create and register an async logger with a single sink -inline std::shared_ptr spdlog::create_async(const std::string &logger_name, const sink_ptr &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) -{ - return details::registry::instance().create_async( - logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink); -} - -// Create and register an async logger with multiple sinks -inline std::shared_ptr spdlog::create_async(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) -{ - return details::registry::instance().create_async( - logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks); -} - -template -inline std::shared_ptr spdlog::create_async(const std::string &logger_name, const It &sinks_begin, const It &sinks_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) -{ - return details::registry::instance().create_async( - logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end); -} - -inline void spdlog::set_formatter(spdlog::formatter_ptr f) -{ - details::registry::instance().formatter(std::move(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::flush_on(level::level_enum log_level) -{ - return details::registry::instance().flush_on(log_level); -} - -inline void spdlog::set_error_handler(log_err_handler handler) -{ - return details::registry::instance().set_error_handler(std::move(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(std::move(fun)); -} - -inline void spdlog::drop_all() -{ - details::registry::instance().drop_all(); -} diff --git a/third_party/spdlog/spdlog/include/spdlog/details/thread_pool.h b/third_party/spdlog/spdlog/include/spdlog/details/thread_pool.h new file mode 100644 index 00000000..282d67e8 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/details/thread_pool.h @@ -0,0 +1,225 @@ +#pragma once + +#include "spdlog/details/log_msg.h" +#include "spdlog/details/mpmc_blocking_q.h" +#include "spdlog/details/os.h" + +#include +#include +#include +#include + +namespace spdlog { +namespace details { + +using async_logger_ptr = std::shared_ptr; + +enum class async_msg_type +{ + log, + flush, + terminate +}; + +// Async msg to move to/from the queue +// Movable only. should never be copied +struct async_msg +{ + async_msg_type msg_type; + level::level_enum level; + log_clock::time_point time; + size_t thread_id; + fmt::basic_memory_buffer raw; + + size_t msg_id; + async_logger_ptr worker_ptr; + + async_msg() = default; + ~async_msg() = default; + + // should only be moved in or out of the queue.. + async_msg(const async_msg &) = delete; + +// support for vs2013 move +#if defined(_MSC_VER) && _MSC_VER <= 1800 + async_msg(async_msg &&other) SPDLOG_NOEXCEPT : msg_type(other.msg_type), + level(other.level), + time(other.time), + thread_id(other.thread_id), + raw(move(other.raw)), + msg_id(other.msg_id), + worker_ptr(std::move(other.worker_ptr)) + { + } + + async_msg &operator=(async_msg &&other) SPDLOG_NOEXCEPT + { + msg_type = other.msg_type; + level = other.level; + time = other.time; + thread_id = other.thread_id; + raw = std::move(other.raw); + msg_id = other.msg_id; + worker_ptr = std::move(other.worker_ptr); + return *this; + } +#else // (_MSC_VER) && _MSC_VER <= 1800 + async_msg(async_msg &&other) = default; + async_msg &operator=(async_msg &&other) = default; +#endif + + // construct from log_msg with given type + async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &&m) + : msg_type(the_type) + , level(m.level) + , time(m.time) + , thread_id(m.thread_id) + , msg_id(m.msg_id) + , worker_ptr(std::forward(worker)) + { + fmt_helper::append_buf(m.raw, raw); + } + + async_msg(async_logger_ptr &&worker, async_msg_type the_type) + : async_msg(std::forward(worker), the_type, details::log_msg()) + { + } + + explicit async_msg(async_msg_type the_type) + : async_msg(nullptr, the_type, details::log_msg()) + { + } + + // copy into log_msg + void to_log_msg(log_msg &msg) + { + msg.logger_name = &worker_ptr->name(); + msg.level = level; + msg.time = time; + msg.thread_id = thread_id; + fmt_helper::append_buf(raw, msg.raw); + msg.msg_id = msg_id; + msg.color_range_start = 0; + msg.color_range_end = 0; + } +}; + +class thread_pool +{ +public: + using item_type = async_msg; + using q_type = details::mpmc_blocking_queue; + + thread_pool(size_t q_max_items, size_t threads_n) + : q_(q_max_items) + { + // std::cout << "thread_pool() q_size_bytes: " << q_size_bytes << + // "\tthreads_n: " << threads_n << std::endl; + if (threads_n == 0 || threads_n > 1000) + { + throw spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid " + "range is 1-1000)"); + } + for (size_t i = 0; i < threads_n; i++) + { + threads_.emplace_back(&thread_pool::worker_loop_, this); + } + } + + // message all threads to terminate gracefully join them + ~thread_pool() + { + try + { + for (size_t i = 0; i < threads_.size(); i++) + { + post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block); + } + + for (auto &t : threads_) + { + t.join(); + } + } + catch (...) + { + } + } + + void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy) + { + async_msg async_m(std::forward(worker_ptr), async_msg_type::log, std::forward(msg)); + post_async_msg_(std::move(async_m), overflow_policy); + } + + void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy) + { + post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy); + } + + size_t overrun_counter() + { + return q_.overrun_counter(); + } + +private: + q_type q_; + + std::vector threads_; + + void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy) + { + if (overflow_policy == async_overflow_policy::block) + { + q_.enqueue(std::move(new_msg)); + } + else + { + q_.enqueue_nowait(std::move(new_msg)); + } + } + + void worker_loop_() + { + while (process_next_msg_()) {}; + } + + // process next message in the queue + // return true if this thread should still be active (while no terminate msg + // was received) + bool process_next_msg_() + { + async_msg incoming_async_msg; + bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10)); + if (!dequeued) + { + return true; + } + + switch (incoming_async_msg.msg_type) + { + case async_msg_type::flush: + { + incoming_async_msg.worker_ptr->backend_flush_(); + return true; + } + + case async_msg_type::terminate: + { + return false; + } + + default: + { + log_msg msg; + incoming_async_msg.to_log_msg(msg); + incoming_async_msg.worker_ptr->backend_log_(msg); + return true; + } + } + return true; // should not be reached + } +}; + +} // namespace details +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/colors.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/colors.h new file mode 100644 index 00000000..003a6679 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/colors.h @@ -0,0 +1,257 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. +// +// Copyright (c) 2018 - present, Remotion (Igor Schulz) +// All Rights Reserved +// {fmt} support for rgb color output. + +#ifndef FMT_COLORS_H_ +#define FMT_COLORS_H_ + +#include "format.h" + +FMT_BEGIN_NAMESPACE + +// rgb is a struct for red, green and blue colors. +// We use rgb as name because some editors will show it as color direct in the +// editor. +struct rgb +{ + FMT_CONSTEXPR_DECL rgb() + : r(0) + , g(0) + , b(0) + { + } + FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_) + : r(r_) + , g(g_) + , b(b_) + { + } + FMT_CONSTEXPR_DECL rgb(uint32_t hex) + : r((hex >> 16) & 0xFF) + , g((hex >> 8) & 0xFF) + , b((hex)&0xFF) + { + } + uint8_t r; + uint8_t g; + uint8_t b; +}; + +namespace internal { + +FMT_CONSTEXPR inline void to_esc(uint8_t c, char out[], int offset) +{ + out[offset + 0] = static_cast('0' + c / 100); + out[offset + 1] = static_cast('0' + c / 10 % 10); + out[offset + 2] = static_cast('0' + c % 10); +} + +} // namespace internal + +FMT_FUNC void vprint_rgb(rgb fd, string_view format, format_args args) +{ + char escape_fd[] = "\x1b[38;2;000;000;000m"; + static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m"; + internal::to_esc(fd.r, escape_fd, 7); + internal::to_esc(fd.g, escape_fd, 11); + internal::to_esc(fd.b, escape_fd, 15); + + std::fputs(escape_fd, stdout); + vprint(format, args); + std::fputs(RESET_COLOR, stdout); +} + +FMT_FUNC void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args) +{ + char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color + char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color + static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m"; + internal::to_esc(fd.r, escape_fd, 7); + internal::to_esc(fd.g, escape_fd, 11); + internal::to_esc(fd.b, escape_fd, 15); + + internal::to_esc(bg.r, escape_bg, 7); + internal::to_esc(bg.g, escape_bg, 11); + internal::to_esc(bg.b, escape_bg, 15); + + std::fputs(escape_fd, stdout); + std::fputs(escape_bg, stdout); + vprint(format, args); + std::fputs(RESET_COLOR, stdout); +} + +template +inline void print_rgb(rgb fd, string_view format_str, const Args &... args) +{ + vprint_rgb(fd, format_str, make_format_args(args...)); +} + +// rgb foreground color +template +inline void print(rgb fd, string_view format_str, const Args &... args) +{ + vprint_rgb(fd, format_str, make_format_args(args...)); +} + +// rgb foreground color and background color +template +inline void print(rgb fd, rgb bg, string_view format_str, const Args &... args) +{ + vprint_rgb(fd, bg, format_str, make_format_args(args...)); +} + +enum class color : uint32_t +{ + alice_blue = 0xF0F8FF, // rgb(240,248,255) + antique_white = 0xFAEBD7, // rgb(250,235,215) + aqua = 0x00FFFF, // rgb(0,255,255) + aquamarine = 0x7FFFD4, // rgb(127,255,212) + azure = 0xF0FFFF, // rgb(240,255,255) + beige = 0xF5F5DC, // rgb(245,245,220) + bisque = 0xFFE4C4, // rgb(255,228,196) + black = 0x000000, // rgb(0,0,0) + blanched_almond = 0xFFEBCD, // rgb(255,235,205) + blue = 0x0000FF, // rgb(0,0,255) + blue_violet = 0x8A2BE2, // rgb(138,43,226) + brown = 0xA52A2A, // rgb(165,42,42) + burly_wood = 0xDEB887, // rgb(222,184,135) + cadet_blue = 0x5F9EA0, // rgb(95,158,160) + chartreuse = 0x7FFF00, // rgb(127,255,0) + chocolate = 0xD2691E, // rgb(210,105,30) + coral = 0xFF7F50, // rgb(255,127,80) + cornflower_blue = 0x6495ED, // rgb(100,149,237) + cornsilk = 0xFFF8DC, // rgb(255,248,220) + crimson = 0xDC143C, // rgb(220,20,60) + cyan = 0x00FFFF, // rgb(0,255,255) + dark_blue = 0x00008B, // rgb(0,0,139) + dark_cyan = 0x008B8B, // rgb(0,139,139) + dark_golden_rod = 0xB8860B, // rgb(184,134,11) + dark_gray = 0xA9A9A9, // rgb(169,169,169) + dark_green = 0x006400, // rgb(0,100,0) + dark_khaki = 0xBDB76B, // rgb(189,183,107) + dark_magenta = 0x8B008B, // rgb(139,0,139) + dark_olive_green = 0x556B2F, // rgb(85,107,47) + dark_orange = 0xFF8C00, // rgb(255,140,0) + dark_orchid = 0x9932CC, // rgb(153,50,204) + dark_red = 0x8B0000, // rgb(139,0,0) + dark_salmon = 0xE9967A, // rgb(233,150,122) + dark_sea_green = 0x8FBC8F, // rgb(143,188,143) + dark_slate_blue = 0x483D8B, // rgb(72,61,139) + dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) + dark_turquoise = 0x00CED1, // rgb(0,206,209) + dark_violet = 0x9400D3, // rgb(148,0,211) + deep_pink = 0xFF1493, // rgb(255,20,147) + deep_sky_blue = 0x00BFFF, // rgb(0,191,255) + dim_gray = 0x696969, // rgb(105,105,105) + dodger_blue = 0x1E90FF, // rgb(30,144,255) + fire_brick = 0xB22222, // rgb(178,34,34) + floral_white = 0xFFFAF0, // rgb(255,250,240) + forest_green = 0x228B22, // rgb(34,139,34) + fuchsia = 0xFF00FF, // rgb(255,0,255) + gainsboro = 0xDCDCDC, // rgb(220,220,220) + ghost_white = 0xF8F8FF, // rgb(248,248,255) + gold = 0xFFD700, // rgb(255,215,0) + golden_rod = 0xDAA520, // rgb(218,165,32) + gray = 0x808080, // rgb(128,128,128) + green = 0x008000, // rgb(0,128,0) + green_yellow = 0xADFF2F, // rgb(173,255,47) + honey_dew = 0xF0FFF0, // rgb(240,255,240) + hot_pink = 0xFF69B4, // rgb(255,105,180) + indian_red = 0xCD5C5C, // rgb(205,92,92) + indigo = 0x4B0082, // rgb(75,0,130) + ivory = 0xFFFFF0, // rgb(255,255,240) + khaki = 0xF0E68C, // rgb(240,230,140) + lavender = 0xE6E6FA, // rgb(230,230,250) + lavender_blush = 0xFFF0F5, // rgb(255,240,245) + lawn_green = 0x7CFC00, // rgb(124,252,0) + lemon_chiffon = 0xFFFACD, // rgb(255,250,205) + light_blue = 0xADD8E6, // rgb(173,216,230) + light_coral = 0xF08080, // rgb(240,128,128) + light_cyan = 0xE0FFFF, // rgb(224,255,255) + light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) + light_gray = 0xD3D3D3, // rgb(211,211,211) + light_green = 0x90EE90, // rgb(144,238,144) + light_pink = 0xFFB6C1, // rgb(255,182,193) + light_salmon = 0xFFA07A, // rgb(255,160,122) + light_sea_green = 0x20B2AA, // rgb(32,178,170) + light_sky_blue = 0x87CEFA, // rgb(135,206,250) + light_slate_gray = 0x778899, // rgb(119,136,153) + light_steel_blue = 0xB0C4DE, // rgb(176,196,222) + light_yellow = 0xFFFFE0, // rgb(255,255,224) + lime = 0x00FF00, // rgb(0,255,0) + lime_green = 0x32CD32, // rgb(50,205,50) + linen = 0xFAF0E6, // rgb(250,240,230) + magenta = 0xFF00FF, // rgb(255,0,255) + maroon = 0x800000, // rgb(128,0,0) + medium_aqua_marine = 0x66CDAA, // rgb(102,205,170) + medium_blue = 0x0000CD, // rgb(0,0,205) + medium_orchid = 0xBA55D3, // rgb(186,85,211) + medium_purple = 0x9370DB, // rgb(147,112,219) + medium_sea_green = 0x3CB371, // rgb(60,179,113) + medium_slate_blue = 0x7B68EE, // rgb(123,104,238) + medium_spring_green = 0x00FA9A, // rgb(0,250,154) + medium_turquoise = 0x48D1CC, // rgb(72,209,204) + medium_violet_red = 0xC71585, // rgb(199,21,133) + midnight_blue = 0x191970, // rgb(25,25,112) + mint_cream = 0xF5FFFA, // rgb(245,255,250) + misty_rose = 0xFFE4E1, // rgb(255,228,225) + moccasin = 0xFFE4B5, // rgb(255,228,181) + navajo_white = 0xFFDEAD, // rgb(255,222,173) + navy = 0x000080, // rgb(0,0,128) + old_lace = 0xFDF5E6, // rgb(253,245,230) + olive = 0x808000, // rgb(128,128,0) + olive_drab = 0x6B8E23, // rgb(107,142,35) + orange = 0xFFA500, // rgb(255,165,0) + orange_red = 0xFF4500, // rgb(255,69,0) + orchid = 0xDA70D6, // rgb(218,112,214) + pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) + pale_green = 0x98FB98, // rgb(152,251,152) + pale_turquoise = 0xAFEEEE, // rgb(175,238,238) + pale_violet_red = 0xDB7093, // rgb(219,112,147) + papaya_whip = 0xFFEFD5, // rgb(255,239,213) + peach_puff = 0xFFDAB9, // rgb(255,218,185) + peru = 0xCD853F, // rgb(205,133,63) + pink = 0xFFC0CB, // rgb(255,192,203) + plum = 0xDDA0DD, // rgb(221,160,221) + powder_blue = 0xB0E0E6, // rgb(176,224,230) + purple = 0x800080, // rgb(128,0,128) + rebecca_purple = 0x663399, // rgb(102,51,153) + red = 0xFF0000, // rgb(255,0,0) + rosy_brown = 0xBC8F8F, // rgb(188,143,143) + royal_blue = 0x4169E1, // rgb(65,105,225) + saddle_brown = 0x8B4513, // rgb(139,69,19) + salmon = 0xFA8072, // rgb(250,128,114) + sandy_brown = 0xF4A460, // rgb(244,164,96) + sea_green = 0x2E8B57, // rgb(46,139,87) + sea_shell = 0xFFF5EE, // rgb(255,245,238) + sienna = 0xA0522D, // rgb(160,82,45) + silver = 0xC0C0C0, // rgb(192,192,192) + sky_blue = 0x87CEEB, // rgb(135,206,235) + slate_blue = 0x6A5ACD, // rgb(106,90,205) + slate_gray = 0x708090, // rgb(112,128,144) + snow = 0xFFFAFA, // rgb(255,250,250) + spring_green = 0x00FF7F, // rgb(0,255,127) + steel_blue = 0x4682B4, // rgb(70,130,180) + tan = 0xD2B48C, // rgb(210,180,140) + teal = 0x008080, // rgb(0,128,128) + thistle = 0xD8BFD8, // rgb(216,191,216) + tomato = 0xFF6347, // rgb(255,99,71) + turquoise = 0x40E0D0, // rgb(64,224,208) + violet = 0xEE82EE, // rgb(238,130,238) + wheat = 0xF5DEB3, // rgb(245,222,179) + white = 0xFFFFFF, // rgb(255,255,255) + white_smoke = 0xF5F5F5, // rgb(245,245,245) + yellow = 0xFFFF00, // rgb(255,255,0) + yellow_green = 0x9ACD32, // rgb(154,205,50) +}; // enum class colors + +FMT_END_NAMESPACE + +#endif // FMT_COLORS_H_ diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/core.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/core.h new file mode 100644 index 00000000..19273a67 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/core.h @@ -0,0 +1,1578 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_CORE_H_ +#define FMT_CORE_H_ + +#include +#include +#include +#include +#include +#include + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 50100 + +#ifdef __has_feature +#define FMT_HAS_FEATURE(x) __has_feature(x) +#else +#define FMT_HAS_FEATURE(x) 0 +#endif + +#ifdef __has_include +#define FMT_HAS_INCLUDE(x) __has_include(x) +#else +#define FMT_HAS_INCLUDE(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 + +#if defined(__GNUC__) && !defined(__clang__) +#define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#else +#define FMT_GCC_VERSION 0 +#endif + +#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) +#define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION +#else +#define FMT_HAS_GXX_CXX11 0 +#endif + +#ifdef _MSC_VER +#define FMT_MSC_VER _MSC_VER +#else +#define FMT_MSC_VER 0 +#endif + +// Check if relaxed c++14 constexpr is supported. +// GCC doesn't allow throw in constexpr until version 6 (bug 67371). +#ifndef FMT_USE_CONSTEXPR +#define FMT_USE_CONSTEXPR \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) +#endif +#if FMT_USE_CONSTEXPR +#define FMT_CONSTEXPR constexpr +#define FMT_CONSTEXPR_DECL constexpr +#else +#define FMT_CONSTEXPR inline +#define FMT_CONSTEXPR_DECL +#endif + +#ifndef FMT_OVERRIDE +#if FMT_HAS_FEATURE(cxx_override) || (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +#define FMT_OVERRIDE override +#else +#define FMT_OVERRIDE +#endif +#endif + +#if FMT_HAS_FEATURE(cxx_explicit_conversions) || FMT_MSC_VER >= 1800 +#define FMT_EXPLICIT explicit +#else +#define FMT_EXPLICIT +#endif + +#ifndef FMT_NULL +#if FMT_HAS_FEATURE(cxx_nullptr) || (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600 +#define FMT_NULL nullptr +#define FMT_USE_NULLPTR 1 +#else +#define FMT_NULL NULL +#endif +#endif + +#ifndef FMT_USE_NULLPTR +#define FMT_USE_NULLPTR 0 +#endif + +#if FMT_HAS_CPP_ATTRIBUTE(noreturn) +#define FMT_NORETURN [[noreturn]] +#else +#define FMT_NORETURN +#endif + +// Check if exceptions are disabled. +#if defined(__GNUC__) && !defined(__EXCEPTIONS) +#define FMT_EXCEPTIONS 0 +#elif FMT_MSC_VER && !_HAS_EXCEPTIONS +#define FMT_EXCEPTIONS 0 +#endif +#ifndef FMT_EXCEPTIONS +#define FMT_EXCEPTIONS 1 +#endif + +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +#define FMT_USE_NOEXCEPT 0 +#endif + +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +#define FMT_DETECTED_NOEXCEPT noexcept +#else +#define FMT_DETECTED_NOEXCEPT throw() +#endif + +#ifndef FMT_NOEXCEPT +#if FMT_EXCEPTIONS +#define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +#else +#define FMT_NOEXCEPT +#endif +#endif + +// This is needed because GCC still uses throw() in its headers when exceptions +// are disabled. +#if FMT_GCC_VERSION +#define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT +#else +#define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT +#endif + +#ifndef FMT_BEGIN_NAMESPACE +#if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || FMT_MSC_VER >= 1900 +#define FMT_INLINE_NAMESPACE inline namespace +#define FMT_END_NAMESPACE \ + } \ + } +#else +#define FMT_INLINE_NAMESPACE namespace +#define FMT_END_NAMESPACE \ + } \ + using namespace v5; \ + } +#endif +#define FMT_BEGIN_NAMESPACE \ + namespace fmt { \ + FMT_INLINE_NAMESPACE v5 \ + { +#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 + +#ifndef FMT_ASSERT +#define FMT_ASSERT(condition, message) assert((condition) && message) +#endif + +#define FMT_DELETED = delete + +// A macro to disallow the copy construction and assignment. +#define FMT_DISALLOW_COPY_AND_ASSIGN(Type) \ + Type(const Type &) FMT_DELETED; \ + void operator=(const Type &) FMT_DELETED + +// libc++ supports string_view in pre-c++17. +#if (FMT_HAS_INCLUDE() && (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +#include +#define FMT_USE_STD_STRING_VIEW +#elif (FMT_HAS_INCLUDE() && __cplusplus >= 201402L) +#include +#define FMT_USE_EXPERIMENTAL_STRING_VIEW +#endif + +// std::result_of is defined in in gcc 4.4. +#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 404 +#include +#endif + +FMT_BEGIN_NAMESPACE + +namespace internal { + +// An implementation of declval for pre-C++11 compilers such as gcc 4. +template +typename std::add_rvalue_reference::type declval() FMT_NOEXCEPT; + +// Casts nonnegative integer to unsigned. +template +FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) +{ + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::type>(value); +} + +} // namespace internal + +/** + An implementation of ``std::basic_string_view`` for pre-C++17. It provides a + subset of the API. ``fmt::basic_string_view`` is used for format strings even + if ``std::string_view`` is available to prevent issues when a library is + compiled with a different ``-std`` option than the client code (which is not + recommended). + */ +template +class basic_string_view +{ +private: + const Char *data_; + size_t size_; + +public: + typedef Char char_type; + typedef const Char *iterator; + +// Standard basic_string_view type. +#if defined(FMT_USE_STD_STRING_VIEW) + typedef std::basic_string_view type; +#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) + typedef std::experimental::basic_string_view type; +#else + struct type + { + const char *data() const + { + return FMT_NULL; + } + size_t size() const + { + return 0; + } + }; +#endif + + FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(FMT_NULL), size_(0) {} + + /** Constructs a string reference object from a C string and a size. */ + FMT_CONSTEXPR basic_string_view(const Char *s, size_t count) FMT_NOEXCEPT : data_(s), size_(count) {} + + /** + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ + basic_string_view(const Char *s) + : data_(s) + , size_(std::char_traits::length(s)) + { + } + + /** Constructs a string reference from a ``std::basic_string`` object. */ + template + FMT_CONSTEXPR basic_string_view(const std::basic_string &s) FMT_NOEXCEPT : data_(s.c_str()), size_(s.size()) + { + } + + FMT_CONSTEXPR basic_string_view(type s) FMT_NOEXCEPT : data_(s.data()), size_(s.size()) {} + + /** Returns a pointer to the string data. */ + const Char *data() const + { + return data_; + } + + /** Returns the string size. */ + FMT_CONSTEXPR size_t size() const + { + return size_; + } + + FMT_CONSTEXPR iterator begin() const + { + return data_; + } + FMT_CONSTEXPR iterator end() const + { + return data_ + size_; + } + + FMT_CONSTEXPR void remove_prefix(size_t n) + { + data_ += n; + size_ -= n; + } + + // Lexicographically compare this string reference to other. + int compare(basic_string_view other) const + { + size_t str_size = size_ < other.size_ ? size_ : other.size_; + int result = std::char_traits::compare(data_, other.data_, str_size); + if (result == 0) + result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); + return result; + } + + friend bool operator==(basic_string_view lhs, basic_string_view rhs) + { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(basic_string_view lhs, basic_string_view rhs) + { + return lhs.compare(rhs) != 0; + } + friend bool operator<(basic_string_view lhs, basic_string_view rhs) + { + return lhs.compare(rhs) < 0; + } + friend bool operator<=(basic_string_view lhs, basic_string_view rhs) + { + return lhs.compare(rhs) <= 0; + } + friend bool operator>(basic_string_view lhs, basic_string_view rhs) + { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(basic_string_view lhs, basic_string_view rhs) + { + return lhs.compare(rhs) >= 0; + } +}; + +typedef basic_string_view string_view; +typedef basic_string_view wstring_view; + +template +class basic_format_arg; + +template +class basic_format_args; + +// A formatter for objects of type T. +template +struct formatter; + +namespace internal { + +/** A contiguous memory buffer with an optional growing ability. */ +template +class basic_buffer +{ +private: + FMT_DISALLOW_COPY_AND_ASSIGN(basic_buffer); + + T *ptr_; + std::size_t size_; + std::size_t capacity_; + +protected: + basic_buffer(T *p = FMT_NULL, std::size_t sz = 0, std::size_t cap = 0) FMT_NOEXCEPT : ptr_(p), size_(sz), capacity_(cap) {} + + /** Sets the buffer data and capacity. */ + void set(T *buf_data, std::size_t buf_capacity) FMT_NOEXCEPT + { + ptr_ = buf_data; + capacity_ = buf_capacity; + } + + /** Increases the buffer capacity to hold at least *capacity* elements. */ + virtual void grow(std::size_t capacity) = 0; + +public: + typedef T value_type; + typedef const T &const_reference; + + virtual ~basic_buffer() {} + + T *begin() FMT_NOEXCEPT + { + return ptr_; + } + T *end() FMT_NOEXCEPT + { + return ptr_ + size_; + } + + /** Returns the size of this buffer. */ + std::size_t size() const FMT_NOEXCEPT + { + return size_; + } + + /** Returns the capacity of this buffer. */ + std::size_t capacity() const FMT_NOEXCEPT + { + return capacity_; + } + + /** Returns a pointer to the buffer data. */ + T *data() FMT_NOEXCEPT + { + return ptr_; + } + + /** Returns a pointer to the buffer data. */ + const T *data() const FMT_NOEXCEPT + { + return ptr_; + } + + /** + Resizes the buffer. If T is a POD type new elements may not be initialized. + */ + void resize(std::size_t new_size) + { + reserve(new_size); + size_ = new_size; + } + + /** Reserves space to store at least *capacity* elements. */ + void reserve(std::size_t new_capacity) + { + if (new_capacity > capacity_) + grow(new_capacity); + } + + void push_back(const T &value) + { + reserve(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]; + } +}; + +typedef basic_buffer buffer; +typedef basic_buffer wbuffer; + +// A container-backed buffer. +template +class container_buffer : public basic_buffer +{ +private: + Container &container_; + +protected: + void grow(std::size_t capacity) FMT_OVERRIDE + { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + +public: + explicit container_buffer(Container &c) + : basic_buffer(&c[0], c.size(), c.size()) + , container_(c) + { + } +}; + +struct error_handler +{ + FMT_CONSTEXPR error_handler() {} + FMT_CONSTEXPR error_handler(const error_handler &) {} + + // This function is intentionally not constexpr to give a compile-time error. + FMT_API void on_error(const char *message); +}; + +// Formatting of wide characters and strings into a narrow output is disallowed: +// fmt::format("{}", L"test"); // error +// To fix this, use a wide format string: +// fmt::format(L"{}", L"test"); +template +inline void require_wchar() +{ + static_assert(std::is_same::value, "formatting of wide characters into a narrow output is disallowed"); +} + +template +struct named_arg_base; + +template +struct named_arg; + +template +struct is_named_arg : std::false_type +{ +}; + +template +struct is_named_arg> : std::true_type +{ +}; + +enum type +{ + none_type, + named_arg_type, + // Integer types should go first, + int_type, + uint_type, + long_long_type, + ulong_long_type, + bool_type, + char_type, + last_integer_type = char_type, + // followed by floating-point types. + double_type, + long_double_type, + last_numeric_type = long_double_type, + cstring_type, + string_type, + pointer_type, + custom_type +}; + +FMT_CONSTEXPR bool is_integral(type t) +{ + FMT_ASSERT(t != internal::named_arg_type, "invalid argument type"); + return t > internal::none_type && t <= internal::last_integer_type; +} + +FMT_CONSTEXPR bool is_arithmetic(type t) +{ + FMT_ASSERT(t != internal::named_arg_type, "invalid argument type"); + return t > internal::none_type && t <= internal::last_numeric_type; +} + +template +struct convert_to_int +{ + enum + { + value = !std::is_arithmetic::value && std::is_convertible::value + }; +}; + +template +struct string_value +{ + const Char *value; + std::size_t size; +}; + +template +struct custom_value +{ + const void *value; + void (*format)(const void *arg, Context &ctx); +}; + +// A formatting argument value. +template +class value +{ +public: + typedef typename Context::char_type char_type; + + union + { + int int_value; + unsigned uint_value; + long long long_long_value; + unsigned long long ulong_long_value; + double double_value; + long double long_double_value; + const void *pointer; + string_value string; + string_value sstring; + string_value ustring; + custom_value custom; + }; + + FMT_CONSTEXPR value(int val = 0) + : int_value(val) + { + } + value(unsigned val) + { + uint_value = val; + } + value(long long val) + { + long_long_value = val; + } + value(unsigned long long val) + { + ulong_long_value = val; + } + value(double val) + { + double_value = val; + } + value(long double val) + { + long_double_value = val; + } + value(const char_type *val) + { + string.value = val; + } + value(const signed char *val) + { + static_assert(std::is_same::value, "incompatible string types"); + sstring.value = val; + } + value(const unsigned char *val) + { + static_assert(std::is_same::value, "incompatible string types"); + ustring.value = val; + } + value(basic_string_view val) + { + string.value = val.data(); + string.size = val.size(); + } + value(const void *val) + { + pointer = val; + } + + template + explicit value(const T &val) + { + custom.value = &val; + custom.format = &format_custom_arg; + } + + const named_arg_base &as_named_arg() + { + return *static_cast *>(pointer); + } + +private: + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg(const void *arg, Context &ctx) + { + // Get the formatter type through the context to allow different contexts + // have different extension points, e.g. `formatter` for `format` and + // `printf_formatter` for `printf`. + typename Context::template formatter_type::type f; + auto &&parse_ctx = ctx.parse_context(); + parse_ctx.advance_to(f.parse(parse_ctx)); + ctx.advance_to(f.format(*static_cast(arg), ctx)); + } +}; + +template +struct typed_value : value +{ + static const type type_tag = TYPE; + + template + FMT_CONSTEXPR typed_value(const T &val) + : value(val) + { + } +}; + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T &value); + +#define FMT_MAKE_VALUE(TAG, ArgType, ValueType) \ + template \ + FMT_CONSTEXPR typed_value make_value(ArgType val) \ + { \ + return static_cast(val); \ + } + +#define FMT_MAKE_VALUE_SAME(TAG, Type) \ + template \ + FMT_CONSTEXPR typed_value make_value(Type val) \ + { \ + return val; \ + } + +FMT_MAKE_VALUE(bool_type, bool, int) +FMT_MAKE_VALUE(int_type, short, int) +FMT_MAKE_VALUE(uint_type, unsigned short, unsigned) +FMT_MAKE_VALUE_SAME(int_type, int) +FMT_MAKE_VALUE_SAME(uint_type, unsigned) + +// 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. +typedef std::conditional::type long_type; +FMT_MAKE_VALUE((sizeof(long) == sizeof(int) ? int_type : long_long_type), long, long_type) +typedef std::conditional::type ulong_type; +FMT_MAKE_VALUE((sizeof(unsigned long) == sizeof(unsigned) ? uint_type : ulong_long_type), unsigned long, ulong_type) + +FMT_MAKE_VALUE_SAME(long_long_type, long long) +FMT_MAKE_VALUE_SAME(ulong_long_type, unsigned long long) +FMT_MAKE_VALUE(int_type, signed char, int) +FMT_MAKE_VALUE(uint_type, unsigned char, unsigned) +FMT_MAKE_VALUE(char_type, char, int) + +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +template +inline typed_value make_value(wchar_t val) +{ + require_wchar(); + return static_cast(val); +} +#endif + +FMT_MAKE_VALUE(double_type, float, double) +FMT_MAKE_VALUE_SAME(double_type, double) +FMT_MAKE_VALUE_SAME(long_double_type, long double) + +// Formatting of wide strings into a narrow buffer and multibyte strings +// into a wide buffer is disallowed (https://github.com/fmtlib/fmt/pull/606). +FMT_MAKE_VALUE(cstring_type, typename C::char_type *, const typename C::char_type *) +FMT_MAKE_VALUE(cstring_type, const typename C::char_type *, const typename C::char_type *) + +FMT_MAKE_VALUE(cstring_type, signed char *, const signed char *) +FMT_MAKE_VALUE_SAME(cstring_type, const signed char *) +FMT_MAKE_VALUE(cstring_type, unsigned char *, const unsigned char *) +FMT_MAKE_VALUE_SAME(cstring_type, const unsigned char *) +FMT_MAKE_VALUE_SAME(string_type, basic_string_view) +FMT_MAKE_VALUE(string_type, typename basic_string_view::type, basic_string_view) +FMT_MAKE_VALUE(string_type, const std::basic_string &, basic_string_view) +FMT_MAKE_VALUE(pointer_type, void *, const void *) +FMT_MAKE_VALUE_SAME(pointer_type, const void *) + +#if FMT_USE_NULLPTR +FMT_MAKE_VALUE(pointer_type, std::nullptr_t, const void *) +#endif + +// Formatting of arbitrary pointers is disallowed. 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. +template +typename std::enable_if::value>::type make_value(const T *) +{ + static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); +} + +template +inline typename std::enable_if::value && convert_to_int::value, typed_value>::type +make_value(const T &val) +{ + return static_cast(val); +} + +template +inline typename std::enable_if::value && !std::is_convertible>::value, + // Implicit conversion to std::string is not handled here because it's + // unsafe: https://github.com/fmtlib/fmt/issues/729 + typed_value>::type +make_value(const T &val) +{ + return val; +} + +template +typed_value make_value(const named_arg &val) +{ + basic_format_arg arg = make_arg(val.value); + std::memcpy(val.data, &arg, sizeof(arg)); + return static_cast(&val); +} + +// Maximum number of arguments with packed types. +enum +{ + max_packed_args = 15 +}; + +template +class arg_map; + +template +struct result_of; + +template +struct result_of +{ + // A workaround for gcc 4.4 that doesn't allow F to be a reference. + typedef typename std::result_of::type(Args...)>::type type; +}; +} // namespace internal + +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in basic_memory_buffer. +template +class basic_format_arg +{ +private: + internal::value value_; + internal::type type_; + + template + friend FMT_CONSTEXPR basic_format_arg internal::make_arg(const T &value); + + template + friend FMT_CONSTEXPR typename internal::result_of::type visit(Visitor &&vis, basic_format_arg arg); + + friend class basic_format_args; + friend class internal::arg_map; + + typedef typename Context::char_type char_type; + +public: + class handle + { + public: + explicit handle(internal::custom_value custom) + : custom_(custom) + { + } + + void format(Context &ctx) const + { + custom_.format(custom_.value, ctx); + } + + private: + internal::custom_value custom_; + }; + + FMT_CONSTEXPR basic_format_arg() + : type_(internal::none_type) + { + } + + FMT_EXPLICIT operator bool() const FMT_NOEXCEPT + { + return type_ != internal::none_type; + } + + internal::type type() const + { + return type_; + } + + bool is_integral() const + { + return internal::is_integral(type_); + } + bool is_arithmetic() const + { + return internal::is_arithmetic(type_); + } +}; + +// Parsing context consisting of a format string range being parsed and an +// argument counter for automatic indexing. +template +class basic_parse_context : private ErrorHandler +{ +private: + basic_string_view format_str_; + int next_arg_id_; + +public: + typedef Char char_type; + typedef typename basic_string_view::iterator iterator; + + explicit FMT_CONSTEXPR basic_parse_context(basic_string_view format_str, ErrorHandler eh = ErrorHandler()) + : ErrorHandler(eh) + , format_str_(format_str) + , next_arg_id_(0) + { + } + + // Returns an iterator to the beginning of the format string range being + // parsed. + FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT + { + return format_str_.begin(); + } + + // Returns an iterator past the end of the format string range being parsed. + FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT + { + return format_str_.end(); + } + + // Advances the begin iterator to ``it``. + FMT_CONSTEXPR void advance_to(iterator it) + { + format_str_.remove_prefix(internal::to_unsigned(it - begin())); + } + + // Returns the next argument index. + FMT_CONSTEXPR unsigned next_arg_id(); + + FMT_CONSTEXPR bool check_arg_id(unsigned) + { + if (next_arg_id_ > 0) + { + on_error("cannot switch from automatic to manual argument indexing"); + return false; + } + next_arg_id_ = -1; + return true; + } + void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void on_error(const char *message) + { + ErrorHandler::on_error(message); + } + + FMT_CONSTEXPR ErrorHandler error_handler() const + { + return *this; + } +}; + +typedef basic_parse_context parse_context; +typedef basic_parse_context wparse_context; + +namespace internal { +// A map from argument names to their values for named arguments. +template +class arg_map +{ +private: + FMT_DISALLOW_COPY_AND_ASSIGN(arg_map); + + typedef typename Context::char_type char_type; + + struct entry + { + basic_string_view name; + basic_format_arg arg; + }; + + entry *map_; + unsigned size_; + + void push_back(value val) + { + const internal::named_arg_base &named = val.as_named_arg(); + map_[size_] = entry{named.name, named.template deserialize()}; + ++size_; + } + +public: + arg_map() + : map_(FMT_NULL) + , size_(0) + { + } + void init(const basic_format_args &args); + ~arg_map() + { + delete[] map_; + } + + basic_format_arg find(basic_string_view name) const + { + // The list is unsorted, so just return the first matching name. + for (entry *it = map_, *end = map_ + size_; it != end; ++it) + { + if (it->name == name) + return it->arg; + } + return basic_format_arg(); + } +}; + +template +class context_base +{ +public: + typedef OutputIt iterator; + +private: + basic_parse_context parse_context_; + iterator out_; + basic_format_args args_; + +protected: + typedef Char char_type; + typedef basic_format_arg format_arg; + + context_base(OutputIt out, basic_string_view format_str, basic_format_args ctx_args) + : parse_context_(format_str) + , out_(out) + , args_(ctx_args) + { + } + + // Returns the argument with specified index. + format_arg do_get_arg(unsigned arg_id) + { + format_arg arg = args_.get(arg_id); + if (!arg) + parse_context_.on_error("argument index out of range"); + return arg; + } + + // Checks if manual indexing is used and returns the argument with + // specified index. + format_arg get_arg(unsigned arg_id) + { + return this->parse_context().check_arg_id(arg_id) ? this->do_get_arg(arg_id) : format_arg(); + } + +public: + basic_parse_context &parse_context() + { + return parse_context_; + } + + internal::error_handler error_handler() + { + return parse_context_.error_handler(); + } + + void on_error(const char *message) + { + parse_context_.on_error(message); + } + + // Returns an iterator to the beginning of the output range. + iterator out() + { + return out_; + } + iterator begin() + { + return out_; + } // deprecated + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) + { + out_ = it; + } + + basic_format_args args() const + { + return args_; + } +}; + +// Extracts a reference to the container from back_insert_iterator. +template +inline Container &get_container(std::back_insert_iterator it) +{ + typedef std::back_insert_iterator bi_iterator; + struct accessor : bi_iterator + { + accessor(bi_iterator iter) + : bi_iterator(iter) + { + } + using bi_iterator::container; + }; + return *accessor(it).container; +} +} // namespace internal + +// Formatting context. +template +class basic_format_context : public internal::context_base, Char> +{ +public: + /** The character type for the output. */ + typedef Char char_type; + + // using formatter_type = formatter; + template + struct formatter_type + { + typedef formatter type; + }; + +private: + internal::arg_map map_; + + FMT_DISALLOW_COPY_AND_ASSIGN(basic_format_context); + + typedef internal::context_base base; + typedef typename base::format_arg format_arg; + using base::get_arg; + +public: + using typename base::iterator; + + /** + Constructs a ``basic_format_context`` object. References to the arguments are + stored in the object so make sure they have appropriate lifetimes. + */ + basic_format_context(OutputIt out, basic_string_view format_str, basic_format_args ctx_args) + : base(out, format_str, ctx_args) + { + } + + format_arg next_arg() + { + return this->do_get_arg(this->parse_context().next_arg_id()); + } + format_arg get_arg(unsigned arg_id) + { + return this->do_get_arg(arg_id); + } + + // Checks if manual indexing is used and returns the argument with the + // specified name. + format_arg get_arg(basic_string_view name); +}; + +template +struct buffer_context +{ + typedef basic_format_context>, Char> type; +}; +typedef buffer_context::type format_context; +typedef buffer_context::type wformat_context; + +namespace internal { +template +struct get_type +{ + typedef decltype(make_value(declval::type &>())) value_type; + static const type value = value_type::type_tag; +}; + +template +FMT_CONSTEXPR unsigned long long get_types() +{ + return 0; +} + +template +FMT_CONSTEXPR unsigned long long get_types() +{ + return get_type::value | (get_types() << 4); +} + +template +FMT_CONSTEXPR basic_format_arg make_arg(const T &value) +{ + basic_format_arg arg; + arg.type_ = get_type::value; + arg.value_ = make_value(value); + return arg; +} + +template +inline typename std::enable_if>::type make_arg(const T &value) +{ + return make_value(value); +} + +template +inline typename std::enable_if>::type make_arg(const T &value) +{ + return make_arg(value); +} +} // namespace internal + +/** + \rst + An array of references to arguments. It can be implicitly converted into + `~fmt::basic_format_args` for passing into type-erased formatting functions + such as `~fmt::vformat`. + \endrst + */ +template +class format_arg_store +{ +private: + static const size_t NUM_ARGS = sizeof...(Args); + + // Packed is a macro on MinGW so use IS_PACKED instead. + static const bool IS_PACKED = NUM_ARGS < internal::max_packed_args; + + typedef typename std::conditional, basic_format_arg>::type value_type; + + // If the arguments are not packed, add one more element to mark the end. + static const size_t DATA_SIZE = NUM_ARGS + (IS_PACKED && NUM_ARGS != 0 ? 0 : 1); + value_type data_[DATA_SIZE]; + + friend class basic_format_args; + + static FMT_CONSTEXPR long long get_types() + { + return IS_PACKED ? static_cast(internal::get_types()) : -static_cast(NUM_ARGS); + } + +public: +#if FMT_USE_CONSTEXPR + static constexpr long long TYPES = get_types(); +#else + static const long long TYPES; +#endif + +#if (FMT_GCC_VERSION && FMT_GCC_VERSION <= 405) || (FMT_MSC_VER && FMT_MSC_VER <= 1800) + // Workaround array initialization issues in gcc <= 4.5 and MSVC <= 2013. + format_arg_store(const Args &... args) + { + value_type init[DATA_SIZE] = {internal::make_arg(args)...}; + std::memcpy(data_, init, sizeof(init)); + } +#else + format_arg_store(const Args &... args) + : data_{internal::make_arg(args)...} + { + } +#endif +}; + +#if !FMT_USE_CONSTEXPR +template +const long long format_arg_store::TYPES = get_types(); +#endif + +/** + \rst + Constructs an `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::format_args`. `Context` + can + be omitted in which case it defaults to `~fmt::context`. + \endrst + */ +template +inline format_arg_store make_format_args(const Args &... args) +{ + return format_arg_store(args...); +} + +template +inline format_arg_store make_format_args(const Args &... args) +{ + return format_arg_store(args...); +} + +/** Formatting arguments. */ +template +class basic_format_args +{ +public: + typedef unsigned size_type; + typedef basic_format_arg format_arg; + +private: + // To reduce compiled code size per formatting function call, types of first + // max_packed_args arguments are passed in the types_ field. + unsigned long long 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 format_arg *args_; + }; + + typename internal::type type(unsigned index) const + { + unsigned shift = index * 4; + unsigned long long mask = 0xf; + return static_cast((types_ & (mask << shift)) >> shift); + } + + friend class internal::arg_map; + + void set_data(const internal::value *values) + { + values_ = values; + } + void set_data(const format_arg *args) + { + args_ = args; + } + + format_arg do_get(size_type index) const + { + long long signed_types = static_cast(types_); + if (signed_types < 0) + { + unsigned long long num_args = static_cast(-signed_types); + return index < num_args ? args_[index] : format_arg(); + } + format_arg arg; + if (index > internal::max_packed_args) + return arg; + arg.type_ = type(index); + if (arg.type_ == internal::none_type) + return arg; + internal::value &val = arg.value_; + val = values_[index]; + return arg; + } + +public: + basic_format_args() + : types_(0) + { + } + + /** + \rst + Constructs a `basic_format_args` object from `~fmt::format_arg_store`. + \endrst + */ + template + basic_format_args(const format_arg_store &store) + : types_(static_cast(store.TYPES)) + { + set_data(store.data_); + } + + /** Returns the argument at specified index. */ + format_arg get(size_type index) const + { + format_arg arg = do_get(index); + return arg.type_ == internal::named_arg_type ? arg.value_.as_named_arg().template deserialize() : arg; + } + + unsigned max_size() const + { + long long signed_types = static_cast(types_); + return static_cast(signed_types < 0 ? -signed_types : static_cast(internal::max_packed_args)); + } +}; + +/** An alias to ``basic_format_args``. */ +// It is a separate type rather than a typedef to make symbols readable. +struct format_args : basic_format_args +{ + template + format_args(Args &&... arg) + : basic_format_args(std::forward(arg)...) + { + } +}; +struct wformat_args : basic_format_args +{ + template + wformat_args(Args &&... arg) + : basic_format_args(std::forward(arg)...) + { + } +}; + +namespace internal { +template +struct named_arg_base +{ + basic_string_view name; + + // Serialized value. + mutable char data[sizeof(basic_format_arg)]; + + named_arg_base(basic_string_view nm) + : name(nm) + { + } + + template + basic_format_arg deserialize() const + { + basic_format_arg arg; + std::memcpy(&arg, data, sizeof(basic_format_arg)); + return arg; + } +}; + +template +struct named_arg : named_arg_base +{ + const T &value; + + named_arg(basic_string_view name, const T &val) + : named_arg_base(name) + , value(val) + { + } +}; +} // namespace internal + +/** + \rst + Returns a named argument to be used in a formatting function. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template +inline internal::named_arg arg(string_view name, const T &arg) +{ + return internal::named_arg(name, arg); +} + +template +inline internal::named_arg arg(wstring_view name, const T &arg) +{ + return internal::named_arg(name, arg); +} + +// This function template is deleted intentionally to disable nested named +// arguments as in ``format("{}", arg("a", arg("b", 42)))``. +template +void arg(S, internal::named_arg) FMT_DELETED; + +#ifndef FMT_EXTENDED_COLORS +// color and (v)print_colored are deprecated. +enum color +{ + black, + red, + green, + yellow, + blue, + magenta, + cyan, + white +}; +FMT_API void vprint_colored(color c, string_view format, format_args args); +FMT_API void vprint_colored(color c, wstring_view format, wformat_args args); +template +inline void print_colored(color c, string_view format_str, const Args &... args) +{ + vprint_colored(c, format_str, make_format_args(args...)); +} +template +inline void print_colored(color c, wstring_view format_str, const Args &... args) +{ + vprint_colored(c, format_str, make_format_args(args...)); +} +#endif + +format_context::iterator vformat_to(internal::buffer &buf, string_view format_str, format_args args); +wformat_context::iterator vformat_to(internal::wbuffer &buf, wstring_view format_str, wformat_args args); + +template +struct is_contiguous : std::false_type +{ +}; + +template +struct is_contiguous> : std::true_type +{ +}; + +template +struct is_contiguous> : std::true_type +{ +}; + +/** Formats a string and writes the output to ``out``. */ +template +typename std::enable_if::value, std::back_insert_iterator>::type vformat_to( + std::back_insert_iterator out, string_view format_str, format_args args) +{ + auto &container = internal::get_container(out); + internal::container_buffer buf(container); + vformat_to(buf, format_str, args); + return std::back_inserter(container); +} + +template +typename std::enable_if::value, std::back_insert_iterator>::type vformat_to( + std::back_insert_iterator out, wstring_view format_str, wformat_args args) +{ + auto &container = internal::get_container(out); + internal::container_buffer buf(container); + vformat_to(buf, format_str, args); + return std::back_inserter(container); +} + +std::string vformat(string_view format_str, format_args args); +std::wstring vformat(wstring_view format_str, wformat_args args); + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + #include + std::string message = fmt::format("The answer is {}", 42); + \endrst +*/ +template +inline std::string format(string_view format_str, const Args &... args) +{ + // This should be just + // return vformat(format_str, make_format_args(args...)); + // but gcc has trouble optimizing the latter, so break it down. + format_arg_store as{args...}; + return vformat(format_str, as); +} +template +inline std::wstring format(wstring_view format_str, const Args &... args) +{ + format_arg_store as{args...}; + return vformat(format_str, as); +} + +FMT_API void vprint(std::FILE *f, string_view format_str, format_args args); +FMT_API void vprint(std::FILE *f, wstring_view format_str, wformat_args args); + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + fmt::print(stderr, "Don't {}!", "panic"); + \endrst + */ +template +inline void print(std::FILE *f, string_view format_str, const Args &... args) +{ + format_arg_store as(args...); + vprint(f, format_str, as); +} +/** + Prints formatted data to the file *f* which should be in wide-oriented mode + set + via ``fwide(f, 1)`` or ``_setmode(_fileno(f), _O_U8TEXT)`` on Windows. + */ +template +inline void print(std::FILE *f, wstring_view format_str, const Args &... args) +{ + format_arg_store as(args...); + vprint(f, format_str, as); +} + +FMT_API void vprint(string_view format_str, format_args args); +FMT_API void vprint(wstring_view format_str, wformat_args args); + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + fmt::print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +template +inline void print(string_view format_str, const Args &... args) +{ + format_arg_store as{args...}; + vprint(format_str, as); +} + +template +inline void print(wstring_view format_str, const Args &... args) +{ + format_arg_store as(args...); + vprint(format_str, as); +} +FMT_END_NAMESPACE + +#endif // FMT_CORE_H_ diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format-inl.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format-inl.h new file mode 100644 index 00000000..1aba8c6e --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format-inl.h @@ -0,0 +1,578 @@ +// Formatting library for C++ +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_FORMAT_INL_H_ +#define FMT_FORMAT_INL_H_ + +#include "format.h" + +#include + +#include +#include +#include +#include +#include +#include // for std::ptrdiff_t +#include + +#if defined(_WIN32) && defined(__MINGW32__) +#include +#endif + +#if FMT_USE_WINDOWS_H +#if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif +#if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) +#include +#else +#define NOMINMAX +#include +#undef NOMINMAX +#endif +#endif + +#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. +inline fmt::internal::null<> strerror_r(int, char *, ...) +{ + return fmt::internal::null<>(); +} +inline fmt::internal::null<> strerror_s(char *, std::size_t, ...) +{ + return fmt::internal::null<>(); +} + +FMT_BEGIN_NAMESPACE + +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) + +typedef void (*FormatFunc)(internal::buffer &, int, string_view); + +// 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 != FMT_NULL && buffer_size != 0, "invalid buffer"); + + class dispatcher + { + private: + int error_code_; + char *&buffer_; + std::size_t buffer_size_; + + // A noop assignment operator to avoid bogus warnings. + void operator=(const dispatcher &) {} + + // 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: + dispatcher(int err_code, char *&buf, std::size_t buf_size) + : error_code_(err_code) + , buffer_(buf) + , buffer_size_(buf_size) + { + } + + int run() + { + return handle(strerror_r(error_code_, buffer_, buffer_size_)); + } + }; + return dispatcher(error_code, buffer, buffer_size).run(); +} + +void format_error_code(internal::buffer &out, int error_code, string_view 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.resize(0); + 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::int_traits::main_type main_type; + main_type 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); + writer w(out); + if (message.size() <= inline_buffer_size - error_code_size) + { + w.write(message); + w.write(SEP); + } + w.write(ERROR_STR); + w.write(error_code); + assert(out.size() <= inline_buffer_size); +} + +void report_error(FormatFunc func, int error_code, string_view message) FMT_NOEXCEPT +{ + memory_buffer 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 + +class locale +{ +private: + std::locale locale_; + +public: + explicit locale(std::locale loc = std::locale()) + : locale_(loc) + { + } + std::locale get() + { + return locale_; + } +}; + +template +FMT_FUNC Char internal::thousands_sep(locale_provider *lp) +{ + std::locale loc = lp ? lp->locale().get() : std::locale(); + return std::use_facet>(loc).thousands_sep(); +} + +FMT_FUNC void system_error::init(int err_code, string_view format_str, format_args args) +{ + error_code_ = err_code; + memory_buffer buffer; + format_system_error(buffer, err_code, vformat(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(to_string(buffer)); +} + +namespace internal { +template +int char_traits::format_float(char *buffer, std::size_t size, const char *format, int precision, T value) +{ + return precision < 0 ? FMT_SNPRINTF(buffer, size, format, value) : FMT_SNPRINTF(buffer, size, format, precision, value); +} + +template +int char_traits::format_float(wchar_t *buffer, std::size_t size, const wchar_t *format, int precision, T value) +{ + return precision < 0 ? FMT_SWPRINTF(buffer, size, format, value) : FMT_SWPRINTF(buffer, size, format, precision, value); +} + +template +const char basic_data::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 basic_data::POWERS_OF_10_32[] = {0, FMT_POWERS_OF_10(1)}; + +template +const uint64_t basic_data::POWERS_OF_10_64[] = {0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ull), 10000000000000000000ull}; + +// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. +// These are generated by support/compute-powers.py. +template +const uint64_t basic_data::POW10_SIGNIFICANDS[] = {0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xcf42894a5dce35ea, + 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, 0x8dd01fad907ffc3c, + 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, 0xc21094364dfb5637, + 0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b, + 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8, + 0x87625f056c7c4a8b, 0xc9bcff6034c13053, 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94, + 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xaa242499697392d3, + 0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, 0xe8d4a51000000000, + 0xad78ebc5ac620000, 0x813f3978f8940984, 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, 0x9f4f2726179a2245, + 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, 0xda01ee641a708dea, + 0xa26da3999aef774a, 0xf209787bb47d6b85, 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0x952ab45cfa97a0b3, + 0xde469fbd99a05fe3, 0xa59bc234db398c25, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, 0xcc20ce9bd35c78a5, + 0x98165af37b2153df, 0xe2a0b5dc971f303a, 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, 0x8bab8eefb6409c1a, + 0xd01fef10a657842c, 0x9b10a4e5e9913129, 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, 0xbf21e44003acdd2d, + 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b}; + +// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding +// to significands above. +template +const int16_t basic_data::POW10_EXPONENTS[] = {-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -927, -901, + -874, -847, -821, -794, -768, -741, -715, -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, -343, -316, + -289, -263, -236, -210, -183, -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, 375, + 402, 428, 455, 481, 508, 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; + +template +const char basic_data::RESET_COLOR[] = "\x1b[0m"; +template +const wchar_t basic_data::WRESET_COLOR[] = L"\x1b[0m"; + +FMT_FUNC fp operator*(fp x, fp y) +{ + // Multiply 32-bit parts of significands. + uint64_t mask = (1ULL << 32) - 1; + uint64_t a = x.f >> 32, b = x.f & mask; + uint64_t c = y.f >> 32, d = y.f & mask; + uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d; + // Compute mid 64-bit of result and round. + uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); + return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64); +} + +FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) +{ + const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10) + int index = static_cast(std::ceil((min_exponent + fp::significand_size - 1) * one_over_log2_10)); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between two consecutive decimal exponents in cached powers of + // 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]); +} +} // namespace internal + +#if FMT_USE_WINDOWS_H + +FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) +{ + static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; + if (s.size() > INT_MAX) + FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG)); + int s_size = static_cast(s.size()); + if (s_size == 0) + { + // MultiByteToWideChar does not support zero length, handle separately. + buffer_.resize(1); + buffer_[0] = 0; + return; + } + + int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0); + if (length == 0) + FMT_THROW(windows_error(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(windows_error(GetLastError(), ERROR_MSG)); + buffer_[length] = 0; +} + +FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) +{ + if (int error_code = convert(s)) + { + FMT_THROW(windows_error(error_code, "cannot convert string from UTF-16 to UTF-8")); + } +} + +FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) +{ + if (s.size() > INT_MAX) + return ERROR_INVALID_PARAMETER; + int s_size = static_cast(s.size()); + if (s_size == 0) + { + // WideCharToMultiByte does not support zero length, handle separately. + buffer_.resize(1); + buffer_[0] = 0; + return 0; + } + + int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL); + if (length == 0) + return GetLastError(); + buffer_.resize(length + 1); + length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL); + if (length == 0) + return GetLastError(); + buffer_[length] = 0; + return 0; +} + +FMT_FUNC void windows_error::init(int err_code, string_view format_str, format_args args) +{ + error_code_ = err_code; + memory_buffer buffer; + internal::format_windows_error(buffer, err_code, vformat(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(to_string(buffer)); +} + +FMT_FUNC void internal::format_windows_error(internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT +{ + FMT_TRY + { + wmemory_buffer buf; + buf.resize(inline_buffer_size); + for (;;) + { + wchar_t *system_message = &buf[0]; + int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FMT_NULL, error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message, static_cast(buf.size()), FMT_NULL); + if (result != 0) + { + utf16_to_utf8 utf8_message; + if (utf8_message.convert(system_message) == ERROR_SUCCESS) + { + writer w(out); + w.write(message); + w.write(": "); + w.write(utf8_message); + return; + } + break; + } + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + break; // Can't get error message, report error code instead. + buf.resize(buf.size() * 2); + } + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +#endif // FMT_USE_WINDOWS_H + +FMT_FUNC void format_system_error(internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT +{ + FMT_TRY + { + memory_buffer buf; + buf.resize(inline_buffer_size); + for (;;) + { + char *system_message = &buf[0]; + int result = safe_strerror(error_code, system_message, buf.size()); + if (result == 0) + { + writer w(out); + w.write(message); + w.write(": "); + w.write(system_message); + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buf.resize(buf.size() * 2); + } + } + FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +template +void basic_fixed_buffer::grow(std::size_t) +{ + FMT_THROW(std::runtime_error("buffer overflow")); +} + +FMT_FUNC void internal::error_handler::on_error(const char *message) +{ + FMT_THROW(format_error(message)); +} + +FMT_FUNC void report_system_error(int error_code, fmt::string_view message) FMT_NOEXCEPT +{ + report_error(format_system_error, error_code, message); +} + +#if FMT_USE_WINDOWS_H +FMT_FUNC void report_windows_error(int error_code, fmt::string_view message) FMT_NOEXCEPT +{ + report_error(internal::format_windows_error, error_code, message); +} +#endif + +FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) +{ + memory_buffer buffer; + vformat_to(buffer, format_str, args); + std::fwrite(buffer.data(), 1, buffer.size(), f); +} + +FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) +{ + wmemory_buffer buffer; + vformat_to(buffer, format_str, args); + std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f); +} + +FMT_FUNC void vprint(string_view format_str, format_args args) +{ + vprint(stdout, format_str, args); +} + +FMT_FUNC void vprint(wstring_view format_str, wformat_args args) +{ + vprint(stdout, format_str, args); +} + +#ifndef FMT_EXTENDED_COLORS +FMT_FUNC void vprint_colored(color c, string_view format, format_args args) +{ + char escape[] = "\x1b[30m"; + escape[3] = static_cast('0' + c); + std::fputs(escape, stdout); + vprint(format, args); + std::fputs(internal::data::RESET_COLOR, stdout); +} + +FMT_FUNC void vprint_colored(color c, wstring_view format, wformat_args args) +{ + wchar_t escape[] = L"\x1b[30m"; + escape[3] = static_cast('0' + c); + std::fputws(escape, stdout); + vprint(format, args); + std::fputws(internal::data::WRESET_COLOR, stdout); +} +#else +namespace internal { +FMT_CONSTEXPR void to_esc(uint8_t c, char out[], int offset) +{ + out[offset + 0] = static_cast('0' + c / 100); + out[offset + 1] = static_cast('0' + c / 10 % 10); + out[offset + 2] = static_cast('0' + c % 10); +} +} // namespace internal + +FMT_FUNC void vprint_rgb(rgb fd, string_view format, format_args args) +{ + char escape_fd[] = "\x1b[38;2;000;000;000m"; + internal::to_esc(fd.r, escape_fd, 7); + internal::to_esc(fd.g, escape_fd, 11); + internal::to_esc(fd.b, escape_fd, 15); + + std::fputs(escape_fd, stdout); + vprint(format, args); + std::fputs(internal::data::RESET_COLOR, stdout); +} + +FMT_FUNC void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args) +{ + char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color + char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color + internal::to_esc(fd.r, escape_fd, 7); + internal::to_esc(fd.g, escape_fd, 11); + internal::to_esc(fd.b, escape_fd, 15); + + internal::to_esc(bg.r, escape_bg, 7); + internal::to_esc(bg.g, escape_bg, 11); + internal::to_esc(bg.b, escape_bg, 15); + + std::fputs(escape_fd, stdout); + std::fputs(escape_bg, stdout); + vprint(format, args); + std::fputs(internal::data::RESET_COLOR, stdout); +} +#endif + +FMT_FUNC locale locale_provider::locale() +{ + return fmt::locale(); +} + +FMT_END_NAMESPACE + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // FMT_FORMAT_INL_H_ diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format.cc b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format.cc deleted file mode 100644 index 2d236bc6..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format.cc +++ /dev/null @@ -1,495 +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. - */ - -#include "format.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(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) -# define WIN32_LEAN_AND_MEAN -# endif -# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) -# include -# else -# define NOMINMAX -# include -# undef NOMINMAX -# endif -#endif - -#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. -FMT_MAYBE_UNUSED -static inline fmt::internal::Null<> strerror_r(int, char *, ...) { - return fmt::internal::Null<>(); -} -FMT_MAYBE_UNUSED -static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { - return fmt::internal::Null<>(); -} - -namespace fmt { - -FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {} -FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {} -FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {} - -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 != FMT_NULL && 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; - } - -#ifdef __c2__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - - // Fallback to strerror if strerror_r and strerror_s are not available. - int fallback(internal::Null<>) { - errno = 0; - buffer_ = strerror(error_code_); - return errno; - } - -#ifdef __c2__ -# pragma clang diagnostic pop -#endif - - public: - StrError(int err_code, char *&buf, std::size_t buf_size) - : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} - - int run() { - 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 - -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, FMT_NULL, 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, FMT_NULL, 0, FMT_NULL, FMT_NULL); - if (length == 0) - return GetLastError(); - buffer_.resize(length + 1); - length = WideCharToMultiByte( - CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL); - 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, - FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - system_message, static_cast(buffer.size()), FMT_NULL); - 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::FixedBuffer::grow(std::size_t) { - FMT_THROW(std::runtime_error("buffer overflow")); -} - -FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg( - unsigned arg_index, const char *&error) { - internal::Arg arg = args_[arg_index]; - switch (arg.type) { - case internal::Arg::NONE: - error = "argument index out of range"; - break; - case internal::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); -} - -#ifndef FMT_HEADER_ONLY - -template struct internal::BasicData; - -// Explicit instantiations for char. - -template void internal::FixedBuffer::grow(std::size_t); - -template FMT_API int internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, double value); - -template FMT_API 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 FMT_API int internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, double value); - -template FMT_API 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/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format.h index b4295cd7..60001f36 100644 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format.h +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/format.h @@ -1,7 +1,7 @@ /* Formatting library for C++ - Copyright (c) 2012 - 2016, Victor Zverovich + Copyright (c) 2012 - present, Victor Zverovich All rights reserved. Redistribution and use in source and binary forms, with or without @@ -28,116 +28,51 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#define FMT_INCLUDE +#include #include -#include #include -#include #include #include #include #include -#include -#include // for std::pair -#include -#undef FMT_INCLUDE - -// The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 40100 - -#if defined(__has_include) -#define FMT_HAS_INCLUDE(x) __has_include(x) -#else -#define FMT_HAS_INCLUDE(x) 0 -#endif - -#if (FMT_HAS_INCLUDE() && __cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) -#include -#define FMT_HAS_STRING_VIEW 1 -#else -#define FMT_HAS_STRING_VIEW 0 -#endif - -#if defined _SECURE_SCL && _SECURE_SCL -#define FMT_SECURE_SCL _SECURE_SCL -#else -#define FMT_SECURE_SCL 0 -#endif - -#if FMT_SECURE_SCL -#include -#endif +#include -#ifdef _MSC_VER -#define FMT_MSC_VER _MSC_VER +#ifdef __clang__ +#define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) #else -#define FMT_MSC_VER 0 +#define FMT_CLANG_VERSION 0 #endif -#if FMT_MSC_VER && FMT_MSC_VER <= 1500 -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -typedef __int64 intmax_t; +#ifdef __INTEL_COMPILER +#define FMT_ICC_VERSION __INTEL_COMPILER +#elif defined(__ICL) +#define FMT_ICC_VERSION __ICL #else -#include +#define FMT_ICC_VERSION 0 #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 +#include "core.h" -#ifdef __GNUC__ -#define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -#define FMT_GCC_EXTENSION __extension__ -#if FMT_GCC_VERSION >= 406 +#if FMT_GCC_VERSION >= 406 || FMT_CLANG_VERSION #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_VERSION 0 -#define FMT_GCC_EXTENSION -#define FMT_HAS_GXX_CXX11 0 -#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) -#define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) -#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__) +#ifdef _SECURE_SCL +#define FMT_SECURE_SCL _SECURE_SCL +#else +#define FMT_SECURE_SCL 0 #endif -#ifdef __has_feature -#define FMT_HAS_FEATURE(x) __has_feature(x) -#else -#define FMT_HAS_FEATURE(x) 0 +#if FMT_SECURE_SCL +#include #endif #ifdef __has_builtin @@ -146,184 +81,78 @@ typedef __int64 intmax_t; #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 - -#if FMT_HAS_CPP_ATTRIBUTE(maybe_unused) -#define FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED -// VC++ 1910 support /std: option and that will set _MSVC_LANG macro -// Clang with Microsoft CodeGen doesn't define _MSVC_LANG macro -#elif defined(_MSVC_LANG) && _MSVC_LANG > 201402 && _MSC_VER >= 1910 -#define FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED -#endif - -#ifdef FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED -#define FMT_MAYBE_UNUSED [[maybe_unused]] -// g++/clang++ also support [[gnu::unused]]. However, we don't use it. -#elif defined(__GNUC__) -#define FMT_MAYBE_UNUSED __attribute__((unused)) -#else -#define FMT_MAYBE_UNUSED -#endif - -// Use the compiler's attribute noreturn -#if defined(__MINGW32__) || defined(__MINGW64__) -#define FMT_NORETURN __attribute__((noreturn)) -#elif FMT_HAS_CPP_ATTRIBUTE(noreturn) && __cplusplus >= 201103L -#define FMT_NORETURN [[noreturn]] -#else -#define FMT_NORETURN -#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 __cplusplus >= 201103L || FMT_MSC_VER >= 1700 -#define FMT_USE_ALLOCATOR_TRAITS 1 -#else -#define FMT_USE_ALLOCATOR_TRAITS 0 -#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 +#ifdef __GNUC_LIBSTD__ +#define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) #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 - -#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 -#define FMT_DETECTED_NOEXCEPT noexcept +#if FMT_MSC_VER +FMT_BEGIN_NAMESPACE +namespace internal { +template +inline void do_throw(const Exception &x) +{ + // Silence unreachable code warnings in MSVC because these are nearly + // impossible to fix in a generic code. + volatile bool b = true; + if (b) + throw x; +} +} // namespace internal +FMT_END_NAMESPACE +#define FMT_THROW(x) fmt::internal::do_throw(x) #else -#define FMT_DETECTED_NOEXCEPT throw() +#define FMT_THROW(x) throw x #endif - -#ifndef FMT_NOEXCEPT -#if FMT_EXCEPTIONS -#define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT #else -#define FMT_NOEXCEPT -#endif +#define FMT_THROW(x) \ + do \ + { \ + static_cast(sizeof(x)); \ + assert(false); \ + } while (false); #endif - -// This is needed because GCC still uses throw() in its headers when exceptions -// are disabled. -#if FMT_GCC_VERSION -#define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT -#else -#define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT #endif -#ifndef FMT_OVERRIDE -#if (defined(FMT_USE_OVERRIDE) && FMT_USE_OVERRIDE) || FMT_HAS_FEATURE(cxx_override) || (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ - FMT_MSC_VER >= 1900 -#define FMT_OVERRIDE override +#ifndef FMT_USE_USER_DEFINED_LITERALS +// For Intel's compiler both it and the system gcc/msc must support UDLs. +#if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || FMT_MSC_VER >= 1900) && (!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1500) +#define FMT_USE_USER_DEFINED_LITERALS 1 #else -#define FMT_OVERRIDE +#define FMT_USE_USER_DEFINED_LITERALS 0 #endif #endif -#ifndef FMT_NULL -#if FMT_HAS_FEATURE(cxx_nullptr) || (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600 -#define FMT_NULL nullptr +#if FMT_USE_USER_DEFINED_LITERALS && !defined(FMT_ICC_VERSION) && \ + ((FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L) || (defined(FMT_CLANG_VERSION) && FMT_CLANG_VERSION >= 304)) +#define FMT_UDL_TEMPLATE 1 #else -#define FMT_NULL NULL -#endif +#define FMT_UDL_TEMPLATE 0 #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 +#ifndef FMT_USE_EXTERN_TEMPLATES +#ifndef FMT_HEADER_ONLY +#define FMT_USE_EXTERN_TEMPLATES ((FMT_CLANG_VERSION >= 209 && __cplusplus >= 201103L) || (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) #else -#define FMT_DELETED_OR_UNDEFINED -#define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName &); \ - TypeName &operator=(const TypeName &) +#define FMT_USE_EXTERN_TEMPLATES 0 #endif - -#ifndef FMT_USE_DEFAULTED_FUNCTIONS -#define FMT_USE_DEFAULTED_FUNCTIONS 0 #endif -#ifndef FMT_DEFAULTED_COPY_CTOR -#if FMT_USE_DEFAULTED_FUNCTIONS || FMT_HAS_FEATURE(cxx_defaulted_functions) || (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || \ - FMT_MSC_VER >= 1800 -#define FMT_DEFAULTED_COPY_CTOR(TypeName) TypeName(const TypeName &) = default; +#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || FMT_MSC_VER >= 1600 +#define FMT_USE_TRAILING_RETURN 1 #else -#define FMT_DEFAULTED_COPY_CTOR(TypeName) -#endif +#define FMT_USE_TRAILING_RETURN 0 #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. -#if 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) -#define FMT_USE_USER_DEFINED_LITERALS 1 +#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_rvalue_references) || FMT_MSC_VER >= 1600 +#define FMT_USE_RVALUE_REFERENCES 1 #else -#define FMT_USE_USER_DEFINED_LITERALS 0 -#endif -#endif - -#ifndef FMT_USE_EXTERN_TEMPLATES -#define FMT_USE_EXTERN_TEMPLATES (FMT_CLANG_VERSION >= 209 || (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) -#endif - -#ifdef FMT_HEADER_ONLY -// If header only do not use extern templates. -#undef FMT_USE_EXTERN_TEMPLATES -#define FMT_USE_EXTERN_TEMPLATES 0 +#define FMT_USE_RVALUE_REFERENCES 0 #endif -#ifndef FMT_ASSERT -#define FMT_ASSERT(condition, message) assert((condition) && message) +#ifndef FMT_USE_GRISU +#define FMT_USE_GRISU 0 #endif // __builtin_clz is broken in clang with Microsoft CodeGen: @@ -338,16 +167,22 @@ typedef __int64 intmax_t; #endif #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. +// A workaround for gcc 4.4 that doesn't support union members with ctors. +#if (FMT_GCC_VERSION && FMT_GCC_VERSION <= 404) || (FMT_MSC_VER && FMT_MSC_VER <= 1800) +#define FMT_UNION struct +#else +#define FMT_UNION union +#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) && !defined(_MANAGED) #include // _BitScanReverse, _BitScanReverse64 -namespace fmt { +FMT_BEGIN_NAMESPACE namespace internal { -// avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning +// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. #ifndef __clang__ #pragma intrinsic(_BitScanReverse) #endif @@ -357,15 +192,14 @@ inline uint32_t clz(uint32_t x) _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. +// 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) -// avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning #if defined(_WIN64) && !defined(__clang__) #pragma intrinsic(_BitScanReverse64) #endif @@ -385,20 +219,66 @@ inline uint32_t clzll(uint64_t 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. +// 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) } // namespace internal -} // namespace fmt +FMT_END_NAMESPACE #endif -namespace fmt { +FMT_BEGIN_NAMESPACE namespace internal { -struct DummyInt + +// An equivalent of `*reinterpret_cast(&source)` that doesn't produce +// undefined behavior (e.g. due to type aliasing). +// Example: uint64_t d = bit_cast(2.718); +template +inline Dest bit_cast(const Source &source) +{ + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + Dest dest; + std::memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +// An implementation of begin and end for pre-C++11 compilers such as gcc 4. +template +FMT_CONSTEXPR auto begin(const C &c) -> decltype(c.begin()) +{ + return c.begin(); +} +template +FMT_CONSTEXPR T *begin(T (&array)[N]) FMT_NOEXCEPT +{ + return array; +} +template +FMT_CONSTEXPR auto end(const C &c) -> decltype(c.end()) +{ + return c.end(); +} +template +FMT_CONSTEXPR T *end(T (&array)[N]) FMT_NOEXCEPT +{ + return array + N; +} + +// For std::result_of in gcc 4.4. +template +struct function +{ + template + struct result + { + typedef Result type; + }; +}; + +struct dummy_int { int data[2]; operator int() const @@ -406,33 +286,124 @@ struct DummyInt return 0; } }; -typedef std::numeric_limits FPUtil; +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(...) +inline dummy_int signbit(...) +{ + return dummy_int(); +} +inline dummy_int _ecvt_s(...) +{ + return dummy_int(); +} +inline dummy_int isinf(...) { - return DummyInt(); + return dummy_int(); } -inline DummyInt _ecvt_s(...) +inline dummy_int _finite(...) { - return DummyInt(); + return dummy_int(); } -inline DummyInt isinf(...) +inline dummy_int isnan(...) { - return DummyInt(); + return dummy_int(); } -inline DummyInt _finite(...) +inline dummy_int _isnan(...) { - return DummyInt(); + return dummy_int(); } -inline DummyInt isnan(...) + +// A handmade floating-point number f * pow(2, e). +class fp +{ +private: + typedef uint64_t significand_type; + + // All sizes are in bits. + static FMT_CONSTEXPR_DECL const int char_size = std::numeric_limits::digits; + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + static FMT_CONSTEXPR_DECL const int double_significand_size = std::numeric_limits::digits - 1; + static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = 1ull << double_significand_size; + +public: + significand_type f; + int e; + + static FMT_CONSTEXPR_DECL const int significand_size = sizeof(significand_type) * char_size; + + fp(uint64_t f, int e) + : f(f) + , e(e) + { + } + + // Constructs fp from an IEEE754 double. It is a template to prevent compile + // errors on platforms where double is not IEEE754. + template + explicit fp(Double d) + { + // Assume double is in the format [sign][exponent][significand]. + typedef std::numeric_limits limits; + const int double_size = sizeof(Double) * char_size; + const int exponent_size = double_size - double_significand_size - 1; // -1 for sign + const uint64_t significand_mask = implicit_bit - 1; + const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask; + const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; + auto u = bit_cast(d); + auto biased_e = (u & exponent_mask) >> double_significand_size; + f = u & significand_mask; + if (biased_e != 0) + f += implicit_bit; + else + biased_e = 1; // Subnormals use biased exponent 1 (min exponent). + e = static_cast(biased_e - exponent_bias - double_significand_size); + } + + // Normalizes the value converted from double and multiplied by (1 << SHIFT). + template + void normalize() + { + // Handle subnormals. + auto shifted_implicit_bit = implicit_bit << SHIFT; + while ((f & shifted_implicit_bit) == 0) + { + f <<= 1; + --e; + } + // Subtract 1 to account for hidden bit. + auto offset = significand_size - double_significand_size - SHIFT - 1; + f <<= offset; + e -= offset; + } +}; + +// Returns an fp number representing x - y. Result may not be normalized. +inline fp operator-(fp x, fp y) { - return DummyInt(); + FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands"); + return fp(x.f - y.f, x.e); } -inline DummyInt _isnan(...) + +// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest +// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be +// normalized. +fp operator*(fp x, fp y); + +// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its +// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3. +fp get_cached_power(int min_exponent, int &pow10_exponent); + +template +typename Allocator::value_type *allocate(Allocator &alloc, std::size_t n) { - return DummyInt(); +#if __cplusplus >= 201103L || FMT_MSC_VER >= 1700 + return std::allocator_traits::allocate(alloc, n); +#else + return alloc.allocate(n); +#endif } // A helper function to suppress bogus "conditional expression is constant" @@ -443,7 +414,7 @@ inline T const_check(T value) return value; } } // namespace internal -} // namespace fmt +FMT_END_NAMESPACE namespace std { // Standard permits specialization of std::numeric_limits. This specialization @@ -451,7 +422,7 @@ namespace std { // 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 +class numeric_limits : public std::numeric_limits { public: // Portable version of isinf. @@ -461,10 +432,8 @@ class numeric_limits : public std::numeric_limits 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))) - { + if (const_check(sizeof(isinf(x)) != sizeof(dummy_int))) return isinf(x) != 0; - } return !_finite(static_cast(x)); } @@ -473,10 +442,8 @@ class numeric_limits : public std::numeric_limits static bool isnotanumber(T x) { using namespace fmt::internal; - if (const_check(sizeof(isnan(x)) == sizeof(bool) || sizeof(isnan(x)) == sizeof(int))) - { + if (const_check(sizeof(isnan(x)) != sizeof(fmt::internal::dummy_int))) return isnan(x) != 0; - } return _isnan(static_cast(x)) != 0; } @@ -484,10 +451,8 @@ class numeric_limits : public std::numeric_limits static bool isnegative(double x) { using namespace fmt::internal; - if (const_check(sizeof(signbit(x)) == sizeof(bool) || sizeof(signbit(x)) == sizeof(int))) - { + if (const_check(sizeof(signbit(x)) != sizeof(fmt::internal::dummy_int))) return signbit(x) != 0; - } if (x < 0) return true; if (!isnotanumber(x)) @@ -500,483 +465,232 @@ class numeric_limits : public std::numeric_limits }; } // 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; - -struct FormatSpec; - -template -class BasicPrintfArgFormatter; - -template> -class BasicFormatter; - -/** - \rst - A string reference. It can be constructed from a C string or - ``std::basic_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); +FMT_BEGIN_NAMESPACE +template +class basic_writer; - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class BasicStringRef +template +class output_range { private: - const Char *data_; - std::size_t size_; + OutputIt it_; + + // Unused yet. + typedef void sentinel; + sentinel end() const; 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) - { - } + typedef OutputIt iterator; + typedef T value_type; - /** - \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)) + explicit output_range(OutputIt it) + : it_(it) { } - - /** - \rst - Constructs a string reference from a ``std::basic_string`` object. - \endrst - */ - template - BasicStringRef(const std::basic_string, Allocator> &s) - : data_(s.c_str()) - , size_(s.size()) + OutputIt begin() const { + return it_; } +}; -#if FMT_HAS_STRING_VIEW - /** - \rst - Constructs a string reference from a ``std::basic_string_view`` object. - \endrst - */ - BasicStringRef(const std::basic_string_view> &s) - : data_(s.data()) - , size_(s.size()) - { - } +// A range where begin() returns back_insert_iterator. +template +class back_insert_range : public output_range> +{ + typedef output_range> base; - /** - \rst - Converts a string reference to an ``std::string_view`` object. - \endrst - */ - explicit operator std::basic_string_view() const FMT_NOEXCEPT - { - return std::basic_string_view(data_, size_); - } -#endif +public: + typedef typename Container::value_type value_type; - /** - \rst - Converts a string reference to an ``std::string`` object. - \endrst - */ - std::basic_string to_string() const + back_insert_range(Container &c) + : base(std::back_inserter(c)) { - return std::basic_string(data_, size_); } - - /** Returns a pointer to the string data. */ - const Char *data() const + back_insert_range(typename base::iterator it) + : base(it) { - return data_; } +}; - /** Returns the string size. */ - std::size_t size() const - { - return size_; - } +typedef basic_writer> writer; +typedef basic_writer> wwriter; - // Lexicographically compare this string reference to other. - int compare(BasicStringRef other) const +/** A formatting error such as invalid format string. */ +class format_error : public std::runtime_error +{ +public: + explicit format_error(const char *message) + : std::runtime_error(message) { - 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) + explicit format_error(const std::string &message) + : std::runtime_error(message) { - 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::basic_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); +namespace internal { - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class BasicCStringRef +#if FMT_SECURE_SCL +template +struct checked { -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 a ``std::basic_string`` object. - \endrst - */ - template - BasicCStringRef(const std::basic_string, Allocator> &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(const FormatError &ferr) - : std::runtime_error(ferr) - { - } - FMT_API ~FormatError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; + typedef stdext::checked_array_iterator type; }; -namespace internal { - -// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. +// Make a checked iterator to avoid warnings on MSVC. 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) +inline stdext::checked_array_iterator make_checked(T *p, std::size_t size) { - FMT_ASSERT(value >= 0, "negative value"); - return static_cast::Type>(value); + return {p, size}; } - -// 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. +#else template -inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) +struct checked { - return stdext::checked_array_iterator(ptr, size); -} -#else + typedef T *type; +}; template -inline T *make_ptr(T *ptr, std::size_t) +inline T *make_checked(T *p, std::size_t) { - return ptr; + return p; } #endif -} // namespace internal -/** - \rst - A buffer supporting a subset of ``std::vector``'s operations. - \endrst - */ template -class Buffer +template +void basic_buffer::append(const U *begin, const U *end) { -private: - FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); - -protected: - T *ptr_; - std::size_t size_; - std::size_t capacity_; - - Buffer(T *ptr = FMT_NULL, std::size_t capacity = 0) - : ptr_(ptr) - , size_(0) - , capacity_(capacity) - { - } + std::size_t new_size = size_ + internal::to_unsigned(end - begin); + reserve(new_size); + std::uninitialized_copy(begin, end, internal::make_checked(ptr_, capacity_) + size_); + size_ = new_size; +} +} // namespace internal - /** - \rst - Increases the buffer capacity to hold at least *size* elements updating - ``ptr_`` and ``capacity_``. - \endrst - */ - virtual void grow(std::size_t size) = 0; +// A wrapper around std::locale used to reduce compile times since +// is very heavy. +class locale; +class locale_provider +{ public: - virtual ~Buffer() {} - - /** Returns the size of this buffer. */ - std::size_t size() const - { - return size_; - } + virtual ~locale_provider() {} + virtual fmt::locale locale(); +}; - /** Returns the capacity of this buffer. */ - std::size_t capacity() const - { - return capacity_; - } +// The number of characters to store in the basic_memory_buffer object itself +// to avoid dynamic memory allocation. +enum +{ + inline_buffer_size = 500 +}; - /** - 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 + A dynamically growing memory buffer for trivially copyable/constructible types + with the first ``SIZE`` elements stored in the object itself. - /** - \rst - Reserves space to store at least *capacity* elements. - \endrst - */ - void reserve(std::size_t capacity) - { - if (capacity > capacity_) - grow(capacity); - } + You can use one of the following typedefs for common character types: - void clear() FMT_NOEXCEPT - { - size_ = 0; - } + +----------------+------------------------------+ + | Type | Definition | + +================+==============================+ + | memory_buffer | basic_memory_buffer | + +----------------+------------------------------+ + | wmemory_buffer | basic_memory_buffer | + +----------------+------------------------------+ - void push_back(const T &value) - { - if (size_ == capacity_) - grow(size_ + 1); - ptr_[size_++] = value; - } + **Example**:: - /** Appends data to the end of the buffer. */ - template - void append(const U *begin, const U *end); + fmt::memory_buffer out; + format_to(out, "The answer is {}.", 42); - T &operator[](std::size_t index) - { - return ptr_[index]; - } - const T &operator[](std::size_t index) const - { - return ptr_[index]; - } -}; + This will write the following output to the ``out`` object: -template -template -void Buffer::append(const U *begin, const U *end) -{ - FMT_ASSERT(end >= begin, "negative value"); - std::size_t new_size = size_ + static_cast(end - begin); - if (new_size > capacity_) - grow(new_size); - std::uninitialized_copy(begin, end, internal::make_ptr(ptr_, capacity_) + size_); - size_ = new_size; -} + .. code-block:: none -namespace internal { + The answer is 42. -// 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 + The output can be converted to an ``std::string`` with ``to_string(out)``. + \endrst + */ +template> +class basic_memory_buffer : private Allocator, public internal::basic_buffer { private: - T data_[SIZE]; + T store_[SIZE]; // Deallocate memory allocated by the buffer. void deallocate() { - if (this->ptr_ != data_) - Allocator::deallocate(this->ptr_, this->capacity_); + T *data = this->data(); + if (data != store_) + Allocator::deallocate(data, this->capacity()); } protected: void grow(std::size_t size) FMT_OVERRIDE; public: - explicit MemoryBuffer(const Allocator &alloc = Allocator()) + explicit basic_memory_buffer(const Allocator &alloc = Allocator()) : Allocator(alloc) - , Buffer(data_, SIZE) { + this->set(store_, SIZE); } - ~MemoryBuffer() FMT_OVERRIDE + ~basic_memory_buffer() { deallocate(); } -#if FMT_USE_RVALUE_REFERENCES private: // Move data from other to this buffer. - void move(MemoryBuffer &other) + void move(basic_memory_buffer &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_) + T *data = other.data(); + std::size_t size = other.size(), capacity = other.capacity(); + if (data == other.store_) { - this->ptr_ = data_; - std::uninitialized_copy(other.data_, other.data_ + this->size_, make_ptr(data_, this->capacity_)); + this->set(store_, capacity); + std::uninitialized_copy(other.store_, other.store_ + size, internal::make_checked(store_, capacity)); } else { - this->ptr_ = other.ptr_; + this->set(data, capacity); // Set pointer to the inline array so that delete is not called // when deallocating. - other.ptr_ = other.data_; + other.set(other.store_, 0); } + this->resize(size); } public: - MemoryBuffer(MemoryBuffer &&other) + /** + \rst + Constructs a :class:`fmt::basic_memory_buffer` object moving the content + of the other object to it. + \endrst + */ + basic_memory_buffer(basic_memory_buffer &&other) { move(other); } - MemoryBuffer &operator=(MemoryBuffer &&other) + /** + \rst + Moves the content of the other ``basic_memory_buffer`` object to this one. + \endrst + */ + basic_memory_buffer &operator=(basic_memory_buffer &&other) { assert(this != &other); deallocate(); move(other); return *this; } -#endif // Returns a copy of the allocator associated with this buffer. Allocator get_allocator() const @@ -986,177 +700,380 @@ class MemoryBuffer : private Allocator, public Buffer }; template -void MemoryBuffer::grow(std::size_t size) +void basic_memory_buffer::grow(std::size_t size) { - std::size_t new_capacity = this->capacity_ + this->capacity_ / 2; + std::size_t old_capacity = this->capacity(); + std::size_t new_capacity = old_capacity + old_capacity / 2; if (size > new_capacity) new_capacity = size; -#if FMT_USE_ALLOCATOR_TRAITS - T *new_ptr = std::allocator_traits::allocate(*this, new_capacity, FMT_NULL); -#else - T *new_ptr = this->allocate(new_capacity, FMT_NULL); -#endif + T *old_data = this->data(); + T *new_data = internal::allocate(*this, 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); + std::uninitialized_copy(old_data, old_data + this->size(), internal::make_checked(new_data, new_capacity)); + this->set(new_data, new_capacity); + // deallocate must not throw according to the standard, but even if it does, + // the buffer already uses the new storage and will deallocate it in + // destructor. + if (old_data != store_) + Allocator::deallocate(old_data, old_capacity); } -// A fixed-size buffer. +typedef basic_memory_buffer memory_buffer; +typedef basic_memory_buffer wmemory_buffer; + +/** + \rst + A fixed-size memory buffer. For a dynamically growing buffer use + :class:`fmt::basic_memory_buffer`. + + Trying to increase the buffer size past the initial capacity will throw + ``std::runtime_error``. + \endrst + */ template -class FixedBuffer : public fmt::Buffer +class basic_fixed_buffer : public internal::basic_buffer { public: - FixedBuffer(Char *array, std::size_t size) - : fmt::Buffer(array, size) + /** + \rst + Constructs a :class:`fmt::basic_fixed_buffer` object for *array* of the + given size. + \endrst + */ + basic_fixed_buffer(Char *array, std::size_t size) + { + this->set(array, size); + } + + /** + \rst + Constructs a :class:`fmt::basic_fixed_buffer` object for *array* of the + size known at compile time. + \endrst + */ + template + explicit basic_fixed_buffer(Char (&array)[SIZE]) { + this->set(array, SIZE); } protected: FMT_API void grow(std::size_t size) FMT_OVERRIDE; }; -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); - } -}; +namespace internal { template -class CharTraits; +struct char_traits; template<> -class CharTraits : public BasicCharTraits +struct char_traits { -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); + FMT_API static int format_float(char *buffer, std::size_t size, const char *format, int precision, T value); +}; + +template<> +struct char_traits +{ + template + FMT_API static int format_float(wchar_t *buffer, std::size_t size, const wchar_t *format, int precision, T value); }; #if FMT_USE_EXTERN_TEMPLATES -extern template int CharTraits::format_float( - char *buffer, std::size_t size, const char *format, unsigned width, int precision, double value); -extern template int CharTraits::format_float( - char *buffer, std::size_t size, const char *format, unsigned width, int precision, long double value); +extern template int char_traits::format_float( + char *buffer, std::size_t size, const char *format, int precision, double value); +extern template int char_traits::format_float( + char *buffer, std::size_t size, const char *format, int precision, long double value); + +extern template int char_traits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, int precision, double value); +extern template int char_traits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, int precision, long double value); #endif -template<> -class CharTraits : public BasicCharTraits +template +inline typename std::enable_if::value, typename checked::type>::type reserve( + std::back_insert_iterator &it, std::size_t n) +{ + Container &c = internal::get_container(it); + std::size_t size = c.size(); + c.resize(size + n); + return make_checked(&c[size], n); +} + +template +inline Iterator &reserve(Iterator &it, std::size_t) +{ + return it; +} + +template +class null_terminating_iterator; + +template +FMT_CONSTEXPR_DECL const Char *pointer_from(null_terminating_iterator it); + +// An iterator that produces a null terminator on *end. This simplifies parsing +// and allows comparing the performance of processing a null-terminated string +// vs string_view. +template +class null_terminating_iterator { public: - static wchar_t convert(char value) + typedef std::ptrdiff_t difference_type; + typedef Char value_type; + typedef const Char *pointer; + typedef const Char &reference; + typedef std::random_access_iterator_tag iterator_category; + + null_terminating_iterator() + : ptr_(0) + , end_(0) { - return value; } - static wchar_t convert(wchar_t value) + + FMT_CONSTEXPR null_terminating_iterator(const Char *ptr, const Char *end) + : ptr_(ptr) + , end_(end) { - 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); -}; + template + FMT_CONSTEXPR explicit null_terminating_iterator(const Range &r) + : ptr_(r.begin()) + , end_(r.end()) + { + } -#if FMT_USE_EXTERN_TEMPLATES -extern template int CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, double value); -extern template int CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, long double value); -#endif + null_terminating_iterator &operator=(const Char *ptr) + { + assert(ptr <= end_); + ptr_ = ptr; + return *this; + } -// Checks if a number is negative - used to avoid warnings. -template -struct SignChecker -{ - template - static bool is_negative(T value) + FMT_CONSTEXPR Char operator*() const { - return value < 0; + return ptr_ != end_ ? *ptr_ : 0; } -}; -template<> -struct SignChecker -{ - template - static bool is_negative(T) + FMT_CONSTEXPR null_terminating_iterator operator++() { - return false; + ++ptr_; + return *this; + } + + FMT_CONSTEXPR null_terminating_iterator operator++(int) + { + null_terminating_iterator result(*this); + ++ptr_; + return result; + } + + FMT_CONSTEXPR null_terminating_iterator operator--() + { + --ptr_; + return *this; + } + + FMT_CONSTEXPR null_terminating_iterator operator+(difference_type n) + { + return null_terminating_iterator(ptr_ + n, end_); + } + + FMT_CONSTEXPR null_terminating_iterator operator-(difference_type n) + { + return null_terminating_iterator(ptr_ - n, end_); + } + + FMT_CONSTEXPR null_terminating_iterator operator+=(difference_type n) + { + ptr_ += n; + return *this; + } + + FMT_CONSTEXPR difference_type operator-(null_terminating_iterator other) const + { + return ptr_ - other.ptr_; } + + FMT_CONSTEXPR bool operator!=(null_terminating_iterator other) const + { + return ptr_ != other.ptr_; + } + + bool operator>=(null_terminating_iterator other) const + { + return ptr_ >= other.ptr_; + } + + // This should be a friend specialization pointer_from but the latter + // doesn't compile by gcc 5.1 due to a compiler bug. + template + friend FMT_CONSTEXPR_DECL const CharT *pointer_from(null_terminating_iterator it); + +private: + const Char *ptr_; + const Char *end_; }; -// 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) +FMT_CONSTEXPR const T *pointer_from(const T *p) { - return SignChecker::is_signed>::is_negative(value); + return p; } -// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. -template -struct TypeSelector -{ - typedef uint32_t Type; -}; - -template<> -struct TypeSelector +template +FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator it) { - typedef uint64_t Type; -}; + return it.ptr_; +} +// An output iterator that counts the number of objects written to it and +// discards them. template -struct IntTraits +class counting_iterator { - // Smallest of uint32_t and uint64_t that is large enough to represent - // all values of T. - typedef typename TypeSelector::digits <= 32>::Type MainType; -}; +private: + std::size_t count_; + mutable T blackhole_; -FMT_API FMT_NORETURN void report_unknown_type(char code, const char *type); +public: + typedef std::output_iterator_tag iterator_category; + typedef T value_type; + typedef std::ptrdiff_t difference_type; + typedef T *pointer; + typedef T &reference; + typedef counting_iterator _Unchecked_type; // Mark iterator as checked. -// 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[]; -}; + counting_iterator() + : count_(0) + { + } -#if FMT_USE_EXTERN_TEMPLATES -extern template struct BasicData; -#endif + std::size_t count() const + { + return count_; + } -typedef BasicData<> Data; + counting_iterator &operator++() + { + ++count_; + return *this; + } + + counting_iterator operator++(int) + { + auto it = *this; + ++*this; + return it; + } + + T &operator*() const + { + return blackhole_; + } +}; + +// An output iterator that truncates the output and counts the number of objects +// written to it. +template +class truncating_iterator +{ +private: + typedef std::iterator_traits traits; + + OutputIt out_; + std::size_t limit_; + std::size_t count_; + mutable typename traits::value_type blackhole_; + +public: + typedef std::output_iterator_tag iterator_category; + typedef typename traits::value_type value_type; + typedef typename traits::difference_type difference_type; + typedef typename traits::pointer pointer; + typedef typename traits::reference reference; + typedef truncating_iterator _Unchecked_type; // Mark iterator as checked. + + truncating_iterator(OutputIt out, std::size_t limit) + : out_(out) + , limit_(limit) + , count_(0) + { + } + + OutputIt base() const + { + return out_; + } + std::size_t count() const + { + return count_; + } + + truncating_iterator &operator++() + { + if (count_++ < limit_) + ++out_; + return *this; + } + + truncating_iterator operator++(int) + { + auto it = *this; + ++*this; + return it; + } + + reference operator*() const + { + return count_ < limit_ ? *out_ : blackhole_; + } +}; + +// Returns true if value is negative, false otherwise. +// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. +template +FMT_CONSTEXPR typename std::enable_if::is_signed, bool>::type is_negative(T value) +{ + return value < 0; +} +template +FMT_CONSTEXPR typename std::enable_if::is_signed, bool>::type is_negative(T) +{ + return false; +} + +template +struct int_traits +{ + // Smallest of uint32_t and uint64_t that is large enough to represent + // all values of T. + typedef typename std::conditional::digits <= 32, uint32_t, uint64_t>::type main_type; +}; + +// Static data is placed in this class template to allow header-only +// configuration. +template +struct FMT_API basic_data +{ + static const uint32_t POWERS_OF_10_32[]; + static const uint64_t POWERS_OF_10_64[]; + static const uint64_t POW10_SIGNIFICANDS[]; + static const int16_t POW10_EXPONENTS[]; + static const char DIGITS[]; + static const char RESET_COLOR[]; + static const wchar_t WRESET_COLOR[]; +}; + +#if FMT_USE_EXTERN_TEMPLATES +extern template struct basic_data; +#endif + +typedef basic_data<> data; #ifdef FMT_BUILTIN_CLZLL // Returns the number of decimal digits in n. Leading zeros are not counted @@ -1166,7 +1083,7 @@ 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; + 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. @@ -1192,18 +1109,110 @@ inline unsigned count_digits(uint64_t n) } #endif +#if FMT_HAS_CPP_ATTRIBUTE(always_inline) +#define FMT_ALWAYS_INLINE __attribute__((always_inline)) +#else +#define FMT_ALWAYS_INLINE +#endif + +template +inline char *lg(uint32_t n, Handler h) FMT_ALWAYS_INLINE; + +// Computes g = floor(log10(n)) and calls h.on(n); +template +inline char *lg(uint32_t n, Handler h) +{ + return n < 100 ? n < 10 ? h.template on<0>(n) : h.template on<1>(n) + : n < 1000000 ? n < 10000 ? n < 1000 ? h.template on<2>(n) : h.template on<3>(n) + : n < 100000 ? h.template on<4>(n) : h.template on<5>(n) + : n < 100000000 ? n < 10000000 ? h.template on<6>(n) : h.template on<7>(n) + : n < 1000000000 ? h.template on<8>(n) : h.template on<9>(n); +} + +// An lg handler that formats a decimal number. +// Usage: lg(n, decimal_formatter(buffer)); +class decimal_formatter +{ +private: + char *buffer_; + + void write_pair(unsigned N, uint32_t index) + { + std::memcpy(buffer_ + N, data::DIGITS + index * 2, 2); + } + +public: + explicit decimal_formatter(char *buf) + : buffer_(buf) + { + } + + template + char *on(uint32_t u) + { + if (N == 0) + { + *buffer_ = static_cast(u) + '0'; + } + else if (N == 1) + { + write_pair(0, u); + } + else + { + // The idea of using 4.32 fixed-point numbers is based on + // https://github.com/jeaiii/itoa + unsigned n = N - 1; + unsigned a = n / 5 * n * 53 / 16; + uint64_t t = ((1ULL << (32 + a)) / data::POWERS_OF_10_32[n] + 1 - n / 9); + t = ((t * u) >> a) + n / 5 * 4; + write_pair(0, t >> 32); + for (unsigned i = 2; i < N; i += 2) + { + t = 100ULL * static_cast(t); + write_pair(i, t >> 32); + } + if (N % 2 == 0) + { + buffer_[N] = static_cast((10ULL * static_cast(t)) >> 32) + '0'; + } + } + return buffer_ += N + 1; + } +}; + +// An lg handler that formats a decimal number with a terminating null. +class decimal_formatter_null : public decimal_formatter +{ +public: + explicit decimal_formatter_null(char *buf) + : decimal_formatter(buf) + { + } + + template + char *on(uint32_t u) + { + char *buf = decimal_formatter::on(u); + *buf = '\0'; + return buf; + } +}; + #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; + return to_unsigned(t) - (n < data::POWERS_OF_10_32[t]) + 1; } #endif // A functor that doesn't add a thousands separator. -struct NoThousandsSep +struct no_thousands_sep { + typedef char char_type; + template void operator()(Char *) { @@ -1211,38 +1220,44 @@ struct NoThousandsSep }; // A functor that adds a thousands separator. -class ThousandsSep +template +class add_thousands_sep { private: - fmt::StringRef sep_; + basic_string_view sep_; // Index of a decimal digit with the least significant digit having index 0. unsigned digit_index_; public: - explicit ThousandsSep(fmt::StringRef sep) + typedef Char char_type; + + explicit add_thousands_sep(basic_string_view 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())); + std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(), internal::make_checked(buffer, sep_.size())); } }; +template +FMT_API Char thousands_sep(locale_provider *lp); + // 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) +inline Char *format_decimal(Char *buffer, UInt value, unsigned num_digits, ThousandsSep thousands_sep) { buffer += num_digits; + Char *end = buffer; while (value >= 100) { // Integer division is slow so do it for a group of two digits instead @@ -1250,27 +1265,61 @@ inline void format_decimal(Char *buffer, UInt value, unsigned num_digits, Thousa // "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]; + *--buffer = data::DIGITS[index + 1]; thousands_sep(buffer); - *--buffer = Data::DIGITS[index]; + *--buffer = data::DIGITS[index]; thousands_sep(buffer); } if (value < 10) { *--buffer = static_cast('0' + value); - return; + return end; } unsigned index = static_cast(value * 2); - *--buffer = Data::DIGITS[index + 1]; + *--buffer = data::DIGITS[index + 1]; thousands_sep(buffer); - *--buffer = Data::DIGITS[index]; + *--buffer = data::DIGITS[index]; + return end; +} + +template +inline Iterator format_decimal(Iterator out, UInt value, unsigned num_digits, ThousandsSep sep) +{ + typedef typename ThousandsSep::char_type char_type; + // Buffer should be large enough to hold all digits (digits10 + 1) and null. + char_type buffer[std::numeric_limits::digits10 + 2]; + format_decimal(buffer, value, num_digits, sep); + return std::copy_n(buffer, num_digits, out); +} + +template +inline It format_decimal(It out, UInt value, unsigned num_digits) +{ + return format_decimal(out, value, num_digits, no_thousands_sep()); +} + +template +inline Char *format_uint(Char *buffer, UInt value, unsigned num_digits, bool upper = false) +{ + buffer += num_digits; + Char *end = buffer; + do + { + const char *digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + unsigned digit = (value & ((1 << BASE_BITS) - 1)); + *--buffer = BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]; + } while ((value >>= BASE_BITS) != 0); + return end; } -template -inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) +template +inline It format_uint(It out, UInt value, unsigned num_digits, bool upper = false) { - format_decimal(buffer, value, num_digits, NoThousandsSep()); - return; + // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1) + // and null. + char buffer[std::numeric_limits::digits / BASE_BITS + 2]; + format_uint(buffer, value, num_digits, upper); + return std::copy_n(buffer, num_digits, out); } #ifndef _WIN32 @@ -1284,16 +1333,16 @@ inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) #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 +class utf8_to_utf16 { private: - MemoryBuffer buffer_; + wmemory_buffer buffer_; public: - FMT_API explicit UTF8ToUTF16(StringRef s); - operator WStringRef() const + FMT_API explicit utf8_to_utf16(string_view s); + operator wstring_view() const { - return WStringRef(&buffer_[0], size()); + return wstring_view(&buffer_[0], size()); } size_t size() const { @@ -1311,17 +1360,17 @@ class UTF8ToUTF16 // A converter from UTF-16 to UTF-8. // It is only provided for Windows since other systems support UTF-8 natively. -class UTF16ToUTF8 +class utf16_to_utf8 { private: - MemoryBuffer buffer_; + memory_buffer buffer_; public: - UTF16ToUTF8() {} - FMT_API explicit UTF16ToUTF8(WStringRef s); - operator StringRef() const + utf16_to_utf8() {} + FMT_API explicit utf16_to_utf8(wstring_view s); + operator string_view() const { - return StringRef(&buffer_[0], size()); + return string_view(&buffer_[0], size()); } size_t size() const { @@ -1339,1756 +1388,1542 @@ class UTF16ToUTF8 // 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 int convert(wstring_view s); }; -FMT_API void format_windows_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; +FMT_API void format_windows_error(fmt::internal::buffer &out, int error_code, fmt::string_view message) FMT_NOEXCEPT; #endif -// A formatting argument value. -struct Value +template +struct null { - 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 - }; }; +} // namespace internal -// A formatting argument. It is a trivially copyable/constructible type to -// allow storage in internal::MemoryBuffer. -struct Arg : Value +struct monostate { - Type type; }; -template -struct NamedArg; -template -struct NamedArgWithType; - -template -struct Null +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +template +FMT_CONSTEXPR typename internal::result_of::type visit(Visitor &&vis, basic_format_arg arg) { -}; + typedef typename Context::char_type char_type; + switch (arg.type_) + { + case internal::none_type: + break; + case internal::named_arg_type: + FMT_ASSERT(false, "invalid argument type"); + break; + case internal::int_type: + return vis(arg.value_.int_value); + case internal::uint_type: + return vis(arg.value_.uint_value); + case internal::long_long_type: + return vis(arg.value_.long_long_value); + case internal::ulong_long_type: + return vis(arg.value_.ulong_long_value); + case internal::bool_type: + return vis(arg.value_.int_value != 0); + case internal::char_type: + return vis(static_cast(arg.value_.int_value)); + case internal::double_type: + return vis(arg.value_.double_value); + case internal::long_double_type: + return vis(arg.value_.long_double_value); + case internal::cstring_type: + return vis(arg.value_.string.value); + case internal::string_type: + return vis(basic_string_view(arg.value_.string.value, arg.value_.string.size)); + case internal::pointer_type: + return vis(arg.value_.pointer); + case internal::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); + } + return vis(monostate()); +} -// A helper class template to enable or disable overloads taking wide -// characters and strings in MakeValue. -template -struct WCharHelper +enum alignment { - typedef Null Supported; - typedef T Unsupported; + ALIGN_DEFAULT, + ALIGN_LEFT, + ALIGN_RIGHT, + ALIGN_CENTER, + ALIGN_NUMERIC }; -template -struct WCharHelper +// Flags. +enum { - typedef T Supported; - typedef Null Unsupported; + SIGN_FLAG = 1, + PLUS_FLAG = 2, + MINUS_FLAG = 4, + HASH_FLAG = 8 }; -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 format_spec_tag { - enum - { - value = ENABLE_CONVERSION - }; + fill_tag, + align_tag, + width_tag, + type_tag }; -template -struct ConvertToIntImpl2 +// Format specifier. +template +class format_spec { - enum +private: + T value_; + +public: + typedef T value_type; + + explicit format_spec(T value) + : value_(value) { - value = false - }; -}; + } -template -struct ConvertToIntImpl2 -{ - enum + T value() const { - // Don't convert numeric types. - value = ConvertToIntImpl::is_specialized>::value - }; + return value_; + } }; -template -struct ConvertToInt +// template +// typedef format_spec fill_spec; +template +class fill_spec : public format_spec { - enum - { - enable_conversion = sizeof(fmt::internal::convert(get())) == sizeof(Yes) - }; - enum +public: + explicit fill_spec(Char value) + : format_spec(value) { - 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); +typedef format_spec width_spec; +typedef format_spec type_spec; -template -struct EnableIf +// An empty format specifier. +struct empty_spec { }; -template -struct EnableIf +// An alignment specifier. +struct align_spec : empty_spec { - typedef T type; -}; + unsigned width_; + // Fill is always wchar_t and cast to char if necessary to avoid having + // two specialization of AlignSpec and its subclasses. + wchar_t fill_; + alignment align_; -template -struct Conditional -{ - typedef T type; -}; + FMT_CONSTEXPR align_spec(unsigned width, wchar_t fill, alignment align = ALIGN_DEFAULT) + : width_(width) + , fill_(fill) + , align_(align) + { + } -template -struct Conditional -{ - typedef F type; -}; + FMT_CONSTEXPR unsigned width() const + { + return width_; + } + FMT_CONSTEXPR wchar_t fill() const + { + return fill_; + } + FMT_CONSTEXPR alignment align() const + { + return align_; + } -// For bcc32 which doesn't understand ! in template arguments. -template -struct Not -{ - enum + int precision() const { - value = 0 - }; + return -1; + } }; -template<> -struct Not +// Format specifiers. +template +class basic_format_specs : public align_spec { - enum +public: + unsigned flags_; + int precision_; + Char type_; + + FMT_CONSTEXPR basic_format_specs(unsigned width = 0, char type = 0, wchar_t fill = ' ') + : align_spec(width, fill) + , flags_(0) + , precision_(-1) + , type_(type) { - value = 1 - }; -}; + } -template -struct FalseType -{ - enum + FMT_CONSTEXPR bool flag(unsigned f) const { - value = 0 - }; + return (flags_ & f) != 0; + } + FMT_CONSTEXPR int precision() const + { + return precision_; + } + FMT_CONSTEXPR Char type() const + { + return type_; + } }; -template -struct LConvCheck -{ - LConvCheck(int) {} -}; +typedef basic_format_specs format_specs; -// 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) +template +FMT_CONSTEXPR unsigned basic_parse_context::next_arg_id() { - return lc->thousands_sep; + if (next_arg_id_ >= 0) + return internal::to_unsigned(next_arg_id_++); + on_error("cannot switch from manual to automatic argument indexing"); + return 0; } -inline fmt::StringRef thousands_sep(...) +struct format_string { - return ""; -} +}; -#define FMT_CONCAT(a, b) a##b +namespace internal { -#if FMT_GCC_VERSION >= 303 -#define FMT_UNUSED __attribute__((unused)) -#else -#define FMT_UNUSED -#endif +template +FMT_CONSTEXPR void handle_int_type_spec(Char spec, Handler &&handler) +{ + switch (spec) + { + case 0: + case 'd': + handler.on_dec(); + break; + case 'x': + case 'X': + handler.on_hex(); + break; + case 'b': + case 'B': + handler.on_bin(); + break; + case 'o': + handler.on_oct(); + break; + case 'n': + handler.on_num(); + break; + default: + handler.on_error(); + } +} -#ifndef FMT_USE_STATIC_ASSERT -#define FMT_USE_STATIC_ASSERT 0 -#endif +template +FMT_CONSTEXPR void handle_float_type_spec(Char spec, Handler &&handler) +{ + switch (spec) + { + case 0: + case 'g': + case 'G': + handler.on_general(); + break; + case 'e': + case 'E': + handler.on_exp(); + break; + case 'f': + case 'F': + handler.on_fixed(); + break; + case 'a': + case 'A': + handler.on_hex(); + break; + default: + handler.on_error(); + break; + } +} -#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 +FMT_CONSTEXPR void handle_char_specs(const basic_format_specs &specs, Handler &&handler) +{ + if (specs.type() && specs.type() != 'c') + { + handler.on_int(); + return; + } + if (specs.align() == ALIGN_NUMERIC || specs.flag(~0u) != 0) + handler.on_error("invalid format specifier for char"); + handler.on_char(); +} -template -void format_arg(Formatter &, ...) +template +FMT_CONSTEXPR void handle_cstring_type_spec(Char spec, Handler &&handler) { - FMT_STATIC_ASSERT(FalseType::value, "Cannot format argument. To enable the use of ostream " - "operator<< include fmt/ostream.h. Otherwise provide " - "an overload of format_arg."); + if (spec == 0 || spec == 's') + handler.on_string(); + else if (spec == 'p') + handler.on_pointer(); + else + handler.on_error("invalid type specifier"); } -// Makes an Arg object from any type. -template -class MakeValue : public Arg +template +FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler &&eh) { -public: - typedef typename Formatter::Char Char; + if (spec != 0 && spec != 's') + eh.on_error("invalid type specifier"); +} -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); -#if FMT_HAS_STRING_VIEW - MakeValue(typename WCharHelper::Unsupported); -#endif - MakeValue(typename WCharHelper::Unsupported); +template +FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler &&eh) +{ + if (spec != 0 && spec != 'p') + eh.on_error("invalid type specifier"); +} - void set_string(StringRef str) +template +class int_type_checker : private ErrorHandler +{ +public: + FMT_CONSTEXPR explicit int_type_checker(ErrorHandler eh) + : ErrorHandler(eh) { - string.value = str.data(); - string.size = str.size(); } - void set_string(WStringRef str) - { - wstring.value = str.data(); - wstring.size = str.size(); - } + FMT_CONSTEXPR void on_dec() {} + FMT_CONSTEXPR void on_hex() {} + FMT_CONSTEXPR void on_bin() {} + FMT_CONSTEXPR void on_oct() {} + FMT_CONSTEXPR void on_num() {} - // 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) + FMT_CONSTEXPR void on_error() { - format_arg(*static_cast(formatter), *static_cast(format_str_ptr), *static_cast(arg)); + ErrorHandler::on_error("invalid type specifier"); } +}; +template +class float_type_checker : private ErrorHandler +{ public: - MakeValue() {} - -#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \ - MakeValue(Type value) \ - { \ - field = rhs; \ - } \ - static uint64_t type(Type) \ - { \ - return Arg::TYPE; \ + FMT_CONSTEXPR explicit float_type_checker(ErrorHandler eh) + : ErrorHandler(eh) + { } -#define FMT_MAKE_VALUE(Type, field, TYPE) FMT_MAKE_VALUE_(Type, field, TYPE, value) + FMT_CONSTEXPR void on_general() {} + FMT_CONSTEXPR void on_exp() {} + FMT_CONSTEXPR void on_fixed() {} + FMT_CONSTEXPR void on_hex() {} - 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) + FMT_CONSTEXPR void on_error() { - return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; + ErrorHandler::on_error("invalid type specifier"); } +}; - 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) +template +class char_specs_checker : public ErrorHandler +{ +private: + CharType type_; + +public: + FMT_CONSTEXPR char_specs_checker(CharType type, ErrorHandler eh) + : ErrorHandler(eh) + , type_(type) { - 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 __cplusplus >= 201103L - template::value && ConvertToInt::value>::type> - MakeValue(T value) + FMT_CONSTEXPR void on_int() { - int_value = value; + handle_int_type_spec(type_, int_type_checker(*this)); } + FMT_CONSTEXPR void on_char() {} +}; - template::value && ConvertToInt::value>::type> - static uint64_t type(T) +template +class cstring_type_checker : public ErrorHandler +{ +public: + FMT_CONSTEXPR explicit cstring_type_checker(ErrorHandler eh) + : ErrorHandler(eh) { - return Arg::INT; } -#endif -#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) - MakeValue(typename WCharHelper::Supported value) + FMT_CONSTEXPR void on_string() {} + FMT_CONSTEXPR void on_pointer() {} +}; + +template +void arg_map::init(const basic_format_args &args) +{ + if (map_) + return; + map_ = new entry[args.max_size()]; + bool use_values = args.type(max_packed_args - 1) == internal::none_type; + if (use_values) { - int_value = value; + for (unsigned i = 0; /*nothing*/; ++i) + { + internal::type arg_type = args.type(i); + switch (arg_type) + { + case internal::none_type: + return; + case internal::named_arg_type: + push_back(args.values_[i]); + break; + default: + break; // Do nothing. + } + } + return; } - static uint64_t type(wchar_t) + for (unsigned i = 0;; ++i) { - return Arg::CHAR; + switch (args.args_[i].type_) + { + case internal::none_type: + return; + case internal::named_arg_type: + push_back(args.args_[i].value_); + break; + default: + break; // Do nothing. + } } -#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) -#if FMT_HAS_STRING_VIEW - FMT_MAKE_STR_VALUE(const std::string_view &, STRING) -#endif - FMT_MAKE_STR_VALUE(StringRef, STRING) - FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str()) +template +class arg_formatter_base +{ +public: + typedef typename Range::value_type char_type; + typedef decltype(internal::declval().begin()) iterator; + typedef basic_format_specs format_specs; -#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ - MakeValue(typename WCharHelper::Supported value) \ - { \ - set_string(value); \ - } \ - static uint64_t type(Type) \ - { \ - return Arg::TYPE; \ - } +private: + typedef basic_writer writer_type; + writer_type writer_; + format_specs &specs_; - FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) - FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING) - FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING) -#if FMT_HAS_STRING_VIEW - FMT_MAKE_WSTR_VALUE(const std::wstring_view &, WSTRING) -#endif - FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING) + FMT_DISALLOW_COPY_AND_ASSIGN(arg_formatter_base); - FMT_MAKE_VALUE(void *, pointer, POINTER) - FMT_MAKE_VALUE(const void *, pointer, POINTER) + struct char_writer + { + char_type value; + template + void operator()(It &&it) const + { + *it++ = value; + } + }; - template - MakeValue(const T &value, typename EnableIf::value>::value, int>::type = 0) + void write_char(char_type value) { - custom.value = &value; - custom.format = &format_custom_arg; + writer_.write_padded(1, specs_, char_writer{value}); } - template - static typename EnableIf::value>::value, uint64_t>::type type(const T &) + void write_pointer(const void *p) { - return Arg::CUSTOM; + format_specs specs = specs_; + specs.flags_ = HASH_FLAG; + specs.type_ = 'x'; + writer_.write_int(reinterpret_cast(p), specs); } - // Additional template param `Char_` is needed here because make_type always - // uses char. - template - MakeValue(const NamedArg &value) +protected: + writer_type &writer() + { + return writer_; + } + format_specs &spec() { - pointer = &value; + return specs_; } - template - MakeValue(const NamedArgWithType &value) + iterator out() { - pointer = &value; + return writer_.out(); } - template - static uint64_t type(const NamedArg &) + void write(bool value) { - return Arg::NAMED_ARG; + writer_.write_str(string_view(value ? "true" : "false"), specs_); } - template - static uint64_t type(const NamedArgWithType &) + + void write(const char_type *value) { - return Arg::NAMED_ARG; + if (!value) + FMT_THROW(format_error("string pointer is null")); + auto length = std::char_traits::length(value); + writer_.write_str(basic_string_view(value, length), specs_); } -}; -template -class MakeArg : public Arg -{ public: - MakeArg() + arg_formatter_base(Range r, format_specs &s) + : writer_(r) + , specs_(s) { - type = Arg::NONE; } - template - MakeArg(const T &value) - : Arg(MakeValue(value)) + iterator operator()(monostate) { - type = static_cast(MakeValue::type(value)); + FMT_ASSERT(false, "invalid argument type"); + return out(); } -}; - -template -struct NamedArg : Arg -{ - BasicStringRef name; template - NamedArg(BasicStringRef argname, const T &value) - : Arg(MakeArg>(value)) - , name(argname) + typename std::enable_if::value, iterator>::type operator()(T value) { + // MSVC2013 fails to compile separate overloads for bool and char_type so + // use std::is_same instead. + if (std::is_same::value) + { + if (specs_.type_) + return (*this)(value ? 1 : 0); + write(value != 0); + } + else if (std::is_same::value) + { + internal::handle_char_specs(specs_, char_spec_handler(*this, static_cast(value))); + } + else + { + writer_.write_int(value, specs_); + } + return out(); } -}; -template -struct NamedArgWithType : NamedArg -{ - NamedArgWithType(BasicStringRef argname, const T &value) - : NamedArg(argname, value) + template + typename std::enable_if::value, iterator>::type operator()(T value) { + writer_.write_double(value, specs_); + return out(); } -}; -class RuntimeError : public std::runtime_error -{ -protected: - RuntimeError() - : std::runtime_error("") - { - } - RuntimeError(const RuntimeError &rerr) - : std::runtime_error(rerr) + struct char_spec_handler : internal::error_handler { - } - FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; -}; + arg_formatter_base &formatter; + char_type value; -template -class ArgMap; -} // namespace internal + char_spec_handler(arg_formatter_base &f, char_type val) + : formatter(f) + , value(val) + { + } -/** 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_; + void on_int() + { + formatter.writer_.write_int(value, formatter.specs_); + } + void on_char() + { + formatter.write_char(value); + } }; - internal::Arg::Type type(unsigned index) const + struct cstring_spec_handler : internal::error_handler { - return type(types_, index); - } + arg_formatter_base &formatter; + const char_type *value; - template - friend class internal::ArgMap; + cstring_spec_handler(arg_formatter_base &f, const char_type *val) + : formatter(f) + , value(val) + { + } -public: - // Maximum number of arguments with packed types. - enum - { - MAX_PACKED_ARGS = 16 + void on_string() + { + formatter.write(value); + } + void on_pointer() + { + formatter.write_pointer(value); + } }; - 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) - { - } - - uint64_t types() const + iterator operator()(const char_type *value) { - return types_; + internal::handle_cstring_type_spec(specs_.type_, cstring_spec_handler(*this, value)); + return out(); } - /** Returns the argument at specified index. */ - internal::Arg operator[](unsigned index) const + iterator operator()(basic_string_view value) { - 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]; + internal::check_string_type_spec(specs_.type_, internal::error_handler()); + writer_.write_str(value, specs_); + return out(); } - static internal::Arg::Type type(uint64_t types, unsigned index) + iterator operator()(const void *value) { - unsigned shift = index * 4; - uint64_t mask = 0xf; - return static_cast((types & (mask << shift)) >> shift); + check_pointer_type_spec(specs_.type_, internal::error_handler()); + write_pointer(value); + return out(); } }; -#define FMT_DISPATCH(call) static_cast(this)->call +template +struct is_format_string : std::integral_constant::value> +{ +}; -/** - \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. +template +FMT_CONSTEXPR bool is_name_start(Char c) +{ + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} - **Example**:: +// Parses the input as an unsigned integer. This function assumes that the +// first character is a digit and presence of a non-digit character at the end. +// it: an iterator pointing to the beginning of the input range. +template +FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) +{ + assert('0' <= *it && *it <= '9'); + unsigned value = 0; + // Convert to unsigned to prevent a warning. + unsigned max_int = (std::numeric_limits::max)(); + unsigned big = max_int / 10; + do + { + // Check for overflow. + if (value > big) + { + value = max_int + 1; + break; + } + value = value * 10 + unsigned(*it - '0'); + // Workaround for MSVC "setup_exception stack overflow" error: + auto next = it; + ++next; + it = next; + } while ('0' <= *it && *it <= '9'); + if (value > max_int) + eh.on_error("number is too big"); + return value; +} - 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 +template +class custom_formatter : public function { private: - typedef internal::Arg Arg; + Context &ctx_; public: - void report_unhandled_arg() {} - - Result visit_unhandled_arg() + explicit custom_formatter(Context &ctx) + : ctx_(ctx) { - FMT_DISPATCH(report_unhandled_arg()); - return Result(); } - /** Visits an ``int`` argument. **/ - Result visit_int(int value) + bool operator()(typename basic_format_arg::handle h) const { - 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)); + h.format(ctx_); + return true; } - /** Visits an ``unsigned`` argument. **/ - Result visit_uint(unsigned value) + template + bool operator()(T) const { - return FMT_DISPATCH(visit_any_int(value)); + return false; } +}; - /** Visits an ``unsigned long long`` argument. **/ - Result visit_ulong_long(ULongLong value) +template +struct is_integer +{ + enum { - return FMT_DISPATCH(visit_any_int(value)); - } + value = + std::is_integral::value && !std::is_same::value && !std::is_same::value && !std::is_same::value + }; +}; - /** Visits a ``bool`` argument. **/ - Result visit_bool(bool value) +template +class width_checker : public function +{ +public: + explicit FMT_CONSTEXPR width_checker(ErrorHandler &eh) + : handler_(eh) { - return FMT_DISPATCH(visit_any_int(value)); } - /** Visits a ``char`` or ``wchar_t`` argument. **/ - Result visit_char(int value) + template + FMT_CONSTEXPR typename std::enable_if::value, unsigned long long>::type operator()(T value) { - return FMT_DISPATCH(visit_any_int(value)); + if (is_negative(value)) + handler_.on_error("negative width"); + return static_cast(value); } - /** Visits an argument of any integral type. **/ template - Result visit_any_int(T) + FMT_CONSTEXPR typename std::enable_if::value, unsigned long long>::type operator()(T) { - return FMT_DISPATCH(visit_unhandled_arg()); + handler_.on_error("width is not integer"); + return 0; } - /** Visits a ``double`` argument. **/ - Result visit_double(double value) +private: + ErrorHandler &handler_; +}; + +template +class precision_checker : public function +{ +public: + explicit FMT_CONSTEXPR precision_checker(ErrorHandler &eh) + : handler_(eh) { - return FMT_DISPATCH(visit_any_double(value)); } - /** Visits a ``long double`` argument. **/ - Result visit_long_double(long double value) + template + FMT_CONSTEXPR typename std::enable_if::value, unsigned long long>::type operator()(T value) { - return FMT_DISPATCH(visit_any_double(value)); + if (is_negative(value)) + handler_.on_error("negative precision"); + return static_cast(value); } - /** Visits a ``double`` or ``long double`` argument. **/ template - Result visit_any_double(T) + FMT_CONSTEXPR typename std::enable_if::value, unsigned long long>::type operator()(T) { - return FMT_DISPATCH(visit_unhandled_arg()); + handler_.on_error("precision is not integer"); + return 0; } - /** Visits a null-terminated C string (``const char *``) argument. **/ - Result visit_cstring(const char *) +private: + ErrorHandler &handler_; +}; + +// A format specifier handler that sets fields in basic_format_specs. +template +class specs_setter +{ +public: + explicit FMT_CONSTEXPR specs_setter(basic_format_specs &specs) + : specs_(specs) { - return FMT_DISPATCH(visit_unhandled_arg()); } - /** Visits a string argument. **/ - Result visit_string(Arg::StringValue) + FMT_CONSTEXPR specs_setter(const specs_setter &other) + : specs_(other.specs_) { - return FMT_DISPATCH(visit_unhandled_arg()); } - /** Visits a wide string argument. **/ - Result visit_wstring(Arg::StringValue) + FMT_CONSTEXPR void on_align(alignment align) { - return FMT_DISPATCH(visit_unhandled_arg()); + specs_.align_ = align; } - - /** Visits a pointer argument. **/ - Result visit_pointer(const void *) + FMT_CONSTEXPR void on_fill(Char fill) { - return FMT_DISPATCH(visit_unhandled_arg()); + specs_.fill_ = fill; } - - /** Visits an argument of a custom (user-defined) type. **/ - Result visit_custom(Arg::CustomValue) + FMT_CONSTEXPR void on_plus() { - return FMT_DISPATCH(visit_unhandled_arg()); + specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; } - - /** - \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 + FMT_CONSTEXPR void on_minus() { - return ALIGN_DEFAULT; + specs_.flags_ |= MINUS_FLAG; } - unsigned width() const + FMT_CONSTEXPR void on_space() { - return 0; + specs_.flags_ |= SIGN_FLAG; } - int precision() const + FMT_CONSTEXPR void on_hash() { - return -1; + specs_.flags_ |= HASH_FLAG; } - bool flag(unsigned) const + + FMT_CONSTEXPR void on_zero() { - return false; + specs_.align_ = ALIGN_NUMERIC; + specs_.fill_ = '0'; } - char type() const + + FMT_CONSTEXPR void on_width(unsigned width) { - return TYPE; + specs_.width_ = width; } - char type_prefix() const + FMT_CONSTEXPR void on_precision(unsigned precision) { - return TYPE; + specs_.precision_ = static_cast(precision); } - char fill() const + FMT_CONSTEXPR void end_precision() {} + + FMT_CONSTEXPR void on_type(Char type) { - return ' '; + specs_.type_ = type; } + +protected: + basic_format_specs &specs_; }; -// A width specifier. -struct WidthSpec +// A format specifier handler that checks if specifiers are consistent with the +// argument type. +template +class specs_checker : public Handler { - 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) +public: + FMT_CONSTEXPR specs_checker(const Handler &handler, internal::type arg_type) + : Handler(handler) + , arg_type_(arg_type) { } - unsigned width() const + FMT_CONSTEXPR specs_checker(const specs_checker &other) + : Handler(other) + , arg_type_(other.arg_type_) { - return width_; } - wchar_t fill() const + + FMT_CONSTEXPR void on_align(alignment align) { - return fill_; + if (align == ALIGN_NUMERIC) + require_numeric_argument(); + Handler::on_align(align); } -}; -// An alignment specifier. -struct AlignSpec : WidthSpec -{ - Alignment align_; + FMT_CONSTEXPR void on_plus() + { + check_sign(); + Handler::on_plus(); + } - AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) - : WidthSpec(width, fill) - , align_(align) + FMT_CONSTEXPR void on_minus() { + check_sign(); + Handler::on_minus(); } - Alignment align() const + FMT_CONSTEXPR void on_space() { - return align_; + check_sign(); + Handler::on_space(); } - int precision() const + FMT_CONSTEXPR void on_hash() { - return -1; + require_numeric_argument(); + Handler::on_hash(); } -}; -// An alignment and type specifier. -template -struct AlignTypeSpec : AlignSpec -{ - AlignTypeSpec(unsigned width, wchar_t fill) - : AlignSpec(width, fill) + FMT_CONSTEXPR void on_zero() { + require_numeric_argument(); + Handler::on_zero(); } - bool flag(unsigned) const + FMT_CONSTEXPR void end_precision() { - return false; + if (is_integral(arg_type_) || arg_type_ == pointer_type) + this->on_error("precision not allowed for this argument type"); } - char type() const + +private: + FMT_CONSTEXPR void require_numeric_argument() { - return TYPE; + if (!is_arithmetic(arg_type_)) + this->on_error("format specifier requires numeric argument"); } - char type_prefix() const + + FMT_CONSTEXPR void check_sign() { - return TYPE; + require_numeric_argument(); + if (is_integral(arg_type_) && arg_type_ != int_type && arg_type_ != long_long_type && arg_type_ != internal::char_type) + { + this->on_error("format specifier requires signed argument"); + } } + + internal::type arg_type_; }; -// A full format specifier. -struct FormatSpec : AlignSpec +template class Handler, typename T, typename Context, typename ErrorHandler> +FMT_CONSTEXPR void set_dynamic_spec(T &value, basic_format_arg arg, ErrorHandler eh) { - unsigned flags_; - int precision_; - char type_; + unsigned long long big_value = visit(Handler(eh), arg); + if (big_value > (std::numeric_limits::max)()) + eh.on_error("number is too big"); + value = static_cast(big_value); +} - FormatSpec(unsigned width = 0, char type = 0, wchar_t fill = ' ') - : AlignSpec(width, fill) - , flags_(0) - , precision_(-1) - , type_(type) - { - } +struct auto_id +{ +}; + +// The standard format specifier handler with checking. +template +class specs_handler : public specs_setter +{ +public: + typedef typename Context::char_type char_type; - bool flag(unsigned f) const + FMT_CONSTEXPR specs_handler(basic_format_specs &specs, Context &ctx) + : specs_setter(specs) + , context_(ctx) { - return (flags_ & f) != 0; } - int precision() const + + template + FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { - return precision_; + set_dynamic_spec(this->specs_.width_, get_arg(arg_id), context_.error_handler()); } - char type() const + + template + FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { - return type_; + set_dynamic_spec(this->specs_.precision_, get_arg(arg_id), context_.error_handler()); } - char type_prefix() const + + void on_error(const char *message) { - return type_; + context_.on_error(message); } -}; -// 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) + FMT_CONSTEXPR basic_format_arg get_arg(auto_id) { + return context_.next_arg(); } - T value() const + template + FMT_CONSTEXPR basic_format_arg get_arg(Id arg_id) { - return value_; + context_.parse_context().check_arg_id(arg_id); + return context_.get_arg(arg_id); } + + Context &context_; }; -// A string format specifier. +// An argument reference. template -class StrFormatSpec : public AlignSpec +struct arg_ref { -private: - const Char *str_; + enum Kind + { + NONE, + INDEX, + NAME + }; -public: - template - StrFormatSpec(const Char *str, unsigned width, FillChar fill) - : AlignSpec(width, fill) - , str_(str) + FMT_CONSTEXPR arg_ref() + : kind(NONE) + , index(0) { - internal::CharTraits::convert(FillChar()); } - - const Char *str() const + FMT_CONSTEXPR explicit arg_ref(unsigned index) + : kind(INDEX) + , index(index) + { + } + explicit arg_ref(basic_string_view name) + : kind(NAME) + , name(name) { - 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. + FMT_CONSTEXPR arg_ref &operator=(unsigned idx) + { + kind = INDEX; + index = idx; + return *this; + } - **Example**:: + Kind kind; + FMT_UNION + { + unsigned index; + basic_string_view name; + }; +}; - MemoryWriter out; - out << pad(hex(0xcafe), 8, '0'); - // out.str() == "0000cafe" +// Format specifiers with width and precision resolved at formatting rather +// than parsing time to allow re-using the same parsed specifiers with +// differents sets of arguments (precompilation of format strings). +template +struct dynamic_format_specs : basic_format_specs +{ + arg_ref width_ref; + arg_ref precision_ref; +}; - \endrst - */ -template -IntFormatSpec, Char> pad(int value, unsigned width, Char fill = ' '); +// Format spec handler that saves references to arguments representing dynamic +// width and precision to be resolved at formatting time. +template +class dynamic_specs_handler : public specs_setter +{ +public: + typedef typename ParseContext::char_type char_type; -#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_CONSTEXPR dynamic_specs_handler(dynamic_format_specs &specs, ParseContext &ctx) + : specs_setter(specs) + , specs_(specs) + , context_(ctx) + { } -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 " + FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler &other) + : specs_setter(other) + , specs_(other.specs_) + , context_(other.context_) + { + } - \endrst - */ -template -inline StrFormatSpec pad(const Char *str, unsigned width, Char fill = ' ') -{ - return StrFormatSpec(str, width, fill); -} + template + FMT_CONSTEXPR void on_dynamic_width(Id arg_id) + { + specs_.width_ref = make_arg_ref(arg_id); + } -inline StrFormatSpec pad(const wchar_t *str, unsigned width, char fill = ' ') -{ - return StrFormatSpec(str, width, fill); -} + template + FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) + { + specs_.precision_ref = make_arg_ref(arg_id); + } -namespace internal { + FMT_CONSTEXPR void on_error(const char *message) + { + context_.on_error(message); + } -template -class ArgMap -{ private: - typedef std::vector, internal::Arg>> MapType; - typedef typename MapType::value_type Pair; + typedef arg_ref arg_ref_type; - MapType map_; - -public: - void init(const ArgList &args); + template + FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) + { + context_.check_arg_id(arg_id); + return arg_ref_type(arg_id); + } - const internal::Arg *find(const fmt::BasicStringRef &name) const + FMT_CONSTEXPR arg_ref_type make_arg_ref(auto_id) { - // 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 FMT_NULL; + return arg_ref_type(context_.next_arg_id()); } + + dynamic_format_specs &specs_; + ParseContext &context_; }; -template -void ArgMap::init(const ArgList &args) +template +FMT_CONSTEXPR Iterator parse_arg_id(Iterator it, IDHandler &&handler) { - if (!map_.empty()) - return; - typedef internal::NamedArg NamedArg; - const NamedArg *named_arg = FMT_NULL; - bool use_values = args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; - if (use_values) + typedef typename std::iterator_traits::value_type char_type; + char_type c = *it; + if (c == '}' || c == ':') { - 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; + handler(); + return it; } - for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) + if (c >= '0' && c <= '9') { - internal::Arg::Type arg_type = args.type(i); - if (arg_type == internal::Arg::NAMED_ARG) + unsigned index = parse_nonnegative_int(it, handler); + if (*it != '}' && *it != ':') { - named_arg = static_cast(args.args_[i].pointer); - map_.push_back(Pair(named_arg->name, *named_arg)); + handler.on_error("invalid format string"); + return it; } + handler(index); + return it; } - for (unsigned i = ArgList::MAX_PACKED_ARGS; /*nothing*/; ++i) + if (!is_name_start(c)) { - 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*/ - ; - } + handler.on_error("invalid format string"); + return it; } + auto start = it; + do + { + c = *++it; + } while (is_name_start(c) || ('0' <= c && c <= '9')); + handler(basic_string_view(pointer_from(start), to_unsigned(it - start))); + return it; } -template -class ArgFormatterBase : public ArgVisitor +// Adapts SpecHandler to IDHandler API for dynamic width. +template +struct width_adapter { -private: - BasicWriter &writer_; - Spec &spec_; + explicit FMT_CONSTEXPR width_adapter(SpecHandler &h) + : handler(h) + { + } - FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase); + FMT_CONSTEXPR void operator()() + { + handler.on_dynamic_width(auto_id()); + } + FMT_CONSTEXPR void operator()(unsigned id) + { + handler.on_dynamic_width(id); + } + FMT_CONSTEXPR void operator()(basic_string_view id) + { + handler.on_dynamic_width(id); + } - void write_pointer(const void *p) + FMT_CONSTEXPR void on_error(const char *message) { - spec_.flags_ = HASH_FLAG; - spec_.type_ = 'x'; - writer_.write_int(reinterpret_cast(p), spec_); + handler.on_error(message); } - // workaround MSVC two-phase lookup issue - typedef internal::Arg Arg; + SpecHandler &handler; +}; -protected: - BasicWriter &writer() +// Adapts SpecHandler to IDHandler API for dynamic precision. +template +struct precision_adapter +{ + explicit FMT_CONSTEXPR precision_adapter(SpecHandler &h) + : handler(h) { - return writer_; } - Spec &spec() + + FMT_CONSTEXPR void operator()() { - return spec_; + handler.on_dynamic_precision(auto_id()); } - - void write(bool value) + FMT_CONSTEXPR void operator()(unsigned id) { - const char *str_value = value ? "true" : "false"; - Arg::StringValue str = {str_value, std::strlen(str_value)}; - writer_.write_str(str, spec_); + handler.on_dynamic_precision(id); + } + FMT_CONSTEXPR void operator()(basic_string_view id) + { + handler.on_dynamic_precision(id); } - void write(const char *value) + FMT_CONSTEXPR void on_error(const char *message) { - Arg::StringValue str = {value, value ? std::strlen(value) : 0}; - writer_.write_str(str, spec_); + handler.on_error(message); } -public: - typedef Spec SpecType; + SpecHandler &handler; +}; + +// Parses standard format specifiers and sends notifications about parsed +// components to handler. +// it: an iterator pointing to the beginning of a null-terminated range of +// characters, possibly emulated via null_terminating_iterator, representing +// format specifiers. +template +FMT_CONSTEXPR Iterator parse_format_specs(Iterator it, SpecHandler &&handler) +{ + typedef typename std::iterator_traits::value_type char_type; + char_type c = *it; + if (c == '}' || !c) + return it; + + // Parse fill and alignment. + alignment align = ALIGN_DEFAULT; + int i = 1; + do + { + auto p = it + i; + switch (*p) + { + case '<': + align = ALIGN_LEFT; + break; + case '>': + align = ALIGN_RIGHT; + break; + case '=': + align = ALIGN_NUMERIC; + break; + case '^': + align = ALIGN_CENTER; + break; + } + if (align != ALIGN_DEFAULT) + { + if (p != it) + { + if (c == '{') + { + handler.on_error("invalid fill character '{'"); + return it; + } + it += 2; + handler.on_fill(c); + } + else + ++it; + handler.on_align(align); + break; + } + } while (--i >= 0); - ArgFormatterBase(BasicWriter &w, Spec &s) - : writer_(w) - , spec_(s) + // Parse sign. + switch (*it) { + case '+': + handler.on_plus(); + ++it; + break; + case '-': + handler.on_minus(); + ++it; + break; + case ' ': + handler.on_space(); + ++it; + break; } - template - void visit_any_int(T value) + if (*it == '#') { - writer_.write_int(value, spec_); + handler.on_hash(); + ++it; } - template - void visit_any_double(T value) + // Parse zero flag. + if (*it == '0') { - writer_.write_double(value, spec_); + handler.on_zero(); + ++it; } - void visit_bool(bool value) + // Parse width. + if ('0' <= *it && *it <= '9') + { + handler.on_width(parse_nonnegative_int(it, handler)); + } + else if (*it == '{') { - if (spec_.type_) + it = parse_arg_id(it + 1, width_adapter(handler)); + if (*it++ != '}') { - visit_any_int(value); - return; + handler.on_error("invalid format string"); + return it; } - write(value); } - void visit_char(int value) + // Parse precision. + if (*it == '.') { - if (spec_.type_ && spec_.type_ != 'c') + ++it; + if ('0' <= *it && *it <= '9') { - spec_.flags_ |= CHAR_FLAG; - writer_.write_int(value, spec_); - return; + handler.on_precision(parse_nonnegative_int(it, handler)); } - 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_SIZE = 1; - if (spec_.width_ > CHAR_SIZE) + else if (*it == '{') { - out = writer_.grow_buffer(spec_.width_); - if (spec_.align_ == ALIGN_RIGHT) - { - std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill); - out += spec_.width_ - CHAR_SIZE; - } - else if (spec_.align_ == ALIGN_CENTER) + it = parse_arg_id(it + 1, precision_adapter(handler)); + if (*it++ != '}') { - out = writer_.fill_padding(out, spec_.width_, internal::const_check(CHAR_SIZE), fill); - } - else - { - std::uninitialized_fill_n(out + CHAR_SIZE, spec_.width_ - CHAR_SIZE, fill); + handler.on_error("invalid format string"); + return it; } } else { - out = writer_.grow_buffer(CHAR_SIZE); + handler.on_error("missing precision specifier"); + return it; } - *out = internal::CharTraits::cast(value); + handler.end_precision(); } - void visit_cstring(const char *value) + // Parse type. + if (*it != '}' && *it) + handler.on_type(*it++); + return it; +} + +template +struct id_adapter +{ + FMT_CONSTEXPR explicit id_adapter(Handler &h) + : handler(h) { - if (spec_.type_ == 'p') - return write_pointer(value); - write(value); } - // Qualification with "internal" here and below is a workaround for nvcc. - void visit_string(internal::Arg::StringValue value) + FMT_CONSTEXPR void operator()() { - writer_.write_str(value, spec_); + handler.on_arg_id(); } - - using ArgVisitor::visit_wstring; - - void visit_wstring(internal::Arg::StringValue value) + FMT_CONSTEXPR void operator()(unsigned id) { - writer_.write_str(value, spec_); + handler.on_arg_id(id); } - - void visit_pointer(const void *value) + FMT_CONSTEXPR void operator()(basic_string_view id) { - if (spec_.type_ && spec_.type_ != 'p') - report_unknown_type(spec_.type_, "pointer"); - write_pointer(value); + handler.on_arg_id(id); } -}; - -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) + FMT_CONSTEXPR void on_error(const char *message) { - args_ = args; - next_arg_index_ = 0; + handler.on_error(message); } - // 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(); - } + Handler &handler; +}; - // Checks if manual indexing is used and returns the argument with - // specified index. - Arg get_arg(unsigned arg_index, const char *&error) +template +FMT_CONSTEXPR void parse_format_string(Iterator it, Handler &&handler) +{ + typedef typename std::iterator_traits::value_type char_type; + auto start = it; + while (*it) { - return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg(); - } + char_type ch = *it++; + if (ch != '{' && ch != '}') + continue; + if (*it == ch) + { + handler.on_text(start, it); + start = ++it; + continue; + } + if (ch == '}') + { + handler.on_error("unmatched '}' in format string"); + return; + } + handler.on_text(start, it - 1); - bool check_no_auto_index(const char *&error) - { - if (next_arg_index_ > 0) + it = parse_arg_id(it, id_adapter(handler)); + if (*it == '}') { - error = "cannot switch from automatic to manual argument indexing"; - return false; + handler.on_replacement_field(it); + } + else if (*it == ':') + { + ++it; + it = handler.on_format_specs(it); + if (*it != '}') + { + handler.on_error("unknown format specifier"); + return; + } + } + else + { + handler.on_error("missing '}' in format string"); + return; } - 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)); + start = ++it; } -}; -} // namespace internal + handler.on_text(start, it); +} -/** - \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 +template +FMT_CONSTEXPR const typename ParseContext::char_type *parse_format_specs(ParseContext &ctx) { -private: - BasicFormatter &formatter_; - const Char *format_; + // GCC 7.2 requires initializer. + formatter f{}; + return f.parse(ctx); +} +template +class format_string_checker +{ 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, Spec &spec, const Char *fmt) - : internal::ArgFormatterBase(formatter.writer(), spec) - , formatter_(formatter) - , format_(fmt) + explicit FMT_CONSTEXPR format_string_checker(basic_string_view format_str, ErrorHandler eh) + : arg_id_(-1) + , context_(format_str, eh) + , parse_funcs_{&parse_format_specs...} { } - /** Formats an argument of a custom (user-defined) type. */ - void visit_custom(internal::Arg::CustomValue c) + FMT_CONSTEXPR void on_text(const Char *, const Char *) {} + + FMT_CONSTEXPR void on_arg_id() { - c.format(&formatter_, c.value, &format_); + arg_id_ = context_.next_arg_id(); + check_arg_id(); } -}; - -/** The default argument formatter. */ -template -class ArgFormatter : public BasicArgFormatter, Char, FormatSpec> -{ -public: - /** Constructs an argument formatter object. */ - ArgFormatter(BasicFormatter &formatter, FormatSpec &spec, const Char *fmt) - : BasicArgFormatter, Char, FormatSpec>(formatter, spec, fmt) + FMT_CONSTEXPR void on_arg_id(unsigned id) { + arg_id_ = id; + context_.check_arg_id(id); + check_arg_id(); } -}; - -/** 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; + FMT_CONSTEXPR void on_arg_id(basic_string_view) {} -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); + FMT_CONSTEXPR void on_replacement_field(const Char *) {} - // 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); + FMT_CONSTEXPR const Char *on_format_specs(const Char *s) + { + context_.advance_to(s); + return to_unsigned(arg_id_) < NUM_ARGS ? parse_funcs_[arg_id_](context_) : 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) + FMT_CONSTEXPR void on_error(const char *message) { + context_.on_error(message); } - /** Returns a reference to the writer associated with this formatter. */ - BasicWriter &writer() +private: + typedef basic_parse_context parse_context_type; + enum { - return writer_; + NUM_ARGS = sizeof...(Args) + }; + + FMT_CONSTEXPR void check_arg_id() + { + if (internal::to_unsigned(arg_id_) >= NUM_ARGS) + context_.on_error("argument index out of range"); } - /** Formats stored arguments and writes the output to the writer. */ - void format(BasicCStringRef format_str); + // Format specifier parsing function. + typedef const Char *(*parse_func)(parse_context_type &); - // Formats a single argument and advances format_str, a format string pointer. - const Char *format(const Char *&format_str, const internal::Arg &arg); + int arg_id_; + parse_context_type context_; + parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1]; }; -// 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() +template +FMT_CONSTEXPR bool check_format_string(basic_string_view s, ErrorHandler eh = ErrorHandler()) { - return 0; + format_string_checker checker(s, eh); + parse_format_string(s.begin(), checker); + return true; } -template -inline uint64_t make_type(const T &arg) +template +void check_format_string(String format_str) { - return MakeValue>::type(arg); + FMT_CONSTEXPR_DECL bool invalid_format = + internal::check_format_string(string_view(format_str.data(), format_str.size())); + (void)invalid_format; } -template -struct ArgArray; - -template -struct ArgArray +// Specifies whether to format T using the standard formatter. +// It is not possible to use get_type in formatter specialization directly +// because of a bug in MSVC. +template +struct format_type : std::integral_constant::value != custom_type> { - // '+' is used to silence GCC -Wduplicated-branches warning. - 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 +// Specifies whether to format enums. +template +struct format_enum : std::integral_constant::value> { - 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 +template class Handler, typename Spec, typename Context> +void handle_dynamic_spec(Spec &value, arg_ref ref, Context &ctx) { - uint64_t type; - - ArgType() - : type(0) - { - } - - template - ArgType(const T &arg) - : type(make_type(arg)) + typedef typename Context::char_type char_type; + switch (ref.kind) { + case arg_ref::NONE: + break; + case arg_ref::INDEX: + internal::set_dynamic_spec(value, ctx.get_arg(ref.index), ctx.error_handler()); + break; + case arg_ref::NAME: + internal::set_dynamic_spec(value, ctx.get_arg(ref.name), ctx.error_handler()); + break; } -}; - -#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>(v##n) -#define FMT_ASSIGN_wchar_t(n) arr[n] = fmt::internal::MakeValue>(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)); \ - } +/** The default argument formatter. */ +template +class arg_formatter : public internal::function::iterator>, + public internal::arg_formatter_base +{ +private: + typedef typename Range::value_type char_type; + typedef internal::arg_formatter_base base; + typedef basic_format_context context_type; -#else + context_type &ctx_; -#define FMT_MAKE_REF(n) fmt::internal::MakeValue>(v##n) -#define FMT_MAKE_REF2(n) v##n +public: + typedef Range range; + typedef typename base::iterator iterator; + typedef typename base::format_specs format_specs; -// 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)); \ + /** + \rst + Constructs an argument formatter object. + *ctx* is a reference to the formatting context, + *spec* contains format specifier information for standard argument types. + \endrst + */ + arg_formatter(context_type &ctx, format_specs &spec) + : base(Range(ctx.out()), spec) + , ctx_(ctx) + { } -// 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 + using base::operator(); -// 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) + /** Formats an argument of a user-defined type. */ + iterator operator()(typename basic_format_arg::handle handle) + { + handle.format(ctx_); + return this->out(); + } +}; /** An error returned by an operating system or a language runtime, for example a file opening error. */ -class SystemError : public internal::RuntimeError +class system_error : public std::runtime_error { private: - FMT_API void init(int err_code, CStringRef format_str, ArgList args); + FMT_API void init(int err_code, string_view format_str, format_args args); protected: int error_code_; - typedef char Char; // For FMT_VARIADIC_CTOR. - - SystemError() {} + system_error() + : std::runtime_error("") + { + } public: /** \rst - Constructs a :class:`fmt::SystemError` object with a description + Constructs a :class:`fmt::system_error` 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 + // This throws a system_error 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); + throw fmt::system_error(errno, "cannot open file '{}'", filename); \endrst */ - SystemError(int error_code, CStringRef message) + template + system_error(int error_code, string_view message, const Args &... args) + : std::runtime_error("") { - init(error_code, message, ArgList()); + init(error_code, message, make_format_args(args...)); } - FMT_DEFAULTED_COPY_CTOR(SystemError) - FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) - - FMT_API ~SystemError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; int error_code() const { @@ -3112,631 +2947,567 @@ class SystemError : public internal::RuntimeError 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; +FMT_API void format_system_error(internal::buffer &out, int error_code, fmt::string_view 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 + This template provides operations for formatting and writing data into a + character range. */ -template -class BasicWriter +template +class basic_writer { +public: + typedef typename Range::value_type char_type; + typedef decltype(internal::declval().begin()) iterator; + typedef basic_format_specs format_specs; + private: - // Output buffer. - Buffer &buffer_; + // Output iterator. + iterator out_; - FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); + std::unique_ptr locale_; - typedef typename internal::CharTraits::CharPtr CharPtr; + FMT_DISALLOW_COPY_AND_ASSIGN(basic_writer); -#if FMT_SECURE_SCL - // Returns pointer value. - static Char *get(CharPtr p) + iterator out() const { - return p.base(); + return out_; } -#else - static Char *get(Char *p) + + // Attempts to reserve space for n extra characters in the output range. + // Returns a pointer to the reserved range or a reference to out_. + auto reserve(std::size_t n) -> decltype(internal::reserve(out_, n)) { - return p; + return internal::reserve(out_, n); } -#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); + // Writes a value in the format + // + // where is written by f(it). + template + void write_padded(std::size_t size, const align_spec &spec, F f); - // Grows the buffer by n characters and returns a pointer to the newly - // allocated area. - CharPtr grow_buffer(std::size_t n) + template + struct padded_int_writer { - std::size_t size = buffer_.size(); - buffer_.resize(size + n); - return internal::make_ptr(&buffer_[size], n); - } + string_view prefix; + char_type fill; + std::size_t padding; + F f; + + template + void operator()(It &&it) const + { + if (prefix.size() != 0) + it = std::copy_n(prefix.data(), prefix.size(), it); + it = std::fill_n(it, padding, fill); + f(it); + } + }; - // Writes an unsigned decimal integer. - template - Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) + // Writes an integer in the format + // + // where are written by f(it). + template + void write_int(unsigned num_digits, string_view prefix, const Spec &spec, F f) { - 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; + std::size_t size = prefix.size() + num_digits; + char_type fill = static_cast(spec.fill()); + std::size_t padding = 0; + if (spec.align() == ALIGN_NUMERIC) + { + if (spec.width() > size) + { + padding = spec.width() - size; + size = spec.width(); + } + } + else if (spec.precision() > static_cast(num_digits)) + { + size = prefix.size() + static_cast(spec.precision()); + padding = static_cast(spec.precision()) - num_digits; + fill = '0'; + } + align_spec as = spec; + if (spec.align() == ALIGN_DEFAULT) + as.align_ = ALIGN_RIGHT; + write_padded(size, as, padded_int_writer{prefix, fill, padding, f}); } // 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)) - { + typedef typename internal::int_traits::main_type main_type; + main_type abs_value = static_cast(value); + bool is_negative = internal::is_negative(value); + if (is_negative) abs_value = 0 - abs_value; - *write_unsigned_decimal(abs_value, 1) = '-'; + unsigned num_digits = internal::count_digits(abs_value); + auto &&it = reserve((is_negative ? 1 : 0) + num_digits); + if (is_negative) + *it++ = '-'; + internal::format_decimal(it, abs_value, num_digits); + } + + // The handle_int_type_spec handler that writes an integer. + template + struct int_writer + { + typedef typename internal::int_traits::main_type unsigned_type; + + basic_writer &writer; + const Spec &spec; + unsigned_type abs_value; + char prefix[4]; + unsigned prefix_size; + + string_view get_prefix() const + { + return string_view(prefix, prefix_size); } - else + + // Counts the number of digits in abs_value. BITS = log2(radix). + template + unsigned count_digits() const { - write_unsigned_decimal(abs_value, 0); + unsigned_type n = abs_value; + unsigned num_digits = 0; + do + { + ++num_digits; + } while ((n >>= BITS) != 0); + return num_digits; } - } - // 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; - } + int_writer(basic_writer &w, Int value, const Spec &s) + : writer(w) + , spec(s) + , abs_value(static_cast(value)) + , prefix_size(0) + { + 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; + } + } - template - CharPtr prepare_int_buffer(unsigned num_digits, const Spec &spec, const char *prefix, unsigned prefix_size); + struct dec_writer + { + unsigned_type abs_value; + unsigned num_digits; - // Formats an integer. - template - void write_int(T value, Spec spec); + template + void operator()(It &&it) const + { + it = internal::format_decimal(it, abs_value, num_digits); + } + }; - // Formats a floating-point number (double or long double). - template - void write_double(T value, const Spec &spec); + void on_dec() + { + unsigned num_digits = internal::count_digits(abs_value); + writer.write_int(num_digits, get_prefix(), spec, dec_writer{abs_value, num_digits}); + } - // Writes a formatted string. - template - CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec); + struct hex_writer + { + int_writer &self; + unsigned num_digits; - template - void write_str(const internal::Arg::StringValue &str, const Spec &spec); + template + void operator()(It &&it) const + { + it = internal::format_uint<4>(it, self.abs_value, num_digits, self.spec.type() != 'x'); + } + }; - // 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); + void on_hex() + { + if (spec.flag(HASH_FLAG)) + { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = static_cast(spec.type()); + } + unsigned num_digits = count_digits<4>(); + writer.write_int(num_digits, get_prefix(), spec, hex_writer{*this, num_digits}); + } - // 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 + struct bin_writer + { + unsigned_type abs_value; + unsigned num_digits; - template - void append_float_length(Char *&, T) - { - } + template + void operator()(It &&it) const + { + it = internal::format_uint(it, abs_value, num_digits); + } + }; - template - friend class internal::ArgFormatterBase; + void on_bin() + { + if (spec.flag(HASH_FLAG)) + { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = static_cast(spec.type()); + } + unsigned num_digits = count_digits<1>(); + writer.write_int(num_digits, get_prefix(), spec, bin_writer<1>{abs_value, num_digits}); + } - template - friend class BasicPrintfArgFormatter; + void on_oct() + { + unsigned num_digits = count_digits<3>(); + if (spec.flag(HASH_FLAG) && spec.precision() <= static_cast(num_digits)) + { + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + prefix[prefix_size++] = '0'; + } + writer.write_int(num_digits, get_prefix(), spec, bin_writer<3>{abs_value, num_digits}); + } -protected: - /** - Constructs a ``BasicWriter`` object. - */ - explicit BasicWriter(Buffer &b) - : buffer_(b) - { - } + enum + { + SEP_SIZE = 1 + }; -public: - /** - \rst - Destroys a ``BasicWriter`` object. - \endrst - */ - virtual ~BasicWriter() {} + struct num_writer + { + unsigned_type abs_value; + unsigned size; + char_type sep; - /** - Returns the total number of characters written. - */ - std::size_t size() const + template + void operator()(It &&it) const + { + basic_string_view s(&sep, SEP_SIZE); + it = format_decimal(it, abs_value, size, internal::add_thousands_sep(s)); + } + }; + + void on_num() + { + unsigned num_digits = internal::count_digits(abs_value); + char_type sep = internal::thousands_sep(writer.locale_.get()); + unsigned size = num_digits + SEP_SIZE * ((num_digits - 1) / 3); + writer.write_int(size, get_prefix(), spec, num_writer{abs_value, size, sep}); + } + + void on_error() + { + FMT_THROW(format_error("invalid type specifier")); + } + }; + + // Writes a formatted integer. + template + void write_int(T value, const Spec &spec) { - return buffer_.size(); + internal::handle_int_type_spec(spec.type(), int_writer(*this, value, spec)); } - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const Char *data() const FMT_NOEXCEPT + enum { - return &buffer_[0]; - } + INF_SIZE = 3 + }; // This is an enum to workaround a bug in MSVC. - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const Char *c_str() const + struct inf_or_nan_writer { - std::size_t size = buffer_.size(); - buffer_.reserve(size + 1); - buffer_[size] = '\0'; - return &buffer_[0]; - } + char sign; + const char *str; - /** - \rst - Returns the content of the output buffer as an `std::string`. - \endrst - */ - std::basic_string str() const + template + void operator()(It &&it) const + { + if (sign) + *it++ = sign; + it = std::copy_n(str, static_cast(INF_SIZE), it); + } + }; + + struct double_writer { - return std::basic_string(&buffer_[0], buffer_.size()); - } + size_t n; + char sign; + basic_memory_buffer &buffer; - /** - \rst - Writes formatted data. + template + void operator()(It &&it) + { + if (sign) + { + *it++ = sign; + --n; + } + it = std::copy_n(buffer.begin(), n, it); + } + }; - *args* is an argument list representing arbitrary arguments. + // Formats a floating-point number (double or long double). + template + void write_double(T value, const format_specs &spec); + template + void write_double_sprintf(T value, const format_specs &spec, internal::basic_buffer &buffer); - **Example**:: + template + struct str_writer + { + const Char *s; + std::size_t size; - MemoryWriter out; - out.write("Current point:\n"); - out.write("({:+f}, {:+f})", -3.14, 3.14); + template + void operator()(It &&it) const + { + it = std::copy_n(s, size, it); + } + }; - This will write the following output to the ``out`` object: + // Writes a formatted string. + template + void write_str(const Char *s, std::size_t size, const align_spec &spec) + { + write_padded(size, spec, str_writer{s, size}); + } - .. code-block:: none + template + void write_str(basic_string_view str, const format_specs &spec); - Current point: - (-3.140000, +3.140000) + // Appends floating-point length specifier to the format string. + // The second argument is only used for overload resolution. + void append_float_length(char_type *&format_ptr, long double) + { + *format_ptr++ = 'L'; + } - The output can be accessed using :func:`data()`, :func:`c_str` or - :func:`str` methods. + template + void append_float_length(char_type *&, T) + { + } - See also :ref:`syntax`. - \endrst - */ - void write(BasicCStringRef format, ArgList args) + template + friend class internal::arg_formatter_base; + +public: + /** Constructs a ``basic_writer`` object. */ + explicit basic_writer(Range out) + : out_(out.begin()) { - BasicFormatter(args, *this).format(format); } - FMT_VARIADIC_VOID(write, BasicCStringRef) - BasicWriter &operator<<(int value) + void write(int value) { write_decimal(value); - return *this; } - BasicWriter &operator<<(unsigned value) + void write(long value) { - return *this << IntFormatSpec(value); + write_decimal(value); } - BasicWriter &operator<<(long value) + void write(long long value) { write_decimal(value); - return *this; } - BasicWriter &operator<<(unsigned long value) + + void write(unsigned value) { - return *this << IntFormatSpec(value); + write_decimal(value); } - BasicWriter &operator<<(LongLong value) + void write(unsigned long value) + { + write_decimal(value); + } + void write(unsigned long long value) { write_decimal(value); - return *this; } /** \rst - Formats *value* and writes it to the stream. + Formats *value* and writes it to the buffer. \endrst */ - BasicWriter &operator<<(ULongLong value) + template + typename std::enable_if::value, void>::type write(T value, FormatSpec spec, FormatSpecs... specs) { - return *this << IntFormatSpec(value); + format_specs s(spec, specs...); + s.align_ = ALIGN_RIGHT; + write_int(value, s); } - BasicWriter &operator<<(double value) + void write(double value) { - write_double(value, FormatSpec()); - return *this; + write_double(value, format_specs()); } /** \rst Formats *value* using the general format for floating-point numbers - (``'g'``) and writes it to the stream. + (``'g'``) and writes it to the buffer. \endrst */ - BasicWriter &operator<<(long double value) + void write(long double value) { - write_double(value, FormatSpec()); - return *this; + write_double(value, format_specs()); } - /** - Writes a character to the stream. - */ - BasicWriter &operator<<(char value) + /** Writes a character to the buffer. */ + void write(char value) { - buffer_.push_back(value); - return *this; + *reserve(1) = value; } - BasicWriter &operator<<(typename internal::WCharHelper::Supported value) + void write(wchar_t value) { - buffer_.push_back(value); - return *this; + internal::require_wchar(); + *reserve(1) = value; } /** \rst - Writes *value* to the stream. + Writes *value* to the buffer. \endrst */ - BasicWriter &operator<<(fmt::BasicStringRef value) + void write(string_view value) { - const Char *str = value.data(); - buffer_.append(str, str + value.size()); - return *this; + auto &&it = reserve(value.size()); + it = std::copy(value.begin(), value.end(), it); } - BasicWriter &operator<<(typename internal::WCharHelper::Supported value) + void write(wstring_view value) { - const char *str = value.data(); - buffer_.append(str, str + value.size()); - return *this; + internal::require_wchar(); + auto &&it = reserve(value.size()); + it = std::uninitialized_copy(value.begin(), value.end(), it); } - template - BasicWriter &operator<<(IntFormatSpec spec) + template + void write(basic_string_view str, FormatSpecs... specs) { - internal::CharTraits::convert(FillChar()); - write_int(spec.value(), spec); - return *this; + write_str(str, format_specs(specs...)); } - 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 + template + typename std::enable_if::value>::type write(const T *p) { - return buffer_; + format_specs specs; + specs.flags_ = HASH_FLAG; + specs.type_ = 'x'; + write_int(reinterpret_cast(p), specs); } }; -template -template -typename BasicWriter::CharPtr BasicWriter::write_str(const StrChar *s, std::size_t size, const AlignSpec &spec) +template +template +void basic_writer::write_padded(std::size_t size, const align_spec &spec, F f) { - CharPtr out = CharPtr(); - if (spec.width() > size) + unsigned width = spec.width(); + if (width <= size) + return f(reserve(size)); + auto &&it = reserve(width); + char_type fill = static_cast(spec.fill()); + std::size_t padding = width - size; + if (spec.align() == ALIGN_RIGHT) { - 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); - } + it = std::fill_n(it, padding, fill); + f(it); + } + else if (spec.align() == ALIGN_CENTER) + { + std::size_t left_padding = padding / 2; + it = std::fill_n(it, left_padding, fill); + f(it); + it = std::fill_n(it, padding - left_padding, fill); } else { - out = grow_buffer(size); + f(it); + it = std::fill_n(it, padding, fill); } - std::uninitialized_copy(s, s + size, out); - return out; } +template template -template -void BasicWriter::write_str(const internal::Arg::StringValue &s, const Spec &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")); - } - } +void basic_writer::write_str(basic_string_view s, const format_specs &spec) +{ + const Char *data = s.data(); + std::size_t size = s.size(); 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); + if (spec.precision_ >= 0 && precision < size) + size = precision; + write_str(data, 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) +struct float_spec_handler { - 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) + Char type; + bool upper; + + explicit float_spec_handler(Char t) + : type(t) + , upper(false) { - p = fill_padding(p, width, size, fill); - std::uninitialized_copy(prefix, prefix + prefix_size, p); - p += size; } - else + + void on_general() { - if (align == ALIGN_NUMERIC) - { - if (prefix_size != 0) - { - p = std::uninitialized_copy(prefix, prefix + prefix_size, p); - size -= prefix_size; - } - } + if (type == 'G') + upper = true; else - { - std::uninitialized_copy(prefix, prefix + prefix_size, end - size); - } - std::uninitialized_fill(p, end - size, fill); - p = end; + type = 'g'; } - 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)) + void on_exp() { - 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_prefix(); - } - 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': + if (type == 'E') + upper = true; + } + + void on_fixed() { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) + if (type == 'F') { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type_prefix(); + upper = true; +#if FMT_MSC_VER + // MSVC's printf doesn't support 'F'. + type = 'f'; +#endif } - 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': + + void on_hex() { - 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; + if (type == 'A') + upper = true; } - case 'n': + + void on_error() { - unsigned num_digits = internal::count_digits(abs_value); - fmt::StringRef sep = ""; -#if !(defined(ANDROID) || defined(__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; + FMT_THROW(format_error("invalid type specifier")); } -} +}; -template -template -void BasicWriter::write_double(T value, const Spec &spec) +template +template +void basic_writer::write_double(T value, const format_specs &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; - } + float_spec_handler handler(spec.type()); + internal::handle_float_type_spec(spec.type(), handler); char sign = 0; // Use isnegative instead of value < 0 because the latter is always // false for NaN. - if (internal::FPUtil::isnegative(static_cast(value))) + if (internal::fputil::isnegative(static_cast(value))) { sign = '-'; value = -value; @@ -3746,72 +3517,100 @@ void BasicWriter::write_double(T value, const Spec &spec) sign = spec.flag(PLUS_FLAG) ? '+' : ' '; } - if (internal::FPUtil::isnotanumber(value)) + struct write_inf_or_nan_t { - // 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) + basic_writer &writer; + format_specs spec; + char sign; + void operator()(const char *str) const { - --nan_size; - ++nan; + writer.write_padded(INF_SIZE + (sign ? 1 : 0), spec, inf_or_nan_writer{sign, str}); } - CharPtr out = write_str(nan, nan_size, spec); - if (sign) - *out = sign; - return; + } write_inf_or_nan = {*this, spec, sign}; + + // Format NaN and ininity ourselves because sprintf's output is not consistent + // across platforms. + if (internal::fputil::isnotanumber(value)) + return write_inf_or_nan(handler.upper ? "NAN" : "nan"); + if (internal::fputil::isinfinity(value)) + return write_inf_or_nan(handler.upper ? "INF" : "inf"); + + basic_memory_buffer buffer; + if (FMT_USE_GRISU && sizeof(T) <= sizeof(double) && std::numeric_limits::is_iec559) + { + internal::fp fp_value(static_cast(value)); + fp_value.normalize(); + // Find a cached power of 10 close to 1 / fp_value. + int dec_exp = 0; + const int min_exp = -60; + auto dec_pow = internal::get_cached_power(min_exp - (fp_value.e + internal::fp::significand_size), dec_exp); + internal::fp product = fp_value * dec_pow; + // Generate output. + internal::fp one(1ull << -product.e, product.e); + uint64_t hi = product.f >> -one.e; + uint64_t f = product.f & (one.f - 1); + typedef back_insert_range> range; + basic_writer w{range(buffer)}; + w.write(hi); + size_t digits = buffer.size(); + w.write('.'); + const unsigned max_digits = 18; + while (digits++ < max_digits) + { + f *= 10; + w.write(static_cast('0' + (f >> -one.e))); + f &= one.f - 1; + } + w.write('e'); + w.write(-dec_exp); } - - if (internal::FPUtil::isinfinity(value)) + else + { + format_specs normalized_spec(spec); + normalized_spec.type_ = handler.type; + write_double_sprintf(value, normalized_spec, buffer); + } + size_t n = buffer.size(); + align_spec as = spec; + if (spec.align() == ALIGN_NUMERIC) { - // 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) + if (sign) { - --inf_size; - ++inf; + auto &&it = reserve(1); + *it++ = sign; + sign = 0; + if (as.width_) + --as.width_; } - CharPtr out = write_str(inf, inf_size, spec); - if (sign) - *out = sign; - return; + as.align_ = ALIGN_RIGHT; } - - std::size_t offset = buffer_.size(); - unsigned width = spec.width(); - if (sign) + else { - buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u)); - if (width > 0) - --width; - ++offset; + if (spec.align() == ALIGN_DEFAULT) + as.align_ = ALIGN_RIGHT; + if (sign) + ++n; } + write_padded(n, as, double_writer{n, sign, buffer}); +} + +template +template +void basic_writer::write_double_sprintf(T value, const format_specs &spec, internal::basic_buffer &buffer) +{ + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buffer.capacity() != 0, "empty buffer"); // Build format string. enum { MAX_FORMAT_SIZE = 10 }; // longest format: %#-*.*Lg - Char format[MAX_FORMAT_SIZE]; - Char *format_ptr = format; + char_type format[MAX_FORMAT_SIZE]; + char_type *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++ = '.'; @@ -3819,221 +3618,51 @@ void BasicWriter::write_double(T value, const Spec &spec) } append_float_length(format_ptr, value); - *format_ptr++ = type; + *format_ptr++ = spec.type(); *format_ptr = '\0'; // Format using snprintf. - Char fill = internal::CharTraits::cast(spec.fill()); - unsigned n = 0; - Char *start = FMT_NULL; + char_type *start = FMT_NULL; 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); + std::size_t buffer_size = buffer.capacity(); + start = &buffer[0]; + int result = internal::char_traits::format_float(start, buffer_size, format, spec.precision(), value); if (result >= 0) { - n = internal::to_unsigned(result); - if (offset + n < buffer_.capacity()) + unsigned n = internal::to_unsigned(result); + if (n < buffer.capacity()) + { + buffer.resize(n); break; // The buffer is large enough - continue with formatting. - buffer_.reserve(offset + n + 1); + } + buffer.reserve(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; + buffer.reserve(buffer.capacity() + 1); } - 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; +FMT_API void report_system_error(int error_code, string_view message) FMT_NOEXCEPT; #if FMT_USE_WINDOWS_H /** A Windows error. */ -class WindowsError : public SystemError +class windows_error : public system_error { private: - FMT_API void init(int error_code, CStringRef format_str, ArgList args); + FMT_API void init(int error_code, string_view format_str, format_args args); public: /** \rst - Constructs a :class:`fmt::WindowsError` object with the description + Constructs a :class:`fmt::windows_error` object with the description of the form .. parsed-literal:: @@ -4047,115 +3676,48 @@ class WindowsError : public SystemError **Example**:: - // This throws a WindowsError with the description + // This throws a windows_error 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); + throw fmt::windows_error(GetLastError(), + "cannot open file '{}'", filename); } \endrst */ - WindowsError(int error_code, CStringRef message) + template + windows_error(int error_code, string_view message, const Args &... args) { - init(error_code, message, ArgList()); + init(error_code, message, make_format_args(args...)); } - 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; +FMT_API void report_windows_error(int error_code, string_view 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 +/** Fast integer formatter. */ +class format_int { 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 + 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) + // Formats value in reverse and returns a pointer to the beginning. + char *format_decimal(unsigned long long value) { - char *buffer_end = buffer_ + BUFFER_SIZE - 1; + char *ptr = buffer_ + BUFFER_SIZE - 1; while (value >= 100) { // Integer division is slow so do it for a group of two digits instead @@ -4163,23 +3725,23 @@ class FormatInt // "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]; + *--ptr = internal::data::DIGITS[index + 1]; + *--ptr = internal::data::DIGITS[index]; } if (value < 10) { - *--buffer_end = static_cast('0' + value); - return buffer_end; + *--ptr = static_cast('0' + value); + return ptr; } unsigned index = static_cast(value * 2); - *--buffer_end = internal::Data::DIGITS[index + 1]; - *--buffer_end = internal::Data::DIGITS[index]; - return buffer_end; + *--ptr = internal::data::DIGITS[index + 1]; + *--ptr = internal::data::DIGITS[index]; + return ptr; } - void FormatSigned(LongLong value) + void format_signed(long long value) { - ULongLong abs_value = static_cast(value); + unsigned long long abs_value = static_cast(value); bool negative = value < 0; if (negative) abs_value = 0 - abs_value; @@ -4189,27 +3751,27 @@ class FormatInt } public: - explicit FormatInt(int value) + explicit format_int(int value) { - FormatSigned(value); + format_signed(value); } - explicit FormatInt(long value) + explicit format_int(long value) { - FormatSigned(value); + format_signed(value); } - explicit FormatInt(LongLong value) + explicit format_int(long long value) { - FormatSigned(value); + format_signed(value); } - explicit FormatInt(unsigned value) + explicit format_int(unsigned value) : str_(format_decimal(value)) { } - explicit FormatInt(unsigned long value) + explicit format_int(unsigned long value) : str_(format_decimal(value)) { } - explicit FormatInt(ULongLong value) + explicit format_int(unsigned long long value) : str_(format_decimal(value)) { } @@ -4256,8 +3818,8 @@ class FormatInt template inline void format_decimal(char *&buffer, T value) { - typedef typename internal::IntTraits::MainType MainType; - MainType abs_value = static_cast(value); + typedef typename internal::int_traits::main_type main_type; + main_type abs_value = static_cast(value); if (internal::is_negative(value)) { *buffer++ = '-'; @@ -4267,572 +3829,771 @@ inline void format_decimal(char *&buffer, T value) { if (abs_value < 10) { - *buffer++ = static_cast('0' + abs_value); - return; + *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; +} + +// Formatter of objects of type T. +template +struct formatter::type, T>::value>::type> +{ + + // Parses format specifiers stopping either at the end of the range or at the + // terminating '}'. + template + FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext &ctx) + { + auto it = internal::null_terminating_iterator(ctx); + typedef internal::dynamic_specs_handler handler_type; + auto type = internal::get_type::type, T>::value; + internal::specs_checker handler(handler_type(specs_, ctx), type); + it = parse_format_specs(it, handler); + auto type_spec = specs_.type(); + auto eh = ctx.error_handler(); + switch (type) + { + case internal::none_type: + case internal::named_arg_type: + FMT_ASSERT(false, "invalid argument type"); + break; + case internal::int_type: + case internal::uint_type: + case internal::long_long_type: + case internal::ulong_long_type: + case internal::bool_type: + handle_int_type_spec(type_spec, internal::int_type_checker(eh)); + break; + case internal::char_type: + handle_char_specs(specs_, internal::char_specs_checker(type_spec, eh)); + break; + case internal::double_type: + case internal::long_double_type: + handle_float_type_spec(type_spec, internal::float_type_checker(eh)); + break; + case internal::cstring_type: + internal::handle_cstring_type_spec(type_spec, internal::cstring_type_checker(eh)); + break; + case internal::string_type: + internal::check_string_type_spec(type_spec, eh); + break; + case internal::pointer_type: + internal::check_pointer_type_spec(type_spec, eh); + break; + case internal::custom_type: + // Custom format specifiers should be checked in parse functions of + // formatter specializations. + break; + } + return pointer_from(it); + } + + template + auto format(const T &val, FormatContext &ctx) -> decltype(ctx.out()) + { + internal::handle_dynamic_spec(specs_.width_, specs_.width_ref, ctx); + internal::handle_dynamic_spec(specs_.precision_, specs_.precision_ref, ctx); + typedef output_range range_type; + return visit(arg_formatter(ctx, specs_), internal::make_arg(val)); + } + +private: + internal::dynamic_format_specs specs_; +}; + +template +struct formatter::value>::type> : public formatter +{ + template + auto parse(ParseContext &ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } +}; + +// A formatter for types known only at run time such as variant alternatives. +// +// Usage: +// typedef std::variant variant; +// template <> +// struct formatter: dynamic_formatter<> { +// void format(buffer &buf, const variant &v, context &ctx) { +// visit([&](const auto &val) { format(buf, val, ctx); }, v); +// } +// }; +template +class dynamic_formatter +{ +private: + struct null_handler : internal::error_handler + { + void on_align(alignment) {} + void on_plus() {} + void on_minus() {} + void on_space() {} + void on_hash() {} + }; + +public: + template + auto parse(ParseContext &ctx) -> decltype(ctx.begin()) + { + auto it = internal::null_terminating_iterator(ctx); + // Checks are deferred to formatting time when the argument type is known. + internal::dynamic_specs_handler handler(specs_, ctx); + it = parse_format_specs(it, handler); + return pointer_from(it); + } + + template + auto format(const T &val, FormatContext &ctx) -> decltype(ctx.out()) + { + handle_specs(ctx); + internal::specs_checker checker(null_handler(), internal::get_type::value); + checker.on_align(specs_.align()); + if (specs_.flags_ == 0) + { + // Do nothing. + } + else if (specs_.flag(SIGN_FLAG)) + { + if (specs_.flag(PLUS_FLAG)) + checker.on_plus(); + else + checker.on_space(); + } + else if (specs_.flag(MINUS_FLAG)) + { + checker.on_minus(); + } + else if (specs_.flag(HASH_FLAG)) + { + checker.on_hash(); } - unsigned index = static_cast(abs_value * 2); - *buffer++ = internal::Data::DIGITS[index]; - *buffer++ = internal::Data::DIGITS[index + 1]; - return; + if (specs_.precision_ != -1) + checker.end_precision(); + typedef output_range range; + visit(arg_formatter(ctx, specs_), internal::make_arg(val)); + return ctx.out(); } - 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**:: +private: + template + void handle_specs(Context &ctx) + { + internal::handle_dynamic_spec(specs_.width_, specs_.width_ref, ctx); + internal::handle_dynamic_spec(specs_.precision_, specs_.precision_ref, ctx); + } - print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); + internal::dynamic_format_specs specs_; +}; - \endrst - */ -template -inline internal::NamedArgWithType arg(StringRef name, const T &arg) +template +typename basic_format_context::format_arg basic_format_context::get_arg(basic_string_view name) { - return internal::NamedArgWithType(name, arg); + map_.init(this->args()); + format_arg arg = map_.find(name); + if (arg.type() == internal::none_type) + this->on_error("argument not found"); + return arg; } -template -inline internal::NamedArgWithType arg(WStringRef name, const T &arg) +template +struct format_handler : internal::error_handler { - return internal::NamedArgWithType(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; -} // namespace fmt - -#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 + typedef internal::null_terminating_iterator iterator; + typedef typename ArgFormatter::range range; -// 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__)) + format_handler(range r, basic_string_view str, basic_format_args format_args) + : context(r.begin(), str, format_args) + { + } -#define FMT_ADD_ARG_NAME(type, index) type arg##index -#define FMT_GET_ARG_NAME(type, index) arg##index + void on_text(iterator begin, iterator end) + { + auto size = internal::to_unsigned(end - begin); + auto out = context.out(); + auto &&it = internal::reserve(out, size); + it = std::copy_n(begin, size, it); + context.advance_to(out); + } -#if FMT_USE_VARIADIC_TEMPLATES -#define FMT_VARIADIC_(Const, Char, ReturnType, func, call, ...) \ - template \ - ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), const Args &... args) Const \ - { \ - 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)); \ + void on_arg_id() + { + arg = context.next_arg(); } -#else -// Defines a wrapper for a function taking __VA_ARGS__ arguments -// and n additional arguments of arbitrary types. -#define FMT_WRAP(Const, Char, ReturnType, func, call, n, ...) \ - template \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), FMT_GEN(n, FMT_MAKE_ARG)) Const \ - { \ - 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)); \ + void on_arg_id(unsigned id) + { + context.parse_context().check_arg_id(id); + arg = context.get_arg(id); + } + void on_arg_id(basic_string_view id) + { + arg = context.get_arg(id); } -#define FMT_VARIADIC_(Const, Char, ReturnType, func, call, ...) \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) Const \ - { \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ - } \ - FMT_WRAP(Const, Char, ReturnType, func, call, 1, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 2, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 3, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 4, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 5, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 6, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 7, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 8, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 9, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 10, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 11, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 12, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 13, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 14, __VA_ARGS__) \ - FMT_WRAP(Const, Char, ReturnType, func, call, 15, __VA_ARGS__) -#endif // FMT_USE_VARIADIC_TEMPLATES + void on_replacement_field(iterator it) + { + context.parse_context().advance_to(pointer_from(it)); + if (visit(internal::custom_formatter(context), arg)) + return; + basic_format_specs specs; + context.advance_to(visit(ArgFormatter(context, specs), arg)); + } -/** - \rst - Defines a variadic function with the specified return type, function name - and argument types passed as variable arguments to this macro. + iterator on_format_specs(iterator it) + { + auto &parse_ctx = context.parse_context(); + parse_ctx.advance_to(pointer_from(it)); + if (visit(internal::custom_formatter(context), arg)) + return iterator(parse_ctx); + basic_format_specs specs; + using internal::specs_handler; + internal::specs_checker> handler(specs_handler(specs, context), arg.type()); + it = parse_format_specs(it, handler); + if (*it != '}') + on_error("missing '}' in format string"); + parse_ctx.advance_to(pointer_from(it)); + context.advance_to(visit(ArgFormatter(context, specs), arg)); + return it; + } - **Example**:: + Context context; + basic_format_arg arg; +}; - 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 *) +/** Formats arguments and writes the output to the range. */ +template +typename Context::iterator vformat_to(typename ArgFormatter::range out, basic_string_view format_str, basic_format_args args) +{ + typedef internal::null_terminating_iterator iterator; + format_handler h(out, format_str, args); + parse_format_string(iterator(format_str.begin(), format_str.end()), h); + return h.context.out(); +} + +// Casts ``p`` to ``const void*`` for pointer formatting. +// Example: +// auto s = format("{}", ptr(p)); +template +inline const void *ptr(const T *p) +{ + return p; +} - ``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 +struct arg_join +{ + It begin; + It end; + basic_string_view sep; - template - void print_error(const char *file, int line, const char *format, - const Args & ... args) { - fmt::print("{}: {}: ", file, line); - fmt::print(format, args...); + arg_join(It begin, It end, basic_string_view sep) + : begin(begin) + , end(end) + , sep(sep) + { } - \endrst - */ -#define FMT_VARIADIC(ReturnType, func, ...) FMT_VARIADIC_(, char, ReturnType, func, return func, __VA_ARGS__) +}; -#define FMT_VARIADIC_CONST(ReturnType, func, ...) FMT_VARIADIC_(const, char, ReturnType, func, return func, __VA_ARGS__) +template +struct formatter, Char> : formatter::value_type, Char> +{ + template + auto format(const arg_join &value, FormatContext &ctx) -> decltype(ctx.out()) + { + typedef formatter::value_type, Char> base; + auto it = value.begin; + auto out = ctx.out(); + if (it != value.end) + { + out = base::format(*it++, ctx); + while (it != value.end) + { + out = std::copy(value.sep.begin(), value.sep.end(), out); + ctx.advance_to(out); + out = base::format(*it++, ctx); + } + } + return out; + } +}; -#define FMT_VARIADIC_W(ReturnType, func, ...) FMT_VARIADIC_(, wchar_t, ReturnType, func, return func, __VA_ARGS__) +template +arg_join join(It begin, It end, string_view sep) +{ + return arg_join(begin, end, sep); +} -#define FMT_VARIADIC_CONST_W(ReturnType, func, ...) FMT_VARIADIC_(const, wchar_t, ReturnType, func, return func, __VA_ARGS__) +template +arg_join join(It begin, It end, wstring_view sep) +{ + return arg_join(begin, end, sep); +} -#define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) +// The following causes ICE in gcc 4.4. +#if FMT_USE_TRAILING_RETURN && (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 405) +template +auto join(const Range &range, string_view sep) -> arg_join +{ + return join(internal::begin(range), internal::end(range), sep); +} -#define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L## #id, id) +template +auto join(const Range &range, wstring_view sep) -> arg_join +{ + return join(internal::begin(range), internal::end(range), sep); +} +#endif /** \rst - Convenient macro to capture the arguments' names and values into several - ``fmt::arg(name, value)``. + Converts *value* to ``std::string`` using the default format for type *T*. **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)); + #include + std::string answer = fmt::to_string(42); \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) +template +std::string to_string(const T &value) { - return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; + std::string str; + internal::container_buffer buf(str); + writer(buf).write(value); + return str; } -// 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) +/** + Converts *value* to ``std::wstring`` using the default format for type *T*. + */ +template +std::wstring to_wstring(const T &value) { - assert('0' <= *s && *s <= '9'); - unsigned value = 0; - // Convert to unsigned to prevent a warning. - unsigned max_int = (std::numeric_limits::max)(); - unsigned big = max_int / 10; - do - { - // Check for overflow. - if (value > big) - { - value = max_int + 1; - break; - } - value = value * 10 + (*s - '0'); - ++s; - } while ('0' <= *s && *s <= '9'); - // Convert to unsigned to prevent a warning. - if (value > max_int) - FMT_THROW(FormatError("number is too big")); - return value; + std::wstring str; + internal::container_buffer buf(str); + wwriter(buf).write(value); + return str; } -inline void require_numeric_argument(const Arg &arg, char spec) +template +std::basic_string to_string(const basic_memory_buffer &buf) { - if (arg.type > Arg::LAST_NUMERIC_TYPE) - { - std::string message = fmt::format("format specifier '{}' requires numeric argument", spec); - FMT_THROW(fmt::FormatError(message)); - } + return std::basic_string(buf.data(), buf.size()); } -template -void check_sign(const Char *&s, const Arg &arg) +inline format_context::iterator vformat_to(internal::buffer &buf, string_view format_str, format_args args) { - 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; + typedef back_insert_range range; + return vformat_to>(buf, format_str, args); } -} // namespace internal -template -inline internal::Arg BasicFormatter::get_arg(BasicStringRef arg_name, const char *&error) +inline wformat_context::iterator vformat_to(internal::wbuffer &buf, wstring_view format_str, wformat_args args) { - 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(); + typedef back_insert_range range; + return vformat_to>(buf, format_str, args); } -template -inline internal::Arg BasicFormatter::parse_arg_index(const Char *&s) +template +inline format_context::iterator format_to(basic_memory_buffer &buf, string_view format_str, const Args &... args) { - const char *error = FMT_NULL; - 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; + return vformat_to(buf, format_str, make_format_args(args...)); } -template -inline internal::Arg BasicFormatter::parse_arg_name(const Char *&s) +template +inline wformat_context::iterator format_to(basic_memory_buffer &buf, wstring_view format_str, const Args &... args) { - 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 = FMT_NULL; - internal::Arg arg = get_arg(BasicStringRef(start, s - start), error); - if (error) - FMT_THROW(FormatError(error)); - return arg; + return vformat_to(buf, format_str, make_format_args(args...)); } -template -const Char *BasicFormatter::format(const Char *&format_str, const internal::Arg &arg) +template +// using format_context_t = basic_format_context; +struct format_context_t { - using internal::Arg; - const Char *s = format_str; - typename ArgFormatter::SpecType 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); - } + typedef basic_format_context type; +}; - // 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; - } +template +// using format_args_t = basic_format_args>; +struct format_args_t +{ + typedef basic_format_args::type> type; +}; - if (*s == '#') - { - require_numeric_argument(arg, '#'); - spec.flags_ |= HASH_FLAG; - ++s; - } +template +inline OutputIt vformat_to(OutputIt out, string_view format_str, typename format_args_t::type args) +{ + typedef output_range range; + return vformat_to>(range(out), format_str, args); +} +template +inline OutputIt vformat_to(OutputIt out, wstring_view format_str, typename format_args_t::type args) +{ + typedef output_range range; + return vformat_to>(range(out), format_str, args); +} - // Parse zero flag. - if (*s == '0') - { - require_numeric_argument(arg, '0'); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; - ++s; - } +/** + \rst + Formats arguments, writes the result to the output iterator ``out`` and returns + the iterator past the end of the output range. - // 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")); - } - unsigned max_int = (std::numeric_limits::max)(); - if (value > max_int) - FMT_THROW(FormatError("number is too big")); - spec.width_ = static_cast(value); - } + **Example**:: - // 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")); - } - unsigned max_int = (std::numeric_limits::max)(); - if (value > max_int) - 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"))); - } - } + std::vector out; + fmt::format_to(std::back_inserter(out), "{}", 42); + \endrst + */ +template +inline OutputIt format_to(OutputIt out, string_view format_str, const Args &... args) +{ + return vformat_to(out, format_str, make_format_args::type>(args...)); +} - // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); - } +template +inline typename std::enable_if::value, std::back_insert_iterator>::type format_to( + std::back_insert_iterator out, string_view format_str, const Args &... args) +{ + return vformat_to(out, format_str, make_format_args(args...)); +} + +template +inline typename std::enable_if::value, std::back_insert_iterator>::type format_to( + std::back_insert_iterator out, wstring_view format_str, const Args &... args) +{ + return vformat_to(out, format_str, make_format_args(args...)); +} + +template +struct format_to_n_result +{ + /** Iterator past the end of the output range. */ + OutputIt out; + /** Total (not truncated) output size. */ + std::size_t size; +}; - if (*s++ != '}') - FMT_THROW(FormatError("missing '}' in format string")); +template +using format_to_n_context = typename fmt::format_context_t>::type; - // Format argument. - ArgFormatter(*this, spec, s - 1).visit(arg); - return s; +template +using format_to_n_args = fmt::basic_format_args>; + +template +inline format_arg_store, Args...> make_format_to_n_args(const Args &... args) +{ + return format_arg_store, Args...>(args...); } -template -void BasicFormatter::format(BasicCStringRef format_str) +template +inline format_to_n_result vformat_to_n(OutputIt out, std::size_t n, string_view format_str, format_to_n_args args) { - 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); + typedef internal::truncating_iterator It; + auto it = vformat_to(It(out, n), format_str, args); + return {it.base(), it.count()}; } -template -struct ArgJoin +/** + \rst + Formats arguments, writes up to ``n`` characters of the result to the output + iterator ``out`` and returns the total output size and the iterator past the + end + of the output range. + \endrst + */ +template +inline format_to_n_result format_to_n(OutputIt out, std::size_t n, string_view format_str, const Args &... args) +{ + return vformat_to_n(out, n, format_str, make_format_to_n_args(args...)); +} +template +inline format_to_n_result format_to_n(OutputIt out, std::size_t n, wstring_view format_str, const Args &... args) { - It first; - It last; - BasicCStringRef sep; + typedef internal::truncating_iterator It; + auto it = vformat_to(It(out, n), format_str, make_format_args::type>(args...)); + return {it.base(), it.count()}; +} - ArgJoin(It first, It last, const BasicCStringRef &sep) - : first(first) - , last(last) - , sep(sep) - { - } -}; +inline std::string vformat(string_view format_str, format_args args) +{ + memory_buffer buffer; + vformat_to(buffer, format_str, args); + return fmt::to_string(buffer); +} -template -ArgJoin join(It first, It last, const BasicCStringRef &sep) +inline std::wstring vformat(wstring_view format_str, wformat_args args) { - return ArgJoin(first, last, sep); + wmemory_buffer buffer; + vformat_to(buffer, format_str, args); + return to_string(buffer); } -template -ArgJoin join(It first, It last, const BasicCStringRef &sep) +template +inline typename std::enable_if::value, std::string>::type format(String format_str, const Args &... args) { - return ArgJoin(first, last, sep); + internal::check_format_string(format_str); + return vformat(format_str.data(), make_format_args(args...)); } -#if FMT_HAS_GXX_CXX11 -template -auto join(const Range &range, const BasicCStringRef &sep) -> ArgJoin +template +inline typename std::enable_if::value>::type print(String format_str, const Args &... args) { - return join(std::begin(range), std::end(range), sep); + internal::check_format_string(format_str); + return vprint(format_str.data(), make_format_args(args...)); } -template -auto join(const Range &range, const BasicCStringRef &sep) -> ArgJoin +/** + Returns the number of characters in the output of + ``format(format_str, args...)``. + */ +template +inline std::size_t formatted_size(string_view format_str, const Args &... args) { - return join(std::begin(range), std::end(range), sep); + auto it = format_to(internal::counting_iterator(), format_str, args...); + return it.count(); } -#endif -template -void format_arg(fmt::BasicFormatter &f, const Char *&format_str, const ArgJoin &e) +// Experimental color support. +#ifdef FMT_EXTENDED_COLORS +enum class color : uint32_t +{ + alice_blue = 0xF0F8FF, // rgb(240,248,255) + antique_white = 0xFAEBD7, // rgb(250,235,215) + aqua = 0x00FFFF, // rgb(0,255,255) + aquamarine = 0x7FFFD4, // rgb(127,255,212) + azure = 0xF0FFFF, // rgb(240,255,255) + beige = 0xF5F5DC, // rgb(245,245,220) + bisque = 0xFFE4C4, // rgb(255,228,196) + black = 0x000000, // rgb(0,0,0) + blanched_almond = 0xFFEBCD, // rgb(255,235,205) + blue = 0x0000FF, // rgb(0,0,255) + blue_violet = 0x8A2BE2, // rgb(138,43,226) + brown = 0xA52A2A, // rgb(165,42,42) + burly_wood = 0xDEB887, // rgb(222,184,135) + cadet_blue = 0x5F9EA0, // rgb(95,158,160) + chartreuse = 0x7FFF00, // rgb(127,255,0) + chocolate = 0xD2691E, // rgb(210,105,30) + coral = 0xFF7F50, // rgb(255,127,80) + cornflower_blue = 0x6495ED, // rgb(100,149,237) + cornsilk = 0xFFF8DC, // rgb(255,248,220) + crimson = 0xDC143C, // rgb(220,20,60) + cyan = 0x00FFFF, // rgb(0,255,255) + dark_blue = 0x00008B, // rgb(0,0,139) + dark_cyan = 0x008B8B, // rgb(0,139,139) + dark_golden_rod = 0xB8860B, // rgb(184,134,11) + dark_gray = 0xA9A9A9, // rgb(169,169,169) + dark_green = 0x006400, // rgb(0,100,0) + dark_khaki = 0xBDB76B, // rgb(189,183,107) + dark_magenta = 0x8B008B, // rgb(139,0,139) + dark_olive_green = 0x556B2F, // rgb(85,107,47) + dark_orange = 0xFF8C00, // rgb(255,140,0) + dark_orchid = 0x9932CC, // rgb(153,50,204) + dark_red = 0x8B0000, // rgb(139,0,0) + dark_salmon = 0xE9967A, // rgb(233,150,122) + dark_sea_green = 0x8FBC8F, // rgb(143,188,143) + dark_slate_blue = 0x483D8B, // rgb(72,61,139) + dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) + dark_turquoise = 0x00CED1, // rgb(0,206,209) + dark_violet = 0x9400D3, // rgb(148,0,211) + deep_pink = 0xFF1493, // rgb(255,20,147) + deep_sky_blue = 0x00BFFF, // rgb(0,191,255) + dim_gray = 0x696969, // rgb(105,105,105) + dodger_blue = 0x1E90FF, // rgb(30,144,255) + fire_brick = 0xB22222, // rgb(178,34,34) + floral_white = 0xFFFAF0, // rgb(255,250,240) + forest_green = 0x228B22, // rgb(34,139,34) + fuchsia = 0xFF00FF, // rgb(255,0,255) + gainsboro = 0xDCDCDC, // rgb(220,220,220) + ghost_white = 0xF8F8FF, // rgb(248,248,255) + gold = 0xFFD700, // rgb(255,215,0) + golden_rod = 0xDAA520, // rgb(218,165,32) + gray = 0x808080, // rgb(128,128,128) + green = 0x008000, // rgb(0,128,0) + green_yellow = 0xADFF2F, // rgb(173,255,47) + honey_dew = 0xF0FFF0, // rgb(240,255,240) + hot_pink = 0xFF69B4, // rgb(255,105,180) + indian_red = 0xCD5C5C, // rgb(205,92,92) + indigo = 0x4B0082, // rgb(75,0,130) + ivory = 0xFFFFF0, // rgb(255,255,240) + khaki = 0xF0E68C, // rgb(240,230,140) + lavender = 0xE6E6FA, // rgb(230,230,250) + lavender_blush = 0xFFF0F5, // rgb(255,240,245) + lawn_green = 0x7CFC00, // rgb(124,252,0) + lemon_chiffon = 0xFFFACD, // rgb(255,250,205) + light_blue = 0xADD8E6, // rgb(173,216,230) + light_coral = 0xF08080, // rgb(240,128,128) + light_cyan = 0xE0FFFF, // rgb(224,255,255) + light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) + light_gray = 0xD3D3D3, // rgb(211,211,211) + light_green = 0x90EE90, // rgb(144,238,144) + light_pink = 0xFFB6C1, // rgb(255,182,193) + light_salmon = 0xFFA07A, // rgb(255,160,122) + light_sea_green = 0x20B2AA, // rgb(32,178,170) + light_sky_blue = 0x87CEFA, // rgb(135,206,250) + light_slate_gray = 0x778899, // rgb(119,136,153) + light_steel_blue = 0xB0C4DE, // rgb(176,196,222) + light_yellow = 0xFFFFE0, // rgb(255,255,224) + lime = 0x00FF00, // rgb(0,255,0) + lime_green = 0x32CD32, // rgb(50,205,50) + linen = 0xFAF0E6, // rgb(250,240,230) + magenta = 0xFF00FF, // rgb(255,0,255) + maroon = 0x800000, // rgb(128,0,0) + medium_aquamarine = 0x66CDAA, // rgb(102,205,170) + medium_blue = 0x0000CD, // rgb(0,0,205) + medium_orchid = 0xBA55D3, // rgb(186,85,211) + medium_purple = 0x9370DB, // rgb(147,112,219) + medium_sea_green = 0x3CB371, // rgb(60,179,113) + medium_slate_blue = 0x7B68EE, // rgb(123,104,238) + medium_spring_green = 0x00FA9A, // rgb(0,250,154) + medium_turquoise = 0x48D1CC, // rgb(72,209,204) + medium_violet_red = 0xC71585, // rgb(199,21,133) + midnight_blue = 0x191970, // rgb(25,25,112) + mint_cream = 0xF5FFFA, // rgb(245,255,250) + misty_rose = 0xFFE4E1, // rgb(255,228,225) + moccasin = 0xFFE4B5, // rgb(255,228,181) + navajo_white = 0xFFDEAD, // rgb(255,222,173) + navy = 0x000080, // rgb(0,0,128) + old_lace = 0xFDF5E6, // rgb(253,245,230) + olive = 0x808000, // rgb(128,128,0) + olive_drab = 0x6B8E23, // rgb(107,142,35) + orange = 0xFFA500, // rgb(255,165,0) + orange_red = 0xFF4500, // rgb(255,69,0) + orchid = 0xDA70D6, // rgb(218,112,214) + pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) + pale_green = 0x98FB98, // rgb(152,251,152) + pale_turquoise = 0xAFEEEE, // rgb(175,238,238) + pale_violet_red = 0xDB7093, // rgb(219,112,147) + papaya_whip = 0xFFEFD5, // rgb(255,239,213) + peach_puff = 0xFFDAB9, // rgb(255,218,185) + peru = 0xCD853F, // rgb(205,133,63) + pink = 0xFFC0CB, // rgb(255,192,203) + plum = 0xDDA0DD, // rgb(221,160,221) + powder_blue = 0xB0E0E6, // rgb(176,224,230) + purple = 0x800080, // rgb(128,0,128) + rebecca_purple = 0x663399, // rgb(102,51,153) + red = 0xFF0000, // rgb(255,0,0) + rosy_brown = 0xBC8F8F, // rgb(188,143,143) + royal_blue = 0x4169E1, // rgb(65,105,225) + saddle_brown = 0x8B4513, // rgb(139,69,19) + salmon = 0xFA8072, // rgb(250,128,114) + sandy_brown = 0xF4A460, // rgb(244,164,96) + sea_green = 0x2E8B57, // rgb(46,139,87) + sea_shell = 0xFFF5EE, // rgb(255,245,238) + sienna = 0xA0522D, // rgb(160,82,45) + silver = 0xC0C0C0, // rgb(192,192,192) + sky_blue = 0x87CEEB, // rgb(135,206,235) + slate_blue = 0x6A5ACD, // rgb(106,90,205) + slate_gray = 0x708090, // rgb(112,128,144) + snow = 0xFFFAFA, // rgb(255,250,250) + spring_green = 0x00FF7F, // rgb(0,255,127) + steel_blue = 0x4682B4, // rgb(70,130,180) + tan = 0xD2B48C, // rgb(210,180,140) + teal = 0x008080, // rgb(0,128,128) + thistle = 0xD8BFD8, // rgb(216,191,216) + tomato = 0xFF6347, // rgb(255,99,71) + turquoise = 0x40E0D0, // rgb(64,224,208) + violet = 0xEE82EE, // rgb(238,130,238) + wheat = 0xF5DEB3, // rgb(245,222,179) + white = 0xFFFFFF, // rgb(255,255,255) + white_smoke = 0xF5F5F5, // rgb(245,245,245) + yellow = 0xFFFF00, // rgb(255,255,0) + yellow_green = 0x9ACD32, // rgb(154,205,50) +}; // enum class color + +// rgb is a struct for red, green and blue colors. +// We use rgb as name because some editors will show it as color direct in the +// editor. +struct rgb +{ + FMT_CONSTEXPR_DECL rgb() + : r(0) + , g(0) + , b(0) + { + } + FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_) + : r(r_) + , g(g_) + , b(b_) + { + } + FMT_CONSTEXPR_DECL rgb(uint32_t hex) + : r((hex >> 16) & 0xFF) + , g((hex >> 8) & 0xFF) + , b((hex)&0xFF) + { + } + FMT_CONSTEXPR_DECL rgb(color hex) + : r((uint32_t(hex) >> 16) & 0xFF) + , g((uint32_t(hex) >> 8) & 0xFF) + , b(uint32_t(hex) & 0xFF) + { + } + uint8_t r; + uint8_t g; + uint8_t b; +}; + +void vprint_rgb(rgb fd, string_view format, format_args args); +void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args); + +/** + Formats a string and prints it to stdout using ANSI escape sequences to + specify foreground color 'fd'. + Example: + fmt::print(fmt::color::red, "Elapsed time: {0:.2f} seconds", 1.23); + */ +template +inline void print(rgb fd, string_view format_str, const Args &... args) { - const Char *end = format_str; - if (*end == ':') - ++end; - while (*end && *end != '}') - ++end; - if (*end != '}') - FMT_THROW(FormatError("missing '}' in format string")); + vprint_rgb(fd, format_str, make_format_args(args...)); +} - It it = e.first; - if (it != e.last) - { - const Char *save = format_str; - f.format(format_str, internal::MakeArg>(*it++)); - while (it != e.last) - { - f.writer().write(e.sep); - format_str = save; - f.format(format_str, internal::MakeArg>(*it++)); - } - } - format_str = end + 1; +/** + Formats a string and prints it to stdout using ANSI escape sequences to + specify foreground color 'fd' and background color 'bg'. + Example: + fmt::print(fmt::color::red, fmt::color::black, "Elapsed time: {0:.2f} + seconds", 1.23); + */ +template +inline void print(rgb fd, rgb bg, string_view format_str, const Args &... args) +{ + vprint_rgb(fd, bg, format_str, make_format_args(args...)); } -} // namespace fmt +#endif // FMT_EXTENDED_COLORS #if FMT_USE_USER_DEFINED_LITERALS -namespace fmt { namespace internal { +#if FMT_UDL_TEMPLATE +template +class udl_formatter +{ +public: + template + std::basic_string operator()(const Args &... args) const + { + FMT_CONSTEXPR_DECL Char s[] = {CHARS..., '\0'}; + FMT_CONSTEXPR_DECL bool invalid_format = + check_format_string(basic_string_view(s, sizeof...(CHARS))); + (void)invalid_format; + return format(s, args...); + } +}; +#else template -struct UdlFormat +struct udl_formatter { const Char *str; @@ -4842,14 +4603,15 @@ struct UdlFormat return format(str, std::forward(args)...); } }; +#endif // FMT_UDL_TEMPLATE template -struct UdlArg +struct udl_arg { const Char *str; template - NamedArgWithType operator=(T &&value) const + named_arg operator=(T &&value) const { return {str, std::forward(value)}; } @@ -4859,9 +4621,16 @@ struct UdlArg inline namespace literals { +#if FMT_UDL_TEMPLATE +template +FMT_CONSTEXPR internal::udl_formatter operator""_format() +{ + return {}; +} +#else /** \rst - C++11 literal equivalent of :func:`fmt::format`. + User-defined literal equivalent of :func:`fmt::format`. **Example**:: @@ -4869,52 +4638,79 @@ inline namespace literals { std::string message = "The answer is {}"_format(42); \endrst */ -inline internal::UdlFormat operator"" _format(const char *s, std::size_t) +inline internal::udl_formatter operator"" _format(const char *s, std::size_t) { return {s}; } -inline internal::UdlFormat operator"" _format(const wchar_t *s, std::size_t) +inline internal::udl_formatter operator"" _format(const wchar_t *s, std::size_t) { return {s}; } +#endif // FMT_UDL_TEMPLATE /** \rst - C++11 literal equivalent of :func:`fmt::arg`. + User-defined literal equivalent of :func:`fmt::arg`. **Example**:: using namespace fmt::literals; - print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); + fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); \endrst */ -inline internal::UdlArg operator"" _a(const char *s, std::size_t) +inline internal::udl_arg operator"" _a(const char *s, std::size_t) { return {s}; } -inline internal::UdlArg operator"" _a(const wchar_t *s, std::size_t) +inline internal::udl_arg operator"" _a(const wchar_t *s, std::size_t) { return {s}; } - } // namespace literals -} // namespace fmt #endif // FMT_USE_USER_DEFINED_LITERALS +FMT_END_NAMESPACE -// Restore warnings. -#if FMT_GCC_VERSION >= 406 -#pragma GCC diagnostic pop -#endif +#define FMT_STRING(s) \ + [] { \ + struct S : fmt::format_string \ + { \ + static FMT_CONSTEXPR decltype(s) data() \ + { \ + return s; \ + } \ + static FMT_CONSTEXPR size_t size() \ + { \ + return sizeof(s); \ + } \ + }; \ + return S{}; \ + }() + +#ifndef FMT_NO_FMT_STRING_ALIAS +/** + \rst + Constructs a compile-time format string. -#if defined(__clang__) && !defined(FMT_ICC_VERSION) -#pragma clang diagnostic pop + **Example**:: + + #include + // A compile-time error because 'd' is an invalid specifier for strings. + std::string s = format(fmt("{:d}"), "foo"); + \endrst + */ +#define fmt(s) FMT_STRING(s) #endif #ifdef FMT_HEADER_ONLY #define FMT_FUNC inline -#include "format.cc" +#include "format-inl.h" #else #define FMT_FUNC #endif +// Restore warnings. +#if FMT_GCC_VERSION >= 406 || FMT_CLANG_VERSION +#pragma GCC diagnostic pop +#endif + #endif // FMT_FORMAT_H_ diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/locale.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/locale.h new file mode 100644 index 00000000..0fa78c85 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/locale.h @@ -0,0 +1,27 @@ +// Formatting library for C++ - locale support +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "format.h" +#include + +namespace fmt { +class locale +{ +private: + std::locale locale_; + +public: + explicit locale(std::locale loc = std::locale()) + : locale_(loc) + { + } + std::locale get() + { + return locale_; + } +}; +} // namespace fmt diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ostream.cc b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ostream.cc deleted file mode 100644 index 2d443f73..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ostream.cc +++ /dev/null @@ -1,35 +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. - */ - -#include "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/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ostream.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ostream.h index ffa75ab0..439ded6e 100644 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ostream.h +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ostream.h @@ -1,11 +1,9 @@ -/* - Formatting library for C++ - std::ostream support - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ +// 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_ @@ -13,21 +11,20 @@ #include "format.h" #include -namespace fmt { - +FMT_BEGIN_NAMESPACE namespace internal { template -class FormatBuf : public std::basic_streambuf +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_; + basic_buffer &buffer_; public: - FormatBuf(Buffer &buffer) + formatbuf(basic_buffer &buffer) : buffer_(buffer) { } @@ -54,64 +51,123 @@ class FormatBuf : public std::basic_streambuf } }; -Yes &convert(std::ostream &); +template +struct test_stream : std::basic_ostream +{ +private: + struct null; + // Hide all operator<< from std::basic_ostream. + void operator<<(null); +}; -struct DummyStream : std::ostream +// Checks if T has a user-defined operator<< (e.g. not a member of +// std::ostream). +template +class is_streamable { - DummyStream(); // Suppress a bogus warning in MSVC. +private: + template + static decltype(internal::declval &>() << internal::declval(), std::true_type()) test(int); - // Hide all operator<< overloads from std::ostream. - template - typename EnableIf::type operator<<(const T &); -}; + template + static std::false_type test(...); -No &operator<<(std::ostream &, int); + typedef decltype(test(0)) result; -template -struct ConvertToIntImpl -{ - // Convert to int only if T doesn't have an overloaded operator<<. - enum - { - value = sizeof(convert(get() << get())) == sizeof(No) - }; +public: + // std::string operator<< is not considered user-defined because we handle + // strings + // specially. + static const bool value = result::value && !std::is_same::value; }; -// Write the content of w to os. -FMT_API void write(std::ostream &os, Writer &w); -} // namespace internal +// Disable conversion to int if T has an overloaded operator<< which is a free +// function (not a member of std::ostream). +template +class convert_to_int +{ +public: + static const bool value = convert_to_int::value && !is_streamable::value; +}; -// Formats a value. -template -void format_arg(BasicFormatter &f, const Char *&format_str, const T &value) +// Write the content of buf to os. +template +void write(std::basic_ostream &os, basic_buffer &buf) { - internal::MemoryBuffer buffer; + const Char *data = buf.data(); + typedef std::make_unsigned::type UnsignedStreamSize; + UnsignedStreamSize size = buf.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); +} - internal::FormatBuf format_buf(buffer); +template +void format_value(basic_buffer &buffer, const T &value) +{ + internal::formatbuf format_buf(buffer); std::basic_ostream output(&format_buf); output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output << value; - - BasicStringRef str(&buffer[0], buffer.size()); - typedef internal::MakeArg> MakeArg; - format_str = f.format(format_str, MakeArg(str)); + buffer.resize(buffer.size()); } +// Disable builtin formatting of enums and use operator<< instead. +template +struct format_enum::value>::type> : std::false_type +{ +}; +} // namespace internal + +// Formats an object of type T that has an overloaded ostream operator<<. +template +struct formatter::value>::type> : formatter, Char> +{ + + template + auto format(const T &value, Context &ctx) -> decltype(ctx.out()) + { + basic_memory_buffer buffer; + internal::format_value(buffer, value); + basic_string_view str(buffer.data(), buffer.size()); + formatter, Char>::format(str, ctx); + return ctx.out(); + } +}; + +template +inline void vprint( + std::basic_ostream &os, basic_string_view format_str, basic_format_args::type> args) +{ + basic_memory_buffer buffer; + vformat_to(buffer, format_str, args); + internal::write(os, buffer); +} /** \rst Prints formatted data to the stream *os*. **Example**:: - print(cerr, "Don't {}!", "panic"); + fmt::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 +template +inline void print(std::ostream &os, string_view format_str, const Args &... args) +{ + vprint(os, format_str, make_format_args(args...)); +} -#ifdef FMT_HEADER_ONLY -#include "ostream.cc" -#endif +template +inline void print(std::wostream &os, wstring_view format_str, const Args &... args) +{ + vprint(os, format_str, make_format_args(args...)); +} +FMT_END_NAMESPACE #endif // FMT_OSTREAM_H_ diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/posix.cc b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/posix.cc deleted file mode 100644 index 356668c1..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/posix.cc +++ /dev/null @@ -1,241 +0,0 @@ -/* - A C++ interface to POSIX functions. - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ - -// Disable bogus MSVC warnings. -#ifndef _CRT_SECURE_NO_WARNINGS -# define _CRT_SECURE_NO_WARNINGS -#endif - -#include "posix.h" - -#include -#include -#include - -#ifndef _WIN32 -# include -#else -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# include -# include - -# define O_CREAT _O_CREAT -# define O_TRUNC _O_TRUNC - -# ifndef S_IRUSR -# define S_IRUSR _S_IREAD -# endif - -# ifndef S_IWUSR -# define S_IWUSR _S_IWRITE -# endif - -# ifdef __MINGW32__ -# define _SH_DENYNO 0x40 -# endif - -#endif // _WIN32 - -#ifdef fileno -# undef fileno -#endif - -namespace { -#ifdef _WIN32 -// Return type of read and write functions. -typedef int RWResult; - -// On Windows the count argument to read and write is unsigned, so convert -// it from size_t preventing integer overflow. -inline unsigned convert_rwcount(std::size_t count) { - return count <= UINT_MAX ? static_cast(count) : UINT_MAX; -} -#else -// Return type of read and write functions. -typedef ssize_t RWResult; - -inline std::size_t convert_rwcount(std::size_t count) { return count; } -#endif -} - -fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT { - if (file_ && FMT_SYSTEM(fclose(file_)) != 0) - fmt::report_system_error(errno, "cannot close file"); -} - -fmt::BufferedFile::BufferedFile( - fmt::CStringRef filename, fmt::CStringRef mode) { - FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0); - if (!file_) - FMT_THROW(SystemError(errno, "cannot open file {}", filename)); -} - -void fmt::BufferedFile::close() { - if (!file_) - return; - int result = FMT_SYSTEM(fclose(file_)); - file_ = FMT_NULL; - if (result != 0) - FMT_THROW(SystemError(errno, "cannot close file")); -} - -// A macro used to prevent expansion of fileno on broken versions of MinGW. -#define FMT_ARGS - -int fmt::BufferedFile::fileno() const { - int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); - if (fd == -1) - FMT_THROW(SystemError(errno, "cannot get file descriptor")); - return fd; -} - -fmt::File::File(fmt::CStringRef path, int oflag) { - int mode = S_IRUSR | S_IWUSR; -#if defined(_WIN32) && !defined(__MINGW32__) - fd_ = -1; - FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); -#else - FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); -#endif - if (fd_ == -1) - FMT_THROW(SystemError(errno, "cannot open file {}", path)); -} - -fmt::File::~File() FMT_NOEXCEPT { - // Don't retry close in case of EINTR! - // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html - if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) - fmt::report_system_error(errno, "cannot close file"); -} - -void fmt::File::close() { - if (fd_ == -1) - return; - // Don't retry close in case of EINTR! - // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html - int result = FMT_POSIX_CALL(close(fd_)); - fd_ = -1; - if (result != 0) - FMT_THROW(SystemError(errno, "cannot close file")); -} - -fmt::LongLong fmt::File::size() const { -#ifdef _WIN32 - // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT - // is less than 0x0500 as is the case with some default MinGW builds. - // Both functions support large file sizes. - DWORD size_upper = 0; - HANDLE handle = reinterpret_cast(_get_osfhandle(fd_)); - DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); - if (size_lower == INVALID_FILE_SIZE) { - DWORD error = GetLastError(); - if (error != NO_ERROR) - FMT_THROW(WindowsError(GetLastError(), "cannot get file size")); - } - fmt::ULongLong long_size = size_upper; - return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; -#else - typedef struct stat Stat; - Stat file_stat = Stat(); - if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) - FMT_THROW(SystemError(errno, "cannot get file attributes")); - FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size), - "return type of File::size is not large enough"); - return file_stat.st_size; -#endif -} - -std::size_t fmt::File::read(void *buffer, std::size_t count) { - RWResult result = 0; - FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); - if (result < 0) - FMT_THROW(SystemError(errno, "cannot read from file")); - return internal::to_unsigned(result); -} - -std::size_t fmt::File::write(const void *buffer, std::size_t count) { - RWResult result = 0; - FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); - if (result < 0) - FMT_THROW(SystemError(errno, "cannot write to file")); - return internal::to_unsigned(result); -} - -fmt::File fmt::File::dup(int fd) { - // Don't retry as dup doesn't return EINTR. - // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html - int new_fd = FMT_POSIX_CALL(dup(fd)); - if (new_fd == -1) - FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd)); - return File(new_fd); -} - -void fmt::File::dup2(int fd) { - int result = 0; - FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); - if (result == -1) { - FMT_THROW(SystemError(errno, - "cannot duplicate file descriptor {} to {}", fd_, fd)); - } -} - -void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT { - int result = 0; - FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); - if (result == -1) - ec = ErrorCode(errno); -} - -void fmt::File::pipe(File &read_end, File &write_end) { - // Close the descriptors first to make sure that assignments don't throw - // and there are no leaks. - read_end.close(); - write_end.close(); - int fds[2] = {}; -#ifdef _WIN32 - // Make the default pipe capacity same as on Linux 2.6.11+. - enum { DEFAULT_CAPACITY = 65536 }; - int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); -#else - // Don't retry as the pipe function doesn't return EINTR. - // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html - int result = FMT_POSIX_CALL(pipe(fds)); -#endif - if (result != 0) - FMT_THROW(SystemError(errno, "cannot create pipe")); - // The following assignments don't throw because read_fd and write_fd - // are closed. - read_end = File(fds[0]); - write_end = File(fds[1]); -} - -fmt::BufferedFile fmt::File::fdopen(const char *mode) { - // Don't retry as fdopen doesn't return EINTR. - FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); - if (!f) - FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor")); - BufferedFile file(f); - fd_ = -1; - return file; -} - -long fmt::getpagesize() { -#ifdef _WIN32 - SYSTEM_INFO si; - GetSystemInfo(&si); - return si.dwPageSize; -#else - long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); - if (size < 0) - FMT_THROW(SystemError(errno, "cannot get memory page size")); - return size; -#endif -} diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/posix.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/posix.h index 20af016c..20c75899 100644 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/posix.h +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/posix.h @@ -1,11 +1,9 @@ -/* - A C++ interface to POSIX functions. - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ +// A C++ interface to POSIX functions. +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. #ifndef FMT_POSIX_H_ #define FMT_POSIX_H_ @@ -65,16 +63,74 @@ #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) -namespace fmt { +FMT_BEGIN_NAMESPACE + +/** + \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 | + +===============+=============================+ + | cstring_view | basic_cstring_view | + +---------------+-----------------------------+ + | wcstring_view | basic_cstring_view | + +---------------+-----------------------------+ + + 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(cstring_view format_str, const Args & ... args); + + format("{}", 42); + format(std::string("{}"), 42); + \endrst + */ +template +class basic_cstring_view +{ +private: + const Char *data_; + +public: + /** Constructs a string reference object from a C string. */ + basic_cstring_view(const Char *s) + : data_(s) + { + } + + /** + \rst + Constructs a string reference from an ``std::string`` object. + \endrst + */ + basic_cstring_view(const std::basic_string &s) + : data_(s.c_str()) + { + } + + /** Returns the pointer to a C string. */ + const Char *c_str() const + { + return data_; + } +}; + +typedef basic_cstring_view cstring_view; +typedef basic_cstring_view wcstring_view; // An error code. -class ErrorCode +class error_code { private: int value_; public: - explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {} + explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} int get() const FMT_NOEXCEPT { @@ -83,24 +139,24 @@ class ErrorCode }; // A buffered file. -class BufferedFile +class buffered_file { private: FILE *file_; - friend class File; + friend class file; - explicit BufferedFile(FILE *f) + explicit buffered_file(FILE *f) : file_(f) { } public: - // Constructs a BufferedFile object which doesn't represent any file. - BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {} + // Constructs a buffered_file object which doesn't represent any file. + buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {} // Destroys the object closing the file it represents if any. - FMT_API ~BufferedFile() FMT_NOEXCEPT; + FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT; #if !FMT_USE_RVALUE_REFERENCES // Emulate a move constructor and a move assignment operator if rvalue @@ -116,16 +172,16 @@ class BufferedFile public: // A "move constructor" for moving from a temporary. - BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {} + buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {} // A "move constructor" for moving from an lvalue. - BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) + buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_) { f.file_ = FMT_NULL; } // A "move assignment operator" for moving from a temporary. - BufferedFile &operator=(Proxy p) + buffered_file &operator=(Proxy p) { close(); file_ = p.file; @@ -133,7 +189,7 @@ class BufferedFile } // A "move assignment operator" for moving from an lvalue. - BufferedFile &operator=(BufferedFile &other) + buffered_file &operator=(buffered_file &other) { close(); file_ = other.file_; @@ -142,7 +198,7 @@ class BufferedFile } // Returns a proxy object for moving from a temporary: - // BufferedFile file = BufferedFile(...); + // buffered_file file = buffered_file(...); operator Proxy() FMT_NOEXCEPT { Proxy p = {file_}; @@ -152,15 +208,15 @@ class BufferedFile #else private: - FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); + FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file); public: - BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) + buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) { other.file_ = FMT_NULL; } - BufferedFile &operator=(BufferedFile &&other) + buffered_file &operator=(buffered_file &&other) { close(); file_ = other.file_; @@ -170,7 +226,7 @@ class BufferedFile #endif // Opens a file. - FMT_API BufferedFile(CStringRef filename, CStringRef mode); + FMT_API buffered_file(cstring_view filename, cstring_view mode); // Closes the file. FMT_API void close(); @@ -185,26 +241,31 @@ class BufferedFile // of MinGW that define fileno as a macro. FMT_API int(fileno)() const; - void print(CStringRef format_str, const ArgList &args) + void vprint(string_view format_str, format_args args) + { + fmt::vprint(file_, format_str, args); + } + + template + inline void print(string_view format_str, const Args &... args) { - fmt::print(file_, format_str, args); + vprint(format_str, make_format_args(args...)); } - FMT_VARIADIC(void, print, CStringRef) }; -// A file. Closed file is represented by a File object with descriptor -1. +// A file. Closed file is represented by a file object with descriptor -1. // Methods that are not declared with FMT_NOEXCEPT may throw -// fmt::SystemError in case of failure. Note that some errors such as +// fmt::system_error in case of failure. Note that some errors such as // closing the file multiple times will cause a crash on Windows rather // than an exception. You can get standard behavior by overriding the // invalid parameter handler with _set_invalid_parameter_handler. -class File +class file { private: int fd_; // File descriptor. - // Constructs a File object with a given descriptor. - explicit File(int fd) + // Constructs a file object with a given descriptor. + explicit file(int fd) : fd_(fd) { } @@ -218,11 +279,11 @@ class File RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. }; - // Constructs a File object which doesn't represent any file. - File() FMT_NOEXCEPT : fd_(-1) {} + // Constructs a file object which doesn't represent any file. + file() FMT_NOEXCEPT : fd_(-1) {} - // Opens a file and constructs a File object representing this file. - FMT_API File(CStringRef path, int oflag); + // Opens a file and constructs a file object representing this file. + FMT_API file(cstring_view path, int oflag); #if !FMT_USE_RVALUE_REFERENCES // Emulate a move constructor and a move assignment operator if rvalue @@ -238,16 +299,16 @@ class File public: // A "move constructor" for moving from a temporary. - File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} + file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} // A "move constructor" for moving from an lvalue. - File(File &other) FMT_NOEXCEPT : fd_(other.fd_) + file(file &other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } // A "move assignment operator" for moving from a temporary. - File &operator=(Proxy p) + file &operator=(Proxy p) { close(); fd_ = p.fd; @@ -255,7 +316,7 @@ class File } // A "move assignment operator" for moving from an lvalue. - File &operator=(File &other) + file &operator=(file &other) { close(); fd_ = other.fd_; @@ -264,7 +325,7 @@ class File } // Returns a proxy object for moving from a temporary: - // File file = File(...); + // file f = file(...); operator Proxy() FMT_NOEXCEPT { Proxy p = {fd_}; @@ -274,15 +335,15 @@ class File #else private: - FMT_DISALLOW_COPY_AND_ASSIGN(File); + FMT_DISALLOW_COPY_AND_ASSIGN(file); public: - File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) + file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } - File &operator=(File &&other) + file &operator=(file &&other) { close(); fd_ = other.fd_; @@ -292,7 +353,7 @@ class File #endif // Destroys the object closing the file it represents if any. - FMT_API ~File() FMT_NOEXCEPT; + FMT_API ~file() FMT_DTOR_NOEXCEPT; // Returns the file descriptor. int descriptor() const FMT_NOEXCEPT @@ -305,7 +366,7 @@ class File // Returns the file size. The size has signed type for consistency with // stat::st_size. - FMT_API LongLong size() const; + FMT_API long long size() const; // Attempts to read count bytes from the file into the specified buffer. FMT_API std::size_t read(void *buffer, std::size_t count); @@ -315,7 +376,7 @@ class File // Duplicates a file descriptor with the dup function and returns // the duplicate as a file object. - FMT_API static File dup(int fd); + FMT_API static file dup(int fd); // Makes fd be the copy of this file descriptor, closing fd first if // necessary. @@ -323,21 +384,21 @@ class File // Makes fd be the copy of this file descriptor, closing fd first if // necessary. - FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT; + FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT; // Creates a pipe setting up read_end and write_end file objects for reading // and writing respectively. - FMT_API static void pipe(File &read_end, File &write_end); + FMT_API static void pipe(file &read_end, file &write_end); - // Creates a BufferedFile object associated with this file and detaches - // this File object from the file. - FMT_API BufferedFile fdopen(const char *mode); + // Creates a buffered_file object associated with this file and detaches + // this file object from the file. + FMT_API buffered_file fdopen(const char *mode); }; // Returns the memory page size. long getpagesize(); -#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && !defined(__ANDROID__) && !defined(__CYGWIN__) +#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && !defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) #define FMT_LOCALE #endif @@ -381,7 +442,7 @@ class Locale : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { if (!locale_) - FMT_THROW(fmt::SystemError(errno, "cannot create locale")); + FMT_THROW(system_error(errno, "cannot create locale")); } ~Locale() { @@ -404,16 +465,16 @@ class Locale } }; #endif // FMT_LOCALE -} // namespace fmt +FMT_END_NAMESPACE #if !FMT_USE_RVALUE_REFERENCES namespace std { // For compatibility with C++98. -inline fmt::BufferedFile &move(fmt::BufferedFile &f) +inline fmt::buffered_file &move(fmt::buffered_file &f) { return f; } -inline fmt::File &move(fmt::File &f) +inline fmt::file &move(fmt::file &f) { return f; } diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/printf.cc b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/printf.cc deleted file mode 100644 index 95d7a36a..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/printf.cc +++ /dev/null @@ -1,32 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ - -#include "format.h" -#include "printf.h" - -namespace fmt { - -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 void PrintfFormatter::format(CStringRef format); -template void PrintfFormatter::format(WCStringRef format); - -#endif // FMT_HEADER_ONLY - -} // namespace fmt diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/printf.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/printf.h index 679e8fc1..1782c2ea 100644 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/printf.h +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/printf.h @@ -1,11 +1,9 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ +// 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_ @@ -15,13 +13,13 @@ #include "ostream.h" -namespace fmt { +FMT_BEGIN_NAMESPACE namespace internal { // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. template -struct IntChecker +struct int_checker { template static bool fits_in_int(T value) @@ -36,7 +34,7 @@ struct IntChecker }; template<> -struct IntChecker +struct int_checker { template static bool fits_in_int(T value) @@ -49,212 +47,177 @@ struct IntChecker } }; -class PrecisionHandler : public ArgVisitor +class printf_precision_handler : public function { public: - void report_unhandled_arg() - { - FMT_THROW(FormatError("precision is not integer")); - } - template - int visit_any_int(T value) + typename std::enable_if::value, int>::type operator()(T value) { - if (!IntChecker::is_signed>::fits_in_int(value)) - FMT_THROW(FormatError("number is too big")); + if (!int_checker::is_signed>::fits_in_int(value)) + FMT_THROW(format_error("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) + typename std::enable_if::value, int>::type operator()(T) { - return value == 0; + FMT_THROW(format_error("precision is not integer")); + return 0; } }; -// returns the default type for format specific "%s" -class DefaultType : public ArgVisitor +// An argument visitor that returns true iff arg is a zero integer. +class is_zero_int : public function { public: - char visit_char(int) - { - return 'c'; - } - - char visit_bool(bool) - { - return 's'; - } - - char visit_pointer(const void *) - { - return 'p'; - } - template - char visit_any_int(T) + typename std::enable_if::value, bool>::type operator()(T value) { - return 'd'; + return value == 0; } template - char visit_any_double(T) + typename std::enable_if::value, bool>::type operator()(T) { - return 'g'; - } - - char visit_unhandled_arg() - { - return 's'; + return false; } }; -template -struct is_same +template +struct make_unsigned_or_bool : std::make_unsigned { - enum - { - value = 0 - }; }; -template -struct is_same +template<> +struct make_unsigned_or_bool { - enum - { - value = 1 - }; + typedef bool type; }; -// 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> +template +class arg_converter : public function { private: - internal::Arg &arg_; - wchar_t type_; + typedef typename Context::char_type Char; - FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); + basic_format_arg &arg_; + typename Context::char_type type_; public: - ArgConverter(internal::Arg &arg, wchar_t type) + arg_converter(basic_format_arg &arg, Char type) : arg_(arg) , type_(type) { } - void visit_bool(bool value) + void operator()(bool value) { if (type_ != 's') - visit_any_int(value); - } - - void visit_char(int value) - { - if (type_ != 's') - visit_any_int(value); + operator()(value); } template - void visit_any_int(U value) + typename std::enable_if::value>::type operator()(U value) { bool is_signed = type_ == 'd' || type_ == 'i'; - if (type_ == 's') - { - is_signed = std::numeric_limits::is_signed; - } - - using internal::Arg; - typedef typename internal::Conditional::value, U, T>::type TargetType; + typedef typename std::conditional::value, U, T>::type TargetType; if (const_check(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)); + arg_ = internal::make_arg(static_cast(static_cast(value))); } else { - arg_.type = Arg::UINT; - typedef typename internal::MakeUnsigned::Type Unsigned; - arg_.uint_value = static_cast(static_cast(value)); + typedef typename make_unsigned_or_bool::type Unsigned; + arg_ = internal::make_arg(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); + arg_ = internal::make_arg(static_cast(value)); } else { - arg_.type = Arg::ULONG_LONG; - arg_.ulong_long_value = static_cast::Type>(value); + arg_ = internal::make_arg(static_cast::type>(value)); } } } + + template + typename std::enable_if::value>::type operator()(U) + { + // No coversion needed for non-integral types. + } }; +// 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 +void convert_arg(basic_format_arg &arg, Char type) +{ + visit(arg_converter(arg, type), arg); +} + // Converts an integer argument to char for printf. -class CharConverter : public ArgVisitor +template +class char_converter : public function { private: - internal::Arg &arg_; + basic_format_arg &arg_; - FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); + FMT_DISALLOW_COPY_AND_ASSIGN(char_converter); public: - explicit CharConverter(internal::Arg &arg) + explicit char_converter(basic_format_arg &arg) : arg_(arg) { } template - void visit_any_int(T value) + typename std::enable_if::value>::type operator()(T value) + { + typedef typename Context::char_type Char; + arg_ = internal::make_arg(static_cast(value)); + } + + template + typename std::enable_if::value>::type operator()(T) { - arg_.type = internal::Arg::CHAR; - arg_.int_value = static_cast(value); + // No coversion needed for non-integral types. } }; // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. -class WidthHandler : public ArgVisitor +template +class printf_width_handler : public function { private: - FormatSpec &spec_; + typedef basic_format_specs format_specs; - FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); + format_specs &spec_; + + FMT_DISALLOW_COPY_AND_ASSIGN(printf_width_handler); public: - explicit WidthHandler(FormatSpec &spec) + explicit printf_width_handler(format_specs &spec) : spec_(spec) { } - void report_unhandled_arg() - { - FMT_THROW(FormatError("width is not integer")); - } - template - unsigned visit_any_int(T value) + typename std::enable_if::value, unsigned>::type operator()(T value) { - typedef typename internal::IntTraits::MainType UnsignedType; + typedef typename internal::int_traits::main_type UnsignedType; UnsignedType width = static_cast(value); if (internal::is_negative(value)) { @@ -263,176 +226,230 @@ class WidthHandler : public ArgVisitor } unsigned int_max = std::numeric_limits::max(); if (width > int_max) - FMT_THROW(FormatError("number is too big")); + FMT_THROW(format_error("number is too big")); return static_cast(width); } + + template + typename std::enable_if::value, unsigned>::type operator()(T) + { + FMT_THROW(format_error("width is not integer")); + return 0; + } }; } // namespace internal +template +class printf_arg_formatter; + +template>>> +class basic_printf_context; + /** \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. + The ``printf`` argument formatter. \endrst */ -template -class BasicPrintfArgFormatter : public internal::ArgFormatterBase +template +class printf_arg_formatter : public internal::function::iterator>, + public internal::arg_formatter_base { private: - void write_null_pointer() + typedef typename Range::value_type char_type; + typedef decltype(internal::declval().begin()) iterator; + typedef internal::arg_formatter_base base; + typedef basic_printf_context context_type; + + context_type &context_; + + void write_null_pointer(char) { this->spec().type_ = 0; this->write("(nil)"); } - typedef internal::ArgFormatterBase Base; + void write_null_pointer(wchar_t) + { + this->spec().type_ = 0; + this->write(L"(nil)"); + } public: + typedef typename base::format_specs format_specs; + /** \rst Constructs an argument formatter object. - *writer* is a reference to the output writer and *spec* contains format + *buffer* is a reference to the output buffer and *spec* contains format specifier information for standard argument types. \endrst */ - BasicPrintfArgFormatter(BasicWriter &w, Spec &s) - : internal::ArgFormatterBase(w, s) + printf_arg_formatter(internal::basic_buffer &buffer, format_specs &spec, context_type &ctx) + : base(back_insert_range>(buffer), spec) + , context_(ctx) { } - /** Formats an argument of type ``bool``. */ - void visit_bool(bool value) - { - Spec &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) + template + typename std::enable_if::value, iterator>::type operator()(T value) { - const Spec &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) + // MSVC2013 fails to compile separate overloads for bool and char_type so + // use std::is_same instead. + if (std::is_same::value) { - 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); - } + format_specs &fmt_spec = this->spec(); + if (fmt_spec.type_ != 's') + return base::operator()(value ? 1 : 0); + fmt_spec.type_ = 0; + this->write(value != 0); + } + else if (std::is_same::value) + { + format_specs &fmt_spec = this->spec(); + if (fmt_spec.type_ && fmt_spec.type_ != 'c') + return (*this)(static_cast(value)); + fmt_spec.flags_ = 0; + fmt_spec.align_ = ALIGN_RIGHT; + return base::operator()(value); } else { - out = w.grow_buffer(1); + return base::operator()(value); } - *out = static_cast(value); + return this->out(); + } + + template + typename std::enable_if::value, iterator>::type operator()(T value) + { + return base::operator()(value); } /** Formats a null-terminated C string. */ - void visit_cstring(const char *value) + iterator operator()(const char *value) { if (value) - Base::visit_cstring(value); + base::operator()(value); else if (this->spec().type_ == 'p') - write_null_pointer(); + write_null_pointer(char_type()); else this->write("(null)"); + return this->out(); + } + + /** Formats a null-terminated wide C string. */ + iterator operator()(const wchar_t *value) + { + if (value) + base::operator()(value); + else if (this->spec().type_ == 'p') + write_null_pointer(char_type()); + else + this->write(L"(null)"); + return this->out(); + } + + iterator operator()(basic_string_view value) + { + return base::operator()(value); + } + + iterator operator()(monostate value) + { + return base::operator()(value); } /** Formats a pointer. */ - void visit_pointer(const void *value) + iterator operator()(const void *value) { if (value) - return Base::visit_pointer(value); + return base::operator()(value); this->spec().type_ = 0; - write_null_pointer(); + write_null_pointer(char_type()); + return this->out(); } /** Formats an argument of a custom (user-defined) type. */ - void visit_custom(internal::Arg::CustomValue c) + iterator operator()(typename basic_format_arg::handle handle) { - BasicFormatter formatter(ArgList(), this->writer()); - const Char format_str[] = {'}', 0}; - const Char *format = format_str; - c.format(&formatter, c.value, &format); + handle.format(context_); + return this->out(); } }; -/** The default printf argument formatter. */ -template -class PrintfArgFormatter : public BasicPrintfArgFormatter, Char, FormatSpec> +template +struct printf_formatter { -public: - /** Constructs an argument formatter object. */ - PrintfArgFormatter(BasicWriter &w, FormatSpec &s) - : BasicPrintfArgFormatter, Char, FormatSpec>(w, s) + template + auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const T &value, FormatContext &ctx) -> decltype(ctx.out()) + { + internal::format_value(internal::get_container(ctx.out()), value); + return ctx.out(); } }; /** This template formats data and writes the output to a writer. */ -template> -class PrintfFormatter : private internal::FormatterBase +template +class basic_printf_context : private internal::context_base, Char> { +public: + /** The character type for the output. */ + typedef Char char_type; + + template + struct formatter_type + { + typedef printf_formatter type; + }; + private: - BasicWriter &writer_; + typedef internal::context_base base; + typedef typename base::format_arg format_arg; + typedef basic_format_specs format_specs; + typedef internal::null_terminating_iterator iterator; - void parse_flags(FormatSpec &spec, const Char *&s); + void parse_flags(format_specs &spec, iterator &it); // 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)()); + format_arg get_arg(iterator it, 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); + unsigned parse_header(iterator &it, format_specs &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 + Constructs a ``printf_context`` object. References to the arguments and + the writer are stored in the context object so make sure they have appropriate lifetimes. \endrst */ - explicit PrintfFormatter(const ArgList &al, BasicWriter &w) - : FormatterBase(al) - , writer_(w) + basic_printf_context(OutputIt out, basic_string_view format_str, basic_format_args args) + : base(out, format_str, args) { } - /** Formats stored arguments and writes the output to the writer. */ - void format(BasicCStringRef format_str); + using base::advance_to; + using base::out; + using base::parse_context; + + /** Formats stored arguments and writes the output to the range. */ + void format(); }; -template -void PrintfFormatter::parse_flags(FormatSpec &spec, const Char *&s) +template +void basic_printf_context::parse_flags(format_specs &spec, iterator &it) { for (;;) { - switch (*s++) + switch (*it++) { case '-': spec.align_ = ALIGN_LEFT; @@ -450,36 +467,36 @@ void PrintfFormatter::parse_flags(FormatSpec &spec, const Char *&s) spec.flags_ |= HASH_FLAG; break; default: - --s; + --it; return; } } } -template -internal::Arg PrintfFormatter::get_arg(const Char *s, unsigned arg_index) +template +typename basic_printf_context::format_arg basic_printf_context::get_arg( + iterator it, unsigned arg_index) { - (void)s; - const char *error = FMT_NULL; - 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; + (void)it; + if (arg_index == std::numeric_limits::max()) + return this->do_get_arg(this->parse_context().next_arg_id()); + return base::get_arg(arg_index - 1); } -template -unsigned PrintfFormatter::parse_header(const Char *&s, FormatSpec &spec) +template +unsigned basic_printf_context::parse_header(iterator &it, format_specs &spec) { unsigned arg_index = std::numeric_limits::max(); - Char c = *s; + char_type c = *it; 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; + internal::error_handler eh; + unsigned value = parse_nonnegative_int(it, eh); + if (*it == '$') + { // value is an argument index + ++it; arg_index = value; } else @@ -495,56 +512,60 @@ unsigned PrintfFormatter::parse_header(const Char *&s, FormatSpec &spe } } } - parse_flags(spec, s); + parse_flags(spec, it); // Parse width. - if (*s >= '0' && *s <= '9') + if (*it >= '0' && *it <= '9') { - spec.width_ = internal::parse_nonnegative_int(s); + internal::error_handler eh; + spec.width_ = parse_nonnegative_int(it, eh); } - else if (*s == '*') + else if (*it == '*') { - ++s; - spec.width_ = internal::WidthHandler(spec).visit(get_arg(s)); + ++it; + spec.width_ = visit(internal::printf_width_handler(spec), get_arg(it)); } return arg_index; } -template -void PrintfFormatter::format(BasicCStringRef format_str) +template +void basic_printf_context::format() { - const Char *start = format_str.c_str(); - const Char *s = start; - while (*s) + auto &buffer = internal::get_container(this->out()); + auto start = iterator(this->parse_context()); + auto it = start; + using internal::pointer_from; + while (*it) { - Char c = *s++; + char_type c = *it++; if (c != '%') continue; - if (*s == c) + if (*it == c) { - write(writer_, start, s); - start = ++s; + buffer.append(pointer_from(start), pointer_from(it)); + start = ++it; continue; } - write(writer_, start, s - 1); + buffer.append(pointer_from(start), pointer_from(it) - 1); - FormatSpec spec; + format_specs spec; spec.align_ = ALIGN_RIGHT; // Parse argument index, flags and width. - unsigned arg_index = parse_header(s, spec); + unsigned arg_index = parse_header(it, spec); // Parse precision. - if (*s == '.') + if (*it == '.') { - ++s; - if ('0' <= *s && *s <= '9') + ++it; + if ('0' <= *it && *it <= '9') { - spec.precision_ = static_cast(internal::parse_nonnegative_int(s)); + internal::error_handler eh; + spec.precision_ = static_cast(parse_nonnegative_int(it, eh)); } - else if (*s == '*') + else if (*it == '*') { - ++s; - spec.precision_ = internal::PrecisionHandler().visit(get_arg(s)); + ++it; + spec.precision_ = visit(internal::printf_precision_handler(), get_arg(it)); } else { @@ -552,64 +573,56 @@ void PrintfFormatter::format(BasicCStringRef format_str) } } - using internal::Arg; - Arg arg = get_arg(s, arg_index); - if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg)) + format_arg arg = get_arg(it, arg_index); + if (spec.flag(HASH_FLAG) && visit(internal::is_zero_int(), arg)) spec.flags_ &= ~internal::to_unsigned(HASH_FLAG); if (spec.fill_ == '0') { - if (arg.type <= Arg::LAST_NUMERIC_TYPE) + if (arg.is_arithmetic()) 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++) + using internal::convert_arg; + switch (*it++) { case 'h': - if (*s == 'h') - ArgConverter(arg, *++s).visit(arg); + if (*it == 'h') + convert_arg(arg, *++it); else - ArgConverter(arg, *s).visit(arg); + convert_arg(arg, *it); break; case 'l': - if (*s == 'l') - ArgConverter(arg, *++s).visit(arg); + if (*it == 'l') + convert_arg(arg, *++it); else - ArgConverter(arg, *s).visit(arg); + convert_arg(arg, *it); break; case 'j': - ArgConverter(arg, *s).visit(arg); + convert_arg(arg, *it); break; case 'z': - ArgConverter(arg, *s).visit(arg); + convert_arg(arg, *it); break; case 't': - ArgConverter(arg, *s).visit(arg); + convert_arg(arg, *it); 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); + --it; + convert_arg(arg, *it); } // Parse type. - if (!*s) - FMT_THROW(FormatError("invalid format string")); - spec.type_ = static_cast(*s++); - - if (spec.type_ == 's') - { - // set the format type to the default if 's' is specified - spec.type_ = internal::DefaultType().visit(arg); - } - - if (arg.type <= Arg::LAST_INTEGER_TYPE) + if (!*it) + FMT_THROW(format_error("invalid format string")); + spec.type_ = static_cast(*it++); + if (arg.is_integral()) { // Normalize type. switch (spec.type_) @@ -619,31 +632,46 @@ void PrintfFormatter::format(BasicCStringRef format_str) spec.type_ = 'd'; break; case 'c': - // TODO: handle wchar_t - internal::CharConverter(arg).visit(arg); + // TODO: handle wchar_t better? + visit(internal::char_converter(arg), arg); break; } } - start = s; + start = it; // Format argument. - AF(writer_, spec).visit(arg); + visit(AF(buffer, spec, *this), arg); } - write(writer_, start, s); + buffer.append(pointer_from(start), pointer_from(it)); } -inline void printf(Writer &w, CStringRef format, ArgList args) +template +void printf(internal::basic_buffer &buf, basic_string_view format, basic_format_args args) { - PrintfFormatter(args, w).format(format); + Context(std::back_inserter(buf), format, args).format(); } -FMT_VARIADIC(void, printf, Writer &, CStringRef) -inline void printf(WWriter &w, WCStringRef format, ArgList args) +template +struct printf_context { - PrintfFormatter(args, w).format(format); + typedef basic_printf_context, typename Buffer::value_type> type; +}; + +template +inline format_arg_store::type, Args...> make_printf_args(const Args &... args) +{ + return format_arg_store::type, Args...>(args...); +} +typedef basic_format_args::type> printf_args; +typedef basic_format_args::type> wprintf_args; + +inline std::string vsprintf(string_view format, printf_args args) +{ + memory_buffer buffer; + printf(buffer, format, args); + return to_string(buffer); } -FMT_VARIADIC(void, printf, WWriter &, WCStringRef) /** \rst @@ -654,21 +682,34 @@ FMT_VARIADIC(void, printf, WWriter &, WCStringRef) std::string message = fmt::sprintf("The answer is %d", 42); \endrst */ -inline std::string sprintf(CStringRef format, ArgList args) +template +inline std::string sprintf(string_view format_str, const Args &... args) +{ + return vsprintf(format_str, make_format_args::type>(args...)); +} + +inline std::wstring vsprintf(wstring_view format, wprintf_args args) { - MemoryWriter w; - printf(w, format, args); - return w.str(); + wmemory_buffer buffer; + printf(buffer, format, args); + return to_string(buffer); } -FMT_VARIADIC(std::string, sprintf, CStringRef) -inline std::wstring sprintf(WCStringRef format, ArgList args) +template +inline std::wstring sprintf(wstring_view format_str, const Args &... args) +{ + return vsprintf(format_str, make_format_args::type>(args...)); +} + +template +inline int vfprintf( + std::FILE *f, basic_string_view format, basic_format_args>::type> args) { - WMemoryWriter w; - printf(w, format, args); - return w.str(); + basic_memory_buffer buffer; + printf(buffer, format, args); + std::size_t size = buffer.size(); + return std::fwrite(buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast(size); } -FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) /** \rst @@ -679,8 +720,28 @@ FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) 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) +template +inline int fprintf(std::FILE *f, string_view format_str, const Args &... args) +{ + auto vargs = make_format_args::type>(args...); + return vfprintf(f, format_str, vargs); +} + +template +inline int fprintf(std::FILE *f, wstring_view format_str, const Args &... args) +{ + return vfprintf(f, format_str, make_format_args::type>(args...)); +} + +inline int vprintf(string_view format, printf_args args) +{ + return vfprintf(stdout, format, args); +} + +inline int vprintf(wstring_view format, wprintf_args args) +{ + return vfprintf(stdout, format, args); +} /** \rst @@ -691,11 +752,33 @@ FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) fmt::printf("Elapsed time: %.2f seconds", 1.23); \endrst */ -inline int printf(CStringRef format, ArgList args) +template +inline int printf(string_view format_str, const Args &... args) +{ + return vprintf(format_str, make_format_args::type>(args...)); +} + +template +inline int printf(wstring_view format_str, const Args &... args) { - return fprintf(stdout, format, args); + return vprintf(format_str, make_format_args::type>(args...)); +} + +inline int vfprintf(std::ostream &os, string_view format_str, printf_args args) +{ + memory_buffer buffer; + printf(buffer, format_str, args); + internal::write(os, buffer); + return static_cast(buffer.size()); +} + +inline int vfprintf(std::wostream &os, wstring_view format_str, wprintf_args args) +{ + wmemory_buffer buffer; + printf(buffer, format_str, args); + internal::write(os, buffer); + return static_cast(buffer.size()); } -FMT_VARIADIC(int, printf, CStringRef) /** \rst @@ -703,21 +786,22 @@ FMT_VARIADIC(int, printf, CStringRef) **Example**:: - fprintf(cerr, "Don't %s!", "panic"); + fmt::fprintf(cerr, "Don't %s!", "panic"); \endrst */ -inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) +template +inline int fprintf(std::ostream &os, string_view format_str, const Args &... args) { - MemoryWriter w; - printf(w, format_str, args); - internal::write(os, w); - return static_cast(w.size()); + auto vargs = make_format_args::type>(args...); + return vfprintf(os, format_str, vargs); } -FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) -} // namespace fmt -#ifdef FMT_HEADER_ONLY -#include "printf.cc" -#endif +template +inline int fprintf(std::wostream &os, wstring_view format_str, const Args &... args) +{ + auto vargs = make_format_args::type>(args...); + return vfprintf(os, format_str, vargs); +} +FMT_END_NAMESPACE #endif // FMT_PRINTF_H_ diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ranges.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ranges.h new file mode 100644 index 00000000..ba2a979c --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/ranges.h @@ -0,0 +1,344 @@ +// Formatting library for C++ - the core API +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. +// +// Copyright (c) 2018 - present, Remotion (Igor Schulz) +// All Rights Reserved +// {fmt} support for ranges, containers and types tuple interface. + +#ifndef FMT_RANGES_H_ +#define FMT_RANGES_H_ + +#include "format.h" +#include + +// output only up to N items from the range. +#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT +#define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 +#endif + +FMT_BEGIN_NAMESPACE + +template +struct formatting_base +{ + template + FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } +}; + +template +struct formatting_range : formatting_base +{ + static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the + // range. + Char prefix; + Char delimiter; + Char postfix; + formatting_range() + : prefix('{') + , delimiter(',') + , postfix('}') + { + } + static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; + static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; +}; + +template +struct formatting_tuple : formatting_base +{ + Char prefix; + Char delimiter; + Char postfix; + formatting_tuple() + : prefix('(') + , delimiter(',') + , postfix(')') + { + } + static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; + static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; +}; + +namespace internal { + +template +void copy(const RangeT &range, OutputIterator out) +{ + for (auto it = range.begin(), end = range.end(); it != end; ++it) + *out++ = *it; +} + +template +void copy(const char *str, OutputIterator out) +{ + const char *p_curr = str; + while (*p_curr) + { + *out++ = *p_curr++; + } +} + +template +void copy(char ch, OutputIterator out) +{ + *out++ = ch; +} + +/// Return true value if T has std::string interface, like std::string_view. +template +class is_like_std_string +{ + template + static auto check(U *p) -> decltype(p->find('a'), p->length(), p->data(), int()); + template + static void check(...); + +public: + static FMT_CONSTEXPR_DECL const bool value = !std::is_void(FMT_NULL))>::value; +}; + +template +struct conditional_helper +{ +}; + +template +struct is_range_ : std::false_type +{ +}; + +#if !FMT_MSC_VER || FMT_MSC_VER > 1800 +template +struct is_range_().begin()), decltype(internal::declval().end())>, void>::type> + : std::true_type +{ +}; +#endif + +/// tuple_size and tuple_element check. +template +class is_tuple_like_ +{ + template + static auto check(U *p) -> decltype(std::tuple_size::value, internal::declval::type>(), int()); + template + static void check(...); + +public: + static FMT_CONSTEXPR_DECL const bool value = !std::is_void(FMT_NULL))>::value; +}; + +// Check for integer_sequence +#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 +template +using integer_sequence = std::integer_sequence; +template +using index_sequence = std::index_sequence; +template +using make_index_sequence = std::make_index_sequence; +#else +template +struct integer_sequence +{ + typedef T value_type; + + static FMT_CONSTEXPR std::size_t size() + { + return sizeof...(N); + } +}; + +template +using index_sequence = integer_sequence; + +template +struct make_integer_sequence : make_integer_sequence +{ +}; +template +struct make_integer_sequence : integer_sequence +{ +}; + +template +using make_index_sequence = make_integer_sequence; +#endif + +template +void for_each(index_sequence, Tuple &&tup, F &&f) FMT_NOEXCEPT +{ + using std::get; + // using free function get(T) now. + const int _[] = {0, ((void)f(get(tup)), 0)...}; + (void)_; // blocks warnings +} + +template +FMT_CONSTEXPR make_index_sequence::value> get_indexes(T const &) +{ + return {}; +} + +template +void for_each(Tuple &&tup, F &&f) +{ + const auto indexes = get_indexes(tup); + for_each(indexes, std::forward(tup), std::forward(f)); +} + +template +FMT_CONSTEXPR const char *format_str_quoted( + bool add_space, const Arg &, typename std::enable_if::type>::value>::type * = nullptr) +{ + return add_space ? " {}" : "{}"; +} + +template +FMT_CONSTEXPR const char *format_str_quoted( + bool add_space, const Arg &, typename std::enable_if::type>::value>::type * = nullptr) +{ + return add_space ? " \"{}\"" : "\"{}\""; +} + +FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char *) +{ + return add_space ? " \"{}\"" : "\"{}\""; +} +FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t *) +{ + return add_space ? L" \"{}\"" : L"\"{}\""; +} + +FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char) +{ + return add_space ? " '{}'" : "'{}'"; +} +FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t) +{ + return add_space ? L" '{}'" : L"'{}'"; +} + +} // namespace internal + +template +struct is_tuple_like +{ + static FMT_CONSTEXPR_DECL const bool value = internal::is_tuple_like_::value && !internal::is_range_::value; +}; + +template +struct formatter::value>::type> +{ +private: + // C++11 generic lambda for format() + template + struct format_each + { + template + void operator()(const T &v) + { + if (i > 0) + { + if (formatting.add_prepostfix_space) + { + *out++ = ' '; + } + internal::copy(formatting.delimiter, out); + } + format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), v), v); + ++i; + } + + formatting_tuple &formatting; + std::size_t &i; + typename std::add_lvalue_reference().out())>::type out; + }; + +public: + formatting_tuple formatting; + + template + FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) + { + return formatting.parse(ctx); + } + + template + auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) + { + auto out = ctx.out(); + std::size_t i = 0; + internal::copy(formatting.prefix, out); + + internal::for_each(values, format_each{formatting, i, out}); + if (formatting.add_prepostfix_space) + { + *out++ = ' '; + } + internal::copy(formatting.postfix, out); + + return ctx.out(); + } +}; + +template +struct is_range +{ + static FMT_CONSTEXPR_DECL const bool value = internal::is_range_::value && !internal::is_like_std_string::value; +}; + +template +struct formatter::value>::type> +{ + + formatting_range formatting; + + template + FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) + { + return formatting.parse(ctx); + } + + template + typename FormatContext::iterator format(const RangeT &values, FormatContext &ctx) + { + auto out = ctx.out(); + internal::copy(formatting.prefix, out); + std::size_t i = 0; + for (auto it = values.begin(), end = values.end(); it != end; ++it) + { + if (i > 0) + { + if (formatting.add_prepostfix_space) + { + *out++ = ' '; + } + internal::copy(formatting.delimiter, out); + } + format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), *it), *it); + if (++i > formatting.range_length_limit) + { + format_to(out, " ... "); + break; + } + } + if (formatting.add_prepostfix_space) + { + *out++ = ' '; + } + internal::copy(formatting.postfix, out); + return ctx.out(); + } +}; + +FMT_END_NAMESPACE + +#endif // FMT_RANGES_H_ diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/time.h b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/time.h index 1d4210c8..3e1443b8 100644 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/time.h +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/bundled/time.h @@ -1,11 +1,9 @@ -/* - Formatting library for C++ - time formatting - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ +// Formatting library for C++ - time formatting +// +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. #ifndef FMT_TIME_H_ #define FMT_TIME_H_ @@ -13,79 +11,36 @@ #include "format.h" #include -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4702) // unreachable code -#pragma warning(disable : 4996) // "deprecated" functions -#endif - -namespace fmt { -template -void format_arg(BasicFormatter &f, const char *&format_str, const std::tm &tm) -{ - if (*format_str == ':') - ++format_str; - const char *end = format_str; - while (*end && *end != '}') - ++end; - if (*end != '}') - FMT_THROW(FormatError("missing '}' in format string")); - internal::MemoryBuffer format; - format.append(format_str, end + 1); - format[format.size() - 1] = '\0'; - Buffer &buffer = f.writer().buffer(); - std::size_t start = buffer.size(); - for (;;) - { - std::size_t size = buffer.capacity() - start; - std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); - if (count != 0) - { - buffer.resize(start + count); - break; - } - if (size >= format.size() * 256) - { - // If the buffer is 256 times larger than the format string, assume - // that `strftime` gives an empty result. There doesn't seem to be a - // better way to distinguish the two cases: - // https://github.com/fmtlib/fmt/issues/367 - break; - } - const std::size_t MIN_GROWTH = 10; - buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); - } - format_str = end + 1; -} +FMT_BEGIN_NAMESPACE namespace internal { -inline Null<> localtime_r(...) +inline null<> localtime_r(...) { - return Null<>(); + return null<>(); } -inline Null<> localtime_s(...) +inline null<> localtime_s(...) { - return Null<>(); + return null<>(); } -inline Null<> gmtime_r(...) +inline null<> gmtime_r(...) { - return Null<>(); + return null<>(); } -inline Null<> gmtime_s(...) +inline null<> gmtime_s(...) { - return Null<>(); + return null<>(); } } // namespace internal // Thread-safe replacement for std::localtime inline std::tm localtime(std::time_t time) { - struct LocalTime + struct dispatcher { std::time_t time_; std::tm tm_; - LocalTime(std::time_t t) + dispatcher(std::time_t t) : time_(t) { } @@ -101,7 +56,7 @@ inline std::tm localtime(std::time_t time) return tm != FMT_NULL; } - bool handle(internal::Null<>) + bool handle(internal::null<>) { using namespace fmt::internal; return fallback(localtime_s(&tm_, &time_)); @@ -112,7 +67,7 @@ inline std::tm localtime(std::time_t time) return res == 0; } - bool fallback(internal::Null<>) + bool fallback(internal::null<>) { using namespace fmt::internal; std::tm *tm = std::localtime(&time_); @@ -121,23 +76,22 @@ inline std::tm localtime(std::time_t time) return tm != FMT_NULL; } }; - LocalTime lt(time); + dispatcher lt(time); if (lt.run()) return lt.tm_; // Too big time values may be unsupported. - FMT_THROW(fmt::FormatError("time_t value out of range")); - return std::tm(); + FMT_THROW(format_error("time_t value out of range")); } // Thread-safe replacement for std::gmtime inline std::tm gmtime(std::time_t time) { - struct GMTime + struct dispatcher { std::time_t time_; std::tm tm_; - GMTime(std::time_t t) + dispatcher(std::time_t t) : time_(t) { } @@ -153,7 +107,7 @@ inline std::tm gmtime(std::time_t time) return tm != FMT_NULL; } - bool handle(internal::Null<>) + bool handle(internal::null<>) { using namespace fmt::internal; return fallback(gmtime_s(&tm_, &time_)); @@ -164,25 +118,82 @@ inline std::tm gmtime(std::time_t time) return res == 0; } - bool fallback(internal::Null<>) + bool fallback(internal::null<>) { std::tm *tm = std::gmtime(&time_); - if (tm != FMT_NULL) + if (tm) tm_ = *tm; return tm != FMT_NULL; } }; - GMTime gt(time); + dispatcher gt(time); if (gt.run()) return gt.tm_; // Too big time values may be unsupported. - FMT_THROW(fmt::FormatError("time_t value out of range")); - return std::tm(); + FMT_THROW(format_error("time_t value out of range")); } -} // namespace fmt -#ifdef _MSC_VER -#pragma warning(pop) -#endif +namespace internal { +inline std::size_t strftime(char *str, std::size_t count, const char *format, const std::tm *time) +{ + return std::strftime(str, count, format, time); +} + +inline std::size_t strftime(wchar_t *str, std::size_t count, const wchar_t *format, const std::tm *time) +{ + return std::wcsftime(str, count, format, time); +} +} // namespace internal + +template +struct formatter +{ + template + auto parse(ParseContext &ctx) -> decltype(ctx.begin()) + { + auto it = internal::null_terminating_iterator(ctx); + if (*it == ':') + ++it; + auto end = it; + while (*end && *end != '}') + ++end; + tm_format.reserve(end - it + 1); + using internal::pointer_from; + tm_format.append(pointer_from(it), pointer_from(end)); + tm_format.push_back('\0'); + return pointer_from(end); + } + + template + auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) + { + internal::basic_buffer &buf = internal::get_container(ctx.out()); + std::size_t start = buf.size(); + for (;;) + { + std::size_t size = buf.capacity() - start; + std::size_t count = internal::strftime(&buf[start], size, &tm_format[0], &tm); + if (count != 0) + { + buf.resize(start + count); + break; + } + if (size >= tm_format.size() * 256) + { + // If the buffer is 256 times larger than the format string, assume + // that `strftime` gives an empty result. There doesn't seem to be a + // better way to distinguish the two cases: + // https://github.com/fmtlib/fmt/issues/367 + break; + } + const std::size_t MIN_GROWTH = 10; + buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); + } + return ctx.out(); + } + + basic_memory_buffer tm_format; +}; +FMT_END_NAMESPACE #endif // FMT_TIME_H_ diff --git a/third_party/spdlog/spdlog/include/spdlog/fmt/fmt.h b/third_party/spdlog/spdlog/include/spdlog/fmt/fmt.h index e2e04854..616af0cc 100644 --- a/third_party/spdlog/spdlog/include/spdlog/fmt/fmt.h +++ b/third_party/spdlog/spdlog/include/spdlog/fmt/fmt.h @@ -1,5 +1,5 @@ // -// Copyright(c) 2016 Gabi Melman. +// Copyright(c) 2016-2018 Gabi Melman. // Distributed under the MIT License (http://opensource.org/licenses/MIT) // @@ -11,23 +11,15 @@ // #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 "bundled/core.h" #include "bundled/format.h" -#if defined(SPDLOG_FMT_PRINTF) -#include "bundled/printf.h" -#endif - #else // external fmtlib - +#include #include -#if defined(SPDLOG_FMT_PRINTF) -#include -#endif - #endif diff --git a/third_party/spdlog/spdlog/include/spdlog/formatter.h b/third_party/spdlog/spdlog/include/spdlog/formatter.h index 55388ec5..a7ef6b8b 100644 --- a/third_party/spdlog/spdlog/include/spdlog/formatter.h +++ b/third_party/spdlog/spdlog/include/spdlog/formatter.h @@ -5,42 +5,16 @@ #pragma once -#include "details/log_msg.h" - -#include -#include -#include +#include "fmt/fmt.h" +#include "spdlog/details/log_msg.h" namespace spdlog { -namespace details { -class flag_formatter; -} class formatter { public: virtual ~formatter() = default; - virtual void format(details::log_msg &msg) = 0; -}; - -class pattern_formatter SPDLOG_FINAL : public formatter -{ -public: - explicit pattern_formatter(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local, - std::string eol = spdlog::details::os::default_eol); - pattern_formatter(const pattern_formatter &) = delete; - pattern_formatter &operator=(const pattern_formatter &) = delete; - void format(details::log_msg &msg) override; - -private: - const std::string _eol; - const std::string _pattern; - const pattern_time_type _pattern_time; - std::vector> _formatters; - std::tm get_time(details::log_msg &msg); - void handle_flag(char flag); - void compile_pattern(const std::string &pattern); + virtual void format(const details::log_msg &msg, fmt::memory_buffer &dest) = 0; + virtual std::unique_ptr clone() const = 0; }; } // namespace spdlog - -#include "details/pattern_formatter_impl.h" diff --git a/third_party/spdlog/spdlog/include/spdlog/logger.h b/third_party/spdlog/spdlog/include/spdlog/logger.h index aa4a2c5d..91eec4bc 100644 --- a/third_party/spdlog/spdlog/include/spdlog/logger.h +++ b/third_party/spdlog/spdlog/include/spdlog/logger.h @@ -1,19 +1,26 @@ // -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-2108 Gabi Melman. // Distributed under the MIT License (http://opensource.org/licenses/MIT) // #pragma once -// Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler()) +// Thread safe logger (except for set_pattern(..), set_formatter(..) and +// set_error_handler()) // 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 +// 1. Checks if its log level is enough to log the message and if yes: +// 2. Call the underlying sinks to do the job. +// 3. Each sink use its own private copy of a formatter to format the message +// and send to its destination. +// +// The use of private formatter per sink provides the opportunity to cache some +// formatted data, +// and support customize format per each sink. -#include "common.h" -#include "sinks/base_sink.h" +#include "spdlog/common.h" +#include "spdlog/formatter.h" +#include "spdlog/sinks/sink.h" #include #include @@ -24,10 +31,10 @@ namespace spdlog { class logger { public: - logger(const std::string &name, sink_ptr single_sink); - logger(const std::string &name, sinks_init_list sinks); + logger(std::string name, sink_ptr single_sink); + logger(std::string name, sinks_init_list sinks); - template + template logger(std::string name, const It &begin, const It &end); virtual ~logger(); @@ -41,28 +48,25 @@ class logger template void log(level::level_enum lvl, const char *msg); - template - void trace(const char *fmt, const Arg1 &, const Args &... args); - - template - void debug(const char *fmt, const Arg1 &, const Args &... args); + template + void trace(const char *fmt, const Args &... args); - template - void info(const char *fmt, const Arg1 &, const Args &... args); + template + void debug(const char *fmt, const Args &... args); - template - void warn(const char *fmt, const Arg1 &, const Args &... args); + template + void info(const char *fmt, const Args &... args); - template - void error(const char *fmt, const Arg1 &, const Args &... args); + template + void warn(const char *fmt, const Args &... args); - template - void critical(const char *fmt, const Arg1 &, const Args &... args); + template + void error(const char *fmt, const Args &... args); -#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template - void log(level::level_enum lvl, const wchar_t *msg); + void critical(const char *fmt, const Args &... args); +#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args); @@ -110,42 +114,48 @@ class logger void set_level(level::level_enum log_level); level::level_enum level() const; const std::string &name() const; - void set_pattern(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local); - void set_formatter(formatter_ptr msg_formatter); - // automatically call flush() if message level >= log_level - void flush_on(level::level_enum log_level); + // set formatting for the sinks in this logger. + // each sink will get a seperate instance of the formatter object. + void set_formatter(std::unique_ptr formatter); + void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local); - virtual void flush(); + void flush(); + void flush_on(level::level_enum log_level); const std::vector &sinks() const; - // error handler - virtual void set_error_handler(log_err_handler err_handler); - virtual log_err_handler error_handler(); + std::vector &sinks(); + + void set_error_handler(log_err_handler err_handler); + log_err_handler error_handler(); protected: - virtual void _sink_it(details::log_msg &msg); - virtual void _set_pattern(const std::string &pattern, pattern_time_type pattern_time); - virtual void _set_formatter(formatter_ptr msg_formatter); - - // 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 &msg); - - // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) - void _incr_msg_counter(details::log_msg &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; - std::atomic _msg_counter; + virtual void sink_it_(details::log_msg &msg); + virtual void flush_(); + + bool should_flush_(const details::log_msg &msg); + + // default error handler: print the error to stderr with the max rate of 1 + // message/minute + void default_err_handler_(const std::string &msg); + + // increment the message count (only if + // defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) + void incr_msg_counter_(details::log_msg &msg); + + const std::string name_; + std::vector sinks_; + spdlog::level_t level_; + spdlog::level_t flush_level_; + log_err_handler err_handler_; + std::atomic last_err_time_; + std::atomic msg_counter_; + +#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT + std::wstring_convert> wstring_converter_; + std::mutex wstring_converter_mutex_; +#endif }; } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/android_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/android_sink.h index dd811633..fcb9ccbf 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/android_sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/android_sink.h @@ -5,10 +5,10 @@ #pragma once -#if defined(__ANDROID__) - -#include "../details/os.h" -#include "sink.h" +#include "spdlog/details/fmt_helper.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/details/os.h" +#include "spdlog/sinks/base_sink.h" #include #include @@ -25,29 +25,40 @@ namespace sinks { /* * Android sink (logging using __android_log_write) - * __android_log_write is thread-safe. No lock is needed. */ -class android_sink : public sink +template +class android_sink SPDLOG_FINAL : public base_sink { public: - explicit android_sink(const std::string &tag = "spdlog", bool use_raw_msg = false) - : _tag(tag) - , _use_raw_msg(use_raw_msg) + explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false) + : tag_(std::move(tag)) + , use_raw_msg_(use_raw_msg) { } - void log(const details::log_msg &msg) override +protected: + void sink_it_(const details::log_msg &msg) override { - const android_LogPriority priority = convert_to_android(msg.level); - const char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str()); + const android_LogPriority priority = convert_to_android_(msg.level); + fmt::memory_buffer formatted; + if (use_raw_msg_) + { + details::fmt_helper::append_buf(msg.raw, formatted); + } + else + { + sink::formatter_->format(msg, formatted); + } + formatted.push_back('\0'); + const char *msg_output = formatted.data(); // See system/core/liblog/logger_write.c for explanation of return value - int ret = __android_log_write(priority, _tag.c_str(), msg_output); + int ret = __android_log_write(priority, tag_.c_str(), msg_output); int retry_count = 0; while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES)) { details::os::sleep_for_millis(5); - ret = __android_log_write(priority, _tag.c_str(), msg_output); + ret = __android_log_write(priority, tag_.c_str(), msg_output); retry_count++; } @@ -57,10 +68,10 @@ class android_sink : public sink } } - void flush() override {} + void flush_() override {} private: - static android_LogPriority convert_to_android(spdlog::level::level_enum level) + static android_LogPriority convert_to_android_(spdlog::level::level_enum level) { switch (level) { @@ -81,11 +92,26 @@ class android_sink : public sink } } - std::string _tag; - bool _use_raw_msg; + std::string tag_; + bool use_raw_msg_; }; +using android_sink_mt = android_sink; +using android_sink_st = android_sink; } // namespace sinks -} // namespace spdlog -#endif +// Create and register android syslog logger + +template +inline std::shared_ptr android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog") +{ + return Factory::template create(logger_name, tag); +} + +template +inline std::shared_ptr android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog") +{ + return Factory::template create(logger_name, tag); +} + +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/ansicolor_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/ansicolor_sink.h index e74389c9..3f6c63b6 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/ansicolor_sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/ansicolor_sink.h @@ -5,10 +5,12 @@ #pragma once -#include "../common.h" -#include "../details/os.h" -#include "base_sink.h" +#include "spdlog/details/console_globals.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/details/os.h" +#include +#include #include #include @@ -16,18 +18,22 @@ namespace spdlog { namespace sinks { /** - * This sink prefixes the output with an ANSI escape sequence color code depending on the severity + * This sink prefixes the output with an ANSI escape sequence color code + * depending on the severity * of the message. * If no color terminal detected, omit the escape codes. */ -template -class ansicolor_sink : public base_sink +template +class ansicolor_sink SPDLOG_FINAL : public sink { public: - explicit ansicolor_sink(FILE *file) - : target_file_(file) + using mutex_t = typename ConsoleMutex::mutex_t; + ansicolor_sink() + : target_file_(TargetStream::stream()) + , mutex_(ConsoleMutex::mutex()) + { - should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal(); + should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal(); colors_[level::trace] = white; colors_[level::debug] = cyan; colors_[level::info] = green; @@ -37,14 +43,14 @@ class ansicolor_sink : public base_sink colors_[level::off] = reset; } - ~ansicolor_sink() override - { - _flush(); - } + ~ansicolor_sink() override = default; + + ansicolor_sink(const ansicolor_sink &other) = delete; + ansicolor_sink &operator=(const ansicolor_sink &other) = delete; void set_color(level::level_enum color_level, const std::string &color) { - std::lock_guard lock(base_sink::_mutex); + std::lock_guard lock(mutex_); colors_[color_level] = color; } @@ -78,73 +84,73 @@ class ansicolor_sink : public base_sink const std::string on_cyan = "\033[46m"; const std::string on_white = "\033[47m"; -protected: - void _sink_it(const details::log_msg &msg) override + void log(const details::log_msg &msg) override { // Wrap the originally formatted message in color codes. // If color is not supported in the terminal, log as is instead. + std::lock_guard lock(mutex_); + + fmt::memory_buffer formatted; + formatter_->format(msg, formatted); if (should_do_colors_ && msg.color_range_end > msg.color_range_start) { // before color range - _print_range(msg, 0, msg.color_range_start); + print_range_(formatted, 0, msg.color_range_start); // in color range - _print_ccode(colors_[msg.level]); - _print_range(msg, msg.color_range_start, msg.color_range_end); - _print_ccode(reset); + print_ccode_(colors_[msg.level]); + print_range_(formatted, msg.color_range_start, msg.color_range_end); + print_ccode_(reset); // after color range - _print_range(msg, msg.color_range_end, msg.formatted.size()); + print_range_(formatted, msg.color_range_end, formatted.size()); } - else + else // no color { - _print_range(msg, 0, msg.formatted.size()); + print_range_(formatted, 0, formatted.size()); } - _flush(); + fflush(target_file_); } - void _flush() override + void flush() override { + std::lock_guard lock(mutex_); fflush(target_file_); } + void set_pattern(const std::string &pattern) SPDLOG_FINAL + { + std::lock_guard lock(mutex_); + formatter_ = std::unique_ptr(new pattern_formatter(pattern)); + } + + void set_formatter(std::unique_ptr sink_formatter) override + { + std::lock_guard lock(mutex_); + formatter_ = std::move(sink_formatter); + } + private: - void _print_ccode(const std::string &color_code) + void print_ccode_(const std::string &color_code) { fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_); } - void _print_range(const details::log_msg &msg, size_t start, size_t end) + void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end) { - fwrite(msg.formatted.data() + start, sizeof(char), end - start, target_file_); + fwrite(formatted.data() + start, sizeof(char), end - start, target_file_); } + FILE *target_file_; + mutex_t &mutex_; + bool should_do_colors_; std::unordered_map colors_; }; -template -class ansicolor_stdout_sink : public ansicolor_sink -{ -public: - ansicolor_stdout_sink() - : ansicolor_sink(stdout) - { - } -}; +using ansicolor_stdout_sink_mt = ansicolor_sink; +using ansicolor_stdout_sink_st = ansicolor_sink; -using ansicolor_stdout_sink_mt = ansicolor_stdout_sink; -using ansicolor_stdout_sink_st = ansicolor_stdout_sink; - -template -class ansicolor_stderr_sink : public ansicolor_sink -{ -public: - ansicolor_stderr_sink() - : ansicolor_sink(stderr) - { - } -}; - -using ansicolor_stderr_sink_mt = ansicolor_stderr_sink; -using ansicolor_stderr_sink_st = ansicolor_stderr_sink; +using ansicolor_stderr_sink_mt = ansicolor_sink; +using ansicolor_stderr_sink_st = ansicolor_sink; } // namespace sinks + } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/base_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/base_sink.h index 96cd001e..43863eae 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/base_sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/base_sink.h @@ -6,44 +6,58 @@ #pragma once // // base sink templated over a mutex (either dummy or real) -// concrete implementation should only override the _sink_it method. -// all locking is taken care of here so no locking needed by the implementers.. +// concrete implementation should override the sink_it_() and flush_() methods. +// locking is taken care of in this class - no locking needed by the +// implementers.. // -#include "../common.h" -#include "../details/log_msg.h" -#include "../formatter.h" -#include "sink.h" - -#include +#include "spdlog/common.h" +#include "spdlog/details/log_msg.h" +#include "spdlog/formatter.h" +#include "spdlog/sinks/sink.h" namespace spdlog { namespace sinks { -template +template class base_sink : public sink { public: - base_sink() = default; + base_sink() + : sink() + { + } base_sink(const base_sink &) = delete; base_sink &operator=(const base_sink &) = delete; - void log(const details::log_msg &msg) SPDLOG_FINAL override + void log(const details::log_msg &msg) SPDLOG_FINAL { - std::lock_guard lock(_mutex); - _sink_it(msg); + std::lock_guard lock(mutex_); + sink_it_(msg); } void flush() SPDLOG_FINAL override { - std::lock_guard lock(_mutex); - _flush(); + std::lock_guard lock(mutex_); + flush_(); + } + + void set_pattern(const std::string &pattern) SPDLOG_FINAL override + { + std::lock_guard lock(mutex_); + formatter_ = std::unique_ptr(new pattern_formatter(pattern)); + } + + void set_formatter(std::unique_ptr sink_formatter) SPDLOG_FINAL override + { + std::lock_guard lock(mutex_); + formatter_ = std::move(sink_formatter); } protected: - virtual void _sink_it(const details::log_msg &msg) = 0; - virtual void _flush() = 0; - Mutex _mutex; + virtual void sink_it_(const details::log_msg &msg) = 0; + virtual void flush_() = 0; + Mutex mutex_; }; } // namespace sinks } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/basic_file_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/basic_file_sink.h new file mode 100644 index 00000000..5db38e8a --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/basic_file_sink.h @@ -0,0 +1,66 @@ +// +// Copyright(c) 2015-2018 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once +#include "spdlog/details/file_helper.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/sinks/base_sink.h" +#include "spdlog/spdlog.h" + +#include +#include + +namespace spdlog { +namespace sinks { +/* + * Trivial file sink with single file as target + */ +template +class basic_file_sink SPDLOG_FINAL : public base_sink +{ +public: + explicit basic_file_sink(const filename_t &filename, bool truncate = false) + { + file_helper_.open(filename, truncate); + } + +protected: + void sink_it_(const details::log_msg &msg) override + { + fmt::memory_buffer formatted; + sink::formatter_->format(msg, formatted); + file_helper_.write(formatted); + } + + void flush_() override + { + file_helper_.flush(); + } + +private: + details::file_helper file_helper_; +}; + +using basic_file_sink_mt = basic_file_sink; +using basic_file_sink_st = basic_file_sink; + +} // namespace sinks + +// +// factory functions +// +template +inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false) +{ + return Factory::template create(logger_name, filename, truncate); +} + +template +inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false) +{ + return Factory::template create(logger_name, filename, truncate); +} + +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/daily_file_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/daily_file_sink.h new file mode 100644 index 00000000..940d55d4 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/daily_file_sink.h @@ -0,0 +1,132 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once +#include "spdlog/details/file_helper.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/fmt/fmt.h" +#include "spdlog/sinks/base_sink.h" +#include "spdlog/spdlog.h" + +#include +#include +#include +#include +#include + +namespace spdlog { +namespace sinks { + +/* + * Generator of daily log file names in format basename.YYYY-MM-DD.ext + */ +struct daily_filename_calculator +{ + // Create filename for the form basename.YYYY-MM-DD + static filename_t calc_filename(const filename_t &filename, const tm &now_tm) + { + filename_t basename, ext; + std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); + std::conditional::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; + fmt::format_to( + w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext); + return fmt::to_string(w); + } +}; + +/* + * Rotating file sink based on date. rotates at midnight + */ +template +class daily_file_sink SPDLOG_FINAL : public base_sink +{ +public: + // create daily file sink which rotates on given time + daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false) + : base_filename_(std::move(base_filename)) + , rotation_h_(rotation_hour) + , rotation_m_(rotation_minute) + , truncate_(truncate) + { + if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) + { + throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); + } + auto now = log_clock::now(); + file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(now)), truncate_); + rotation_tp_ = next_rotation_tp_(); + } + +protected: + void sink_it_(const details::log_msg &msg) override + { + + if (msg.time >= rotation_tp_) + { + file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(msg.time)), truncate_); + rotation_tp_ = next_rotation_tp_(); + } + fmt::memory_buffer formatted; + sink::formatter_->format(msg, formatted); + file_helper_.write(formatted); + } + + void flush_() override + { + file_helper_.flush(); + } + +private: + tm now_tm(log_clock::time_point tp) + { + time_t tnow = log_clock::to_time_t(tp); + return spdlog::details::os::localtime(tnow); + } + + log_clock::time_point next_rotation_tp_() + { + auto now = log_clock::now(); + tm date = now_tm(now); + date.tm_hour = rotation_h_; + date.tm_min = rotation_m_; + date.tm_sec = 0; + auto rotation_time = log_clock::from_time_t(std::mktime(&date)); + if (rotation_time > now) + { + return rotation_time; + } + return {rotation_time + std::chrono::hours(24)}; + } + + filename_t base_filename_; + int rotation_h_; + int rotation_m_; + log_clock::time_point rotation_tp_; + details::file_helper file_helper_; + bool truncate_; +}; + +using daily_file_sink_mt = daily_file_sink; +using daily_file_sink_st = daily_file_sink; + +} // namespace sinks + +// +// factory functions +// +template +inline std::shared_ptr daily_logger_mt( + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false) +{ + return Factory::template create(logger_name, filename, hour, minute, truncate); +} + +template +inline std::shared_ptr daily_logger_st( + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false) +{ + return Factory::template create(logger_name, filename, hour, minute, truncate); +} +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/dist_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/dist_sink.h index 1265cc7e..8d2f6f9e 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/dist_sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/dist_sink.h @@ -5,37 +5,46 @@ #pragma once -#include "../details/log_msg.h" -#include "../details/null_mutex.h" #include "base_sink.h" -#include "sink.h" +#include "spdlog/details/log_msg.h" +#include "spdlog/details/null_mutex.h" #include #include #include #include -// Distribution sink (mux). Stores a vector of sinks which get called when log is called +// Distribution sink (mux). Stores a vector of sinks which get called when log +// is called namespace spdlog { namespace sinks { -template + +template class dist_sink : public base_sink { public: - explicit dist_sink() - : _sinks() - { - } + dist_sink() = default; dist_sink(const dist_sink &) = delete; dist_sink &operator=(const dist_sink &) = delete; -protected: - std::vector> _sinks; + 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()); + } - void _sink_it(const details::log_msg &msg) override +protected: + void sink_it_(const details::log_msg &msg) override { - for (auto &sink : _sinks) + + for (auto &sink : sinks_) { if (sink->should_log(msg.level)) { @@ -44,24 +53,12 @@ class dist_sink : public base_sink } } - void _flush() override + void flush_() override { - for (auto &sink : _sinks) + for (auto &sink : sinks_) sink->flush(); } - -public: - 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()); - } + std::vector> sinks_; }; using dist_sink_mt = dist_sink; diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/file_sinks.h b/third_party/spdlog/spdlog/include/spdlog/sinks/file_sinks.h deleted file mode 100644 index 109c493a..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/file_sinks.h +++ /dev/null @@ -1,255 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include "../details/file_helper.h" -#include "../details/null_mutex.h" -#include "../fmt/fmt.h" -#include "base_sink.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace spdlog { -namespace sinks { -/* - * Trivial file sink with single file as target - */ -template -class simple_file_sink SPDLOG_FINAL : public base_sink -{ -public: - explicit simple_file_sink(const filename_t &filename, bool truncate = false) - : _force_flush(false) - { - _file_helper.open(filename, truncate); - } - - void set_force_flush(bool force_flush) - { - _force_flush = force_flush; - } - -protected: - void _sink_it(const details::log_msg &msg) override - { - _file_helper.write(msg); - if (_force_flush) - { - _file_helper.flush(); - } - } - - void _flush() override - { - _file_helper.flush(); - } - -private: - details::file_helper _file_helper; - bool _force_flush; -}; - -using simple_file_sink_mt = simple_file_sink; -using simple_file_sink_st = simple_file_sink; - -/* - * Rotating file sink based on size - */ -template -class rotating_file_sink SPDLOG_FINAL : public base_sink -{ -public: - rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files) - : _base_filename(std::move(base_filename)) - , _max_size(max_size) - , _max_files(max_files) - { - _file_helper.open(calc_filename(_base_filename, 0)); - _current_size = _file_helper.size(); // expensive. called only once - } - - // calc filename according to index and file extension if exists. - // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". - static filename_t calc_filename(const filename_t &filename, std::size_t index) - { - typename std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; - if (index != 0u) - { - filename_t basename, ext; - std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); - w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); - } - else - { - w.write(SPDLOG_FILENAME_T("{}"), filename); - } - return w.str(); - } - -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); - } - - void _flush() override - { - _file_helper.flush(); - } - -private: - // Rotate files: - // log.txt -> log.1.txt - // log.1.txt -> log.2.txt - // log.2.txt -> log.3.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); - filename_t target = calc_filename(_base_filename, i); - - 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) != 0) - { - 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; - std::size_t _max_size; - std::size_t _max_files; - std::size_t _current_size; - details::file_helper _file_helper; -}; - -using rotating_file_sink_mt = rotating_file_sink; -using rotating_file_sink_st = rotating_file_sink; - -/* - * Default generator of daily log file names. - */ -struct default_daily_file_name_calculator -{ - // Create filename for the form filename.YYYY-MM-DD_hh-mm.ext - static filename_t calc_filename(const filename_t &filename) - { - std::tm tm = spdlog::details::os::localtime(); - filename_t basename, ext; - std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); - 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, ext); - return w.str(); - } -}; - -/* - * Generator of daily log file names in format basename.YYYY-MM-DD.ext - */ -struct dateonly_daily_file_name_calculator -{ - // Create filename for the form basename.YYYY-MM-DD - static filename_t calc_filename(const filename_t &filename) - { - std::tm tm = spdlog::details::os::localtime(); - filename_t basename, ext; - std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); - 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, ext); - return w.str(); - } -}; - -/* - * Rotating file sink based on date. rotates at midnight - */ -template -class daily_file_sink SPDLOG_FINAL : public base_sink -{ -public: - // create daily file sink which rotates on given time - daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute) - : _base_filename(std::move(base_filename)) - , _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)); - } - -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)); - _rotation_tp = _next_rotation_tp(); - } - _file_helper.write(msg); - } - - void _flush() override - { - _file_helper.flush(); - } - -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; - } - return {rotation_time + std::chrono::hours(24)}; - } - - filename_t _base_filename; - int _rotation_h; - int _rotation_m; - std::chrono::system_clock::time_point _rotation_tp; - details::file_helper _file_helper; -}; - -using daily_file_sink_mt = daily_file_sink; -using daily_file_sink_st = daily_file_sink; - -} // namespace sinks -} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/msvc_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/msvc_sink.h index 09a5d671..a0284960 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/msvc_sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/msvc_sink.h @@ -7,8 +7,8 @@ #if defined(_WIN32) -#include "../details/null_mutex.h" -#include "base_sink.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/sinks/base_sink.h" #include @@ -20,24 +20,30 @@ namespace sinks { /* * MSVC sink (logging using OutputDebugStringA) */ -template +template class msvc_sink : public base_sink { public: explicit msvc_sink() {} protected: - void _sink_it(const details::log_msg &msg) override + void sink_it_(const details::log_msg &msg) override { - OutputDebugStringA(msg.formatted.c_str()); + + fmt::memory_buffer formatted; + sink::formatter_->format(msg, formatted); + OutputDebugStringA(fmt::to_string(formatted).c_str()); } - void _flush() override {} + void flush_() override {} }; using msvc_sink_mt = msvc_sink; using msvc_sink_st = msvc_sink; +using windebug_sink_mt = msvc_sink_mt; +using windebug_sink_st = msvc_sink_st; + } // namespace sinks } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/null_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/null_sink.h index c254bc59..73b8c2b7 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/null_sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/null_sink.h @@ -5,24 +5,23 @@ #pragma once -#include "../details/null_mutex.h" -#include "base_sink.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/sinks/base_sink.h" #include namespace spdlog { namespace sinks { -template +template class null_sink : public base_sink { protected: - void _sink_it(const details::log_msg &) override {} - - void _flush() override {} + void sink_it_(const details::log_msg &) override {} + void flush_() override {} }; -using null_sink_mt = null_sink; +using null_sink_mt = null_sink; using null_sink_st = null_sink; } // namespace sinks diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/ostream_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/ostream_sink.h index 97281384..5f31993c 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/ostream_sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/ostream_sink.h @@ -5,41 +5,43 @@ #pragma once -#include "../details/null_mutex.h" -#include "base_sink.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/sinks/base_sink.h" #include #include namespace spdlog { namespace sinks { -template -class ostream_sink : public base_sink +template +class ostream_sink SPDLOG_FINAL : public base_sink { public: explicit ostream_sink(std::ostream &os, bool force_flush = false) - : _ostream(os) - , _force_flush(force_flush) + : ostream_(os) + , force_flush_(force_flush) { } ostream_sink(const ostream_sink &) = delete; ostream_sink &operator=(const ostream_sink &) = delete; protected: - void _sink_it(const details::log_msg &msg) override + void sink_it_(const details::log_msg &msg) override { - _ostream.write(msg.formatted.data(), msg.formatted.size()); - if (_force_flush) - _ostream.flush(); + fmt::memory_buffer formatted; + sink::formatter_->format(msg, formatted); + ostream_.write(formatted.data(), formatted.size()); + if (force_flush_) + ostream_.flush(); } - void _flush() override + void flush_() override { - _ostream.flush(); + ostream_.flush(); } - std::ostream &_ostream; - bool _force_flush; + std::ostream &ostream_; + bool force_flush_; }; using ostream_sink_mt = ostream_sink; diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/rotating_file_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/rotating_file_sink.h new file mode 100644 index 00000000..7ed2385a --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/rotating_file_sink.h @@ -0,0 +1,144 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once +#include "spdlog/details/file_helper.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/fmt/fmt.h" +#include "spdlog/sinks/base_sink.h" +#include "spdlog/spdlog.h" + +#include +#include +#include +#include +#include +#include + +namespace spdlog { +namespace sinks { + +// +// Rotating file sink based on size +// +template +class rotating_file_sink SPDLOG_FINAL : public base_sink +{ +public: + rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files) + : base_filename_(std::move(base_filename)) + , max_size_(max_size) + , max_files_(max_files) + { + file_helper_.open(calc_filename(base_filename_, 0)); + current_size_ = file_helper_.size(); // expensive. called only once + } + + // calc filename according to index and file extension if exists. + // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". + static filename_t calc_filename(const filename_t &filename, std::size_t index) + { + typename std::conditional::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; + if (index != 0u) + { + filename_t basename, ext; + std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); + fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); + } + else + { + fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename); + } + return fmt::to_string(w); + } + +protected: + void sink_it_(const details::log_msg &msg) override + { + fmt::memory_buffer formatted; + sink::formatter_->format(msg, formatted); + current_size_ += formatted.size(); + if (current_size_ > max_size_) + { + rotate_(); + current_size_ = formatted.size(); + } + file_helper_.write(formatted); + } + + void flush_() override + { + file_helper_.flush(); + } + +private: + // Rotate files: + // log.txt -> log.1.txt + // log.1.txt -> log.2.txt + // log.2.txt -> log.3.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); + filename_t target = calc_filename(base_filename_, i); + + 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) != 0) + { + // if failed try again after small delay. + // this is a workaround to a windows issue, where very high rotation + // rates sometimes fail (because of antivirus?). + details::os::sleep_for_millis(20); + details::os::remove(target); + if (details::os::rename(src, target) != 0) + { + 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_; + std::size_t max_size_; + std::size_t max_files_; + std::size_t current_size_; + details::file_helper file_helper_; +}; + +using rotating_file_sink_mt = rotating_file_sink; +using rotating_file_sink_st = rotating_file_sink; + +} // namespace sinks + +// +// factory functions +// + +template +inline std::shared_ptr rotating_logger_mt( + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files) +{ + return Factory::template create(logger_name, filename, max_file_size, max_files); +} + +template +inline std::shared_ptr rotating_logger_st( + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files) +{ + return Factory::template create(logger_name, filename, max_file_size, max_files); +} +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/sink.h index b4e38068..cb8aecb4 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/sink.h @@ -5,40 +5,53 @@ #pragma once -#include "../details/log_msg.h" +#include "spdlog/details/log_msg.h" +#include "spdlog/details/pattern_formatter.h" +#include "spdlog/formatter.h" namespace spdlog { namespace sinks { class sink { public: - virtual ~sink() = default; + sink() + : level_(level::trace) + , formatter_(new pattern_formatter("%+")) + { + } + + explicit sink(std::unique_ptr formatter) + : level_(level::trace) + , formatter_(std::move(formatter)){}; + virtual ~sink() = default; 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{level::trace}; + virtual void set_pattern(const std::string &pattern) = 0; + virtual void set_formatter(std::unique_ptr sink_formatter) = 0; + + bool should_log(level::level_enum msg_level) const + { + return msg_level >= level_.load(std::memory_order_relaxed); + } + + void set_level(level::level_enum log_level) + { + level_.store(log_level); + } + + level::level_enum level() const + { + return static_cast(level_.load(std::memory_order_relaxed)); + } + +protected: + // sink log level - default is all + level_t level_; + + // sink formatter - default is full format + std::unique_ptr formatter_; }; -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)); -} - } // namespace sinks } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/stdout_color_sinks.h b/third_party/spdlog/spdlog/include/spdlog/sinks/stdout_color_sinks.h new file mode 100644 index 00000000..761e32a4 --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/stdout_color_sinks.h @@ -0,0 +1,53 @@ +// +// Copyright(c) 2018 spdlog +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "spdlog/spdlog.h" +#ifdef _WIN32 +#include "spdlog/sinks/wincolor_sink.h" +#else +#include "spdlog/sinks/ansicolor_sink.h" +#endif + +namespace spdlog { +namespace sinks { +#ifdef _WIN32 +using stdout_color_sink_mt = wincolor_stdout_sink_mt; +using stdout_color_sink_st = wincolor_stdout_sink_st; +using stderr_color_sink_mt = wincolor_stderr_sink_mt; +using stderr_color_sink_st = wincolor_stderr_sink_st; +#else +using stdout_color_sink_mt = ansicolor_stdout_sink_mt; +using stdout_color_sink_st = ansicolor_stdout_sink_st; +using stderr_color_sink_mt = ansicolor_stderr_sink_mt; +using stderr_color_sink_st = ansicolor_stderr_sink_st; +#endif +} // namespace sinks + +template +inline std::shared_ptr stdout_color_mt(const std::string &logger_name) +{ + return Factory::template create(logger_name); +} + +template +inline std::shared_ptr stdout_color_st(const std::string &logger_name) +{ + return Factory::template create(logger_name); +} + +template +inline std::shared_ptr stderr_color_mt(const std::string &logger_name) +{ + return Factory::template create(logger_name); +} + +template +inline std::shared_ptr stderr_color_st(const std::string &logger_name) +{ + return Factory::template create(logger_name); +} +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/stdout_sinks.h b/third_party/spdlog/spdlog/include/spdlog/sinks/stdout_sinks.h index b15d0803..e12739c1 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/stdout_sinks.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/stdout_sinks.h @@ -5,75 +5,96 @@ #pragma once -#include "../details/null_mutex.h" -#include "base_sink.h" +#include "spdlog/details/console_globals.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/spdlog.h" #include #include #include +#include namespace spdlog { + namespace sinks { -template -class stdout_sink SPDLOG_FINAL : public base_sink +template +class stdout_sink SPDLOG_FINAL : public sink { - using MyType = stdout_sink; - public: - explicit stdout_sink() = default; - - static std::shared_ptr instance() + using mutex_t = typename ConsoleMutex::mutex_t; + stdout_sink() + : mutex_(ConsoleMutex::mutex()) + , file_(TargetStream::stream()) { - static std::shared_ptr instance = std::make_shared(); - return instance; } + ~stdout_sink() override = default; -protected: - void _sink_it(const details::log_msg &msg) override - { - fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout); - _flush(); - } + stdout_sink(const stdout_sink &other) = delete; + stdout_sink &operator=(const stdout_sink &other) = delete; - void _flush() override + void log(const details::log_msg &msg) override { - fflush(stdout); + std::lock_guard lock(mutex_); + fmt::memory_buffer formatted; + formatter_->format(msg, formatted); + fwrite(formatted.data(), sizeof(char), formatted.size(), file_); + fflush(TargetStream::stream()); } -}; - -using stdout_sink_mt = stdout_sink; -using stdout_sink_st = stdout_sink; - -template -class stderr_sink SPDLOG_FINAL : public base_sink -{ - using MyType = stderr_sink; - -public: - explicit stderr_sink() = default; - static std::shared_ptr instance() + void flush() override { - static std::shared_ptr instance = std::make_shared(); - return instance; + std::lock_guard lock(mutex_); + fflush(file_); } -protected: - void _sink_it(const details::log_msg &msg) override + void set_pattern(const std::string &pattern) override { - fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr); - _flush(); + std::lock_guard lock(mutex_); + formatter_ = std::unique_ptr(new pattern_formatter(pattern)); } - void _flush() override + void set_formatter(std::unique_ptr sink_formatter) override { - fflush(stderr); + std::lock_guard lock(mutex_); + formatter_ = std::move(sink_formatter); } + +private: + mutex_t &mutex_; + FILE *file_; }; -using stderr_sink_mt = stderr_sink; -using stderr_sink_st = stderr_sink; +using stdout_sink_mt = stdout_sink; +using stdout_sink_st = stdout_sink; + +using stderr_sink_mt = stdout_sink; +using stderr_sink_st = stdout_sink; } // namespace sinks + +// factory methods +template +inline std::shared_ptr stdout_logger_mt(const std::string &logger_name) +{ + return Factory::template create(logger_name); +} + +template +inline std::shared_ptr stdout_logger_st(const std::string &logger_name) +{ + return Factory::template create(logger_name); +} + +template +inline std::shared_ptr stderr_logger_mt(const std::string &logger_name) +{ + return Factory::template create(logger_name); +} + +template +inline std::shared_ptr stderr_logger_st(const std::string &logger_name) +{ + return Factory::template create(logger_name); +} } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/syslog_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/syslog_sink.h index 17bbb1df..151e7a11 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/syslog_sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/syslog_sink.h @@ -5,12 +5,8 @@ #pragma once -#include "../common.h" - -#ifdef SPDLOG_ENABLE_SYSLOG - -#include "../details/log_msg.h" -#include "sink.h" +#include "spdlog/sinks/base_sink.h" +#include "spdlog/spdlog.h" #include #include @@ -23,23 +19,24 @@ namespace sinks { * * Locking is not needed, as `syslog()` itself is thread-safe. */ -class syslog_sink : public sink +template +class syslog_sink : public base_sink { public: // - syslog_sink(const std::string &ident = "", int syslog_option = 0, int syslog_facility = LOG_USER) - : _ident(ident) + explicit syslog_sink(std::string ident = "", int syslog_option = 0, int syslog_facility = LOG_USER) + : ident_(std::move(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; + 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); + ::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility); } ~syslog_sink() override @@ -50,27 +47,45 @@ class syslog_sink : public sink syslog_sink(const syslog_sink &) = delete; syslog_sink &operator=(const syslog_sink &) = delete; - void log(const details::log_msg &msg) override +protected: + void sink_it_(const details::log_msg &msg) override { - ::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str()); + ::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.raw).c_str()); } - void flush() override {} + 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; + 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)]; + return priorities_[static_cast(msg.level)]; } }; + +using syslog_sink_mt = syslog_sink; +using syslog_sink_st = syslog_sink; } // namespace sinks -} // namespace spdlog -#endif +// Create and register a syslog logger +template +inline std::shared_ptr syslog_logger_mt( + const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3)) +{ + return Factory::template create(logger_name, syslog_ident, syslog_option, syslog_facility); +} + +template +inline std::shared_ptr syslog_logger_st( + const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3)) +{ + return Factory::template create(logger_name, syslog_ident, syslog_option, syslog_facility); +} +} // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/wincolor_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/wincolor_sink.h index 402fa121..8c63e7fc 100644 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/wincolor_sink.h +++ b/third_party/spdlog/spdlog/include/spdlog/sinks/wincolor_sink.h @@ -5,10 +5,12 @@ #pragma once -#include "../common.h" -#include "../details/null_mutex.h" -#include "base_sink.h" +#include "spdlog/common.h" +#include "spdlog/details/console_globals.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/sinks/sink.h" +#include #include #include #include @@ -17,10 +19,11 @@ namespace spdlog { namespace sinks { /* - * Windows color console sink. Uses WriteConsoleA to write to the console with colors + * Windows color console sink. Uses WriteConsoleA to write to the console with + * colors */ -template -class wincolor_sink : public base_sink +template +class wincolor_sink : public sink { public: const WORD BOLD = FOREGROUND_INTENSITY; @@ -30,8 +33,9 @@ class wincolor_sink : public base_sink const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN; - wincolor_sink(HANDLE std_handle) - : out_handle_(std_handle) + wincolor_sink() + : out_handle_(OutHandle::handle()) + , mutex_(ConsoleMutex::mutex()) { colors_[level::trace] = WHITE; colors_[level::debug] = CYAN; @@ -53,92 +57,83 @@ class wincolor_sink : public base_sink // change the color for the given level void set_color(level::level_enum level, WORD color) { - std::lock_guard lock(base_sink::_mutex); + std::lock_guard lock(mutex_); colors_[level] = color; } -protected: - void _sink_it(const details::log_msg &msg) override + void log(const details::log_msg &msg) SPDLOG_FINAL override { + std::lock_guard lock(mutex_); + fmt::memory_buffer formatted; + formatter_->format(msg, formatted); if (msg.color_range_end > msg.color_range_start) { // before color range - _print_range(msg, 0, msg.color_range_start); + print_range_(formatted, 0, msg.color_range_start); // in color range auto orig_attribs = set_console_attribs(colors_[msg.level]); - _print_range(msg, msg.color_range_start, msg.color_range_end); - ::SetConsoleTextAttribute(out_handle_, orig_attribs); // reset to orig colors - // after color range - _print_range(msg, msg.color_range_end, msg.formatted.size()); + print_range_(formatted, msg.color_range_start, msg.color_range_end); + ::SetConsoleTextAttribute(out_handle_, + orig_attribs); // reset to orig colors + // after color range + print_range_(formatted, msg.color_range_end, formatted.size()); } else // print without colors if color range is invalid { - _print_range(msg, 0, msg.formatted.size()); + print_range_(formatted, 0, formatted.size()); } } - void _flush() override + void flush() SPDLOG_FINAL override { // windows console always flushed? } -private: - HANDLE out_handle_; - std::unordered_map colors_; + void set_pattern(const std::string &pattern) override SPDLOG_FINAL + { + std::lock_guard lock(mutex_); + formatter_ = std::unique_ptr(new pattern_formatter(pattern)); + } + void set_formatter(std::unique_ptr sink_formatter) override SPDLOG_FINAL + { + std::lock_guard lock(mutex_); + formatter_ = std::move(sink_formatter); + } + +private: + using mutex_t = typename ConsoleMutex::mutex_t; // set color and return the orig console attributes (for resetting later) WORD set_console_attribs(WORD attribs) { CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info; - GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info); + ::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info); WORD back_color = orig_buffer_info.wAttributes; // retrieve the current background color back_color &= static_cast(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)); // keep the background color unchanged - SetConsoleTextAttribute(out_handle_, attribs | back_color); + ::SetConsoleTextAttribute(out_handle_, attribs | back_color); return orig_buffer_info.wAttributes; // return orig attribs } // print a range of formatted message to console - void _print_range(const details::log_msg &msg, size_t start, size_t end) + void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end) { - DWORD size = static_cast(end - start); - WriteConsoleA(out_handle_, msg.formatted.data() + start, size, nullptr, nullptr); + auto size = static_cast(end - start); + ::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr); } -}; -// -// windows color console to stdout -// -template -class wincolor_stdout_sink : public wincolor_sink -{ -public: - wincolor_stdout_sink() - : wincolor_sink(GetStdHandle(STD_OUTPUT_HANDLE)) - { - } + HANDLE out_handle_; + mutex_t &mutex_; + std::unordered_map colors_; }; -using wincolor_stdout_sink_mt = wincolor_stdout_sink; -using wincolor_stdout_sink_st = wincolor_stdout_sink; - -// -// windows color console to stderr -// -template -class wincolor_stderr_sink : public wincolor_sink -{ -public: - wincolor_stderr_sink() - : wincolor_sink(GetStdHandle(STD_ERROR_HANDLE)) - { - } -}; +using wincolor_stdout_sink_mt = wincolor_sink; +using wincolor_stdout_sink_st = wincolor_sink; -using wincolor_stderr_sink_mt = wincolor_stderr_sink; -using wincolor_stderr_sink_st = wincolor_stderr_sink; +using wincolor_stderr_sink_mt = wincolor_sink; +using wincolor_stderr_sink_st = wincolor_sink; } // namespace sinks } // namespace spdlog diff --git a/third_party/spdlog/spdlog/include/spdlog/sinks/windebug_sink.h b/third_party/spdlog/spdlog/include/spdlog/sinks/windebug_sink.h deleted file mode 100644 index 2c7f1bc2..00000000 --- a/third_party/spdlog/spdlog/include/spdlog/sinks/windebug_sink.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright(c) 2017 Alexander Dalshov. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#if defined(_WIN32) - -#include "msvc_sink.h" - -namespace spdlog { -namespace sinks { - -/* - * Windows debug sink (logging using OutputDebugStringA, synonym for msvc_sink) - */ -template -using windebug_sink = msvc_sink; - -using windebug_sink_mt = msvc_sink_mt; -using windebug_sink_st = msvc_sink_st; - -} // namespace sinks -} // namespace spdlog - -#endif diff --git a/third_party/spdlog/spdlog/include/spdlog/spdlog.h b/third_party/spdlog/spdlog/include/spdlog/spdlog.h index 21f5951b..40640ab9 100644 --- a/third_party/spdlog/spdlog/include/spdlog/spdlog.h +++ b/third_party/spdlog/spdlog/include/spdlog/spdlog.h @@ -1,5 +1,5 @@ // -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-2018 Gabi Melman. // Distributed under the MIT License (http://opensource.org/licenses/MIT) // // spdlog main header file. @@ -7,8 +7,10 @@ #pragma once -#include "common.h" -#include "logger.h" +#include "spdlog/common.h" +#include "spdlog/details/registry.h" +#include "spdlog/logger.h" +#include "spdlog/version.h" #include #include @@ -17,160 +19,115 @@ namespace spdlog { -// -// Return an existing logger or nullptr if a logger with such name doesn't exist. +// Default logger factory- creates synchronous loggers +struct synchronous_factory +{ + template + + static std::shared_ptr create(std::string logger_name, SinkArgs &&... args) + { + auto sink = std::make_shared(std::forward(args)...); + auto new_logger = std::make_shared(std::move(logger_name), std::move(sink)); + details::registry::instance().register_and_init(new_logger); + return new_logger; + } +}; + +using default_factory = synchronous_factory; + +// Create and register a logger with a templated sink type +// The logger's level, formatter and flush level will be set according the +// global settings. +// Example: +// spdlog::create("logger_name", "dailylog_filename", 11, 59); +template +inline std::shared_ptr create(std::string logger_name, SinkArgs &&... sink_args) +{ + return default_factory::create(std::move(logger_name), std::forward(sink_args)...); +} + +// 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 +inline std::shared_ptr get(const std::string &name) +{ + return details::registry::instance().get(name); +} + +// Set global formatter. Each sink in each logger will get a clone of this object +inline void set_formatter(std::unique_ptr formatter) +{ + details::registry::instance().set_formatter(std::move(formatter)); +} + +// Set global format string. // 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); +inline void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local) +{ + set_formatter(std::unique_ptr(new pattern_formatter(std::move(pattern), time_type))); +} -// // Set global logging level -// -void set_level(level::level_enum log_level); +inline void set_level(level::level_enum log_level) +{ + details::registry::instance().set_level(log_level); +} -// // Set global flush level -// -void flush_on(level::level_enum log_level); +inline void flush_on(level::level_enum log_level) +{ + details::registry::instance().flush_on(log_level); +} + +// Start/Restart a periodic flusher thread +// Warning: Use only if all your loggers are thread safe! +inline void flush_every(std::chrono::seconds interval) +{ + details::registry::instance().flush_every(interval); +} -// // Set global error handler -// -void set_error_handler(log_err_handler 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 limitations 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); -std::shared_ptr stdout_logger_st(const std::string &logger_name); -std::shared_ptr stderr_logger_mt(const std::string &logger_name); -std::shared_ptr stderr_logger_st(const std::string &logger_name); -// -// Create and register colored stdout/stderr loggers -// -std::shared_ptr stdout_color_mt(const std::string &logger_name); -std::shared_ptr stdout_color_st(const std::string &logger_name); -std::shared_ptr stderr_color_mt(const std::string &logger_name); -std::shared_ptr stderr_color_st(const std::string &logger_name); - -// -// 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, int syslog_facilty = (1 << 3)); -#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 with 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"); -template -std::shared_ptr create(const std::string &logger_name, Args... args); - -// Create and register an async logger with a single sink -std::shared_ptr create_async(const std::string &logger_name, const sink_ptr &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); - -// Create and register an async logger with multiple sinks -std::shared_ptr create_async(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); - -template -std::shared_ptr create_async(const std::string &logger_name, const It &sinks_begin, const It &sinks_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); +inline void set_error_handler(log_err_handler handler) +{ + details::registry::instance().set_error_handler(std::move(handler)); +} // Register the given logger with the given name -void register_logger(std::shared_ptr logger); +inline void register_logger(std::shared_ptr logger) +{ + details::registry::instance().register_logger(std::move(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); +inline void apply_all(const std::function)> &fun) +{ + details::registry::instance().apply_all(fun); +} // Drop the reference to the given logger -void drop(const std::string &name); +inline void drop(const std::string &name) +{ + details::registry::instance().drop(name); +} // Drop all references from the registry -void drop_all(); +inline void drop_all() +{ + details::registry::instance().drop_all(); +} + +// stop any running threads started by spdlog and clean registry loggers +inline void shutdown() +{ + details::registry::instance().shutdown(); +} /////////////////////////////////////////////////////////////////////////////// // -// Trace & Debug can be switched on/off at compile time for zero cost debug statements. +// Trace & Debug can be switched on/off at compile time for zero cost debug +// statements. // Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable. // SPDLOG_TRACE(..) will also print current file and line. // @@ -187,7 +144,9 @@ void drop_all(); #ifdef _MSC_VER #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__) #else -#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__) +#define SPDLOG_TRACE(logger, ...) \ + logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ]" \ + " " __VA_ARGS__) #endif #else #define SPDLOG_TRACE(logger, ...) (void)0 @@ -200,5 +159,3 @@ void drop_all(); #endif } // namespace spdlog - -#include "details/spdlog_impl.h" diff --git a/third_party/spdlog/spdlog/include/spdlog/tweakme.h b/third_party/spdlog/spdlog/include/spdlog/tweakme.h index 50ad3fb4..53256450 100644 --- a/third_party/spdlog/spdlog/include/spdlog/tweakme.h +++ b/third_party/spdlog/spdlog/include/spdlog/tweakme.h @@ -7,24 +7,29 @@ /////////////////////////////////////////////////////////////////////////////// // -// Edit this file to squeeze more performance, and to customize supported features +// 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. +// 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. +// Uncomment if date/time logging is not needed and never appear in the log +// pattern. // This will prevent spdlog from querying 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 +// 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 /////////////////////////////////////////////////////////////////////////////// @@ -33,7 +38,8 @@ // Uncomment if thread id logging is not needed (i.e. no %t in the log pattern). // This will prevent spdlog from querying 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. +// 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 /////////////////////////////////////////////////////////////////////////////// @@ -42,7 +48,8 @@ // Uncomment to prevent spdlog from caching thread ids in thread local storage. // By default spdlog saves thread ids in tls to gain a few micros for each call. // -// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined thread ids in the children logs. +// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined +// thread ids in the children logs. // // #define SPDLOG_DISABLE_TID_CACHING /////////////////////////////////////////////////////////////////////////////// @@ -61,17 +68,10 @@ // #define SPDLOG_TRACE_ON /////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()). -// Use only if your code never modifies 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. +// Use only if your code never modifies a logger's log levels concurrently by +// different threads. // // #define SPDLOG_NO_ATOMIC_LEVELS /////////////////////////////////////////////////////////////////////////////// @@ -90,24 +90,12 @@ /////////////////////////////////////////////////////////////////////////////// // 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. +// In this case spdlog will try to include so set your -I flag +// accordingly. // // #define SPDLOG_FMT_EXTERNAL /////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to use printf-style messages in your logs instead of the usual -// format-style used by default. -// -// #define SPDLOG_FMT_PRINTF -/////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////// -// Uncomment to enable syslog (disabled by default) -// -// #define SPDLOG_ENABLE_SYSLOG -/////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////// // Uncomment to enable wchar_t support (convert to utf8) // @@ -139,5 +127,6 @@ /////////////////////////////////////////////////////////////////////////////// // Uncomment to customize level names (e.g. "MT TRACE") // -// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" } +// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", +// "MY ERROR", "MY CRITICAL", "OFF" } /////////////////////////////////////////////////////////////////////////////// diff --git a/third_party/spdlog/spdlog/include/spdlog/version.h b/third_party/spdlog/spdlog/include/spdlog/version.h new file mode 100644 index 00000000..f14078ec --- /dev/null +++ b/third_party/spdlog/spdlog/include/spdlog/version.h @@ -0,0 +1,12 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#define SPDLOG_VER_MAJOR 1 +#define SPDLOG_VER_MINOR 1 +#define SPDLOG_VER_PATCH 0 + +#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH) From 969996ceb7adb841b0b7ee6bcd39eea3e376c68f Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Fri, 31 Aug 2018 17:20:16 +0200 Subject: [PATCH 07/21] add configuration header file and test find_package(SIRIUS) helper configuration definitions: * SIRIUS_ENABLE_LOGS * SIRIUS_ENABLE_CACHE_OPTIMIZATION * GSL_TERMINATE_ON_CONTRACT_VIOLATION * GSL_UNENFORCED_ON_CONTRACT_VIOLATION --- .appveyor.yml | 7 ++ .travis/docker/build_install_sirius.sh | 1 + .travis/linux/build.sh | 15 ++++- .travis/osx/build.sh | 14 +++- VERSION | 1 + cmake/ExtractVersion.cmake | 37 ++++++++-- cmake/FindSIRIUS.cmake | 58 ++++++++++++---- src/CMakeLists.txt | 57 ++++++++++------ src/sirius/config.h.in | 39 +++++++++++ src/sirius/fftw/fftw.cc | 2 + src/sirius/filter.cc | 2 + src/sirius/utils/gsl.h | 2 + src/sirius/utils/log.h | 2 + tests/find-package-test/CMakeLists.txt | 71 ++++++++++++++++++++ tests/find-package-test/basic_sirius_test.cc | 43 ++++++++++++ third_party/gsl/CMakeLists.txt | 8 +-- 16 files changed, 308 insertions(+), 51 deletions(-) create mode 100644 VERSION create mode 100644 src/sirius/config.h.in create mode 100644 tests/find-package-test/CMakeLists.txt create mode 100644 tests/find-package-test/basic_sirius_test.cc diff --git a/.appveyor.yml b/.appveyor.yml index 0d56e394..f14e0c19 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -64,6 +64,13 @@ test_script: - ctest --output-on-failure - appveyor AddMessage "Running sirius..." - src\%configuration%\sirius.exe -h + - src\%configuration%\sirius.exe -v trace -r 2 ..\data\input\lena.jpg lena_z2.tif + - appveyor AddMessage "Testing find_package(SIRIUS)..." + - mkdir %APPVEYOR_BUILD_FOLDER%\.build-test-find-package + - cd %APPVEYOR_BUILD_FOLDER%\.build-test-find-package + - cmake ..\tests\find-package-test -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DSIRIUS_ROOT=%APPVEYOR_BUILD_FOLDER%/.install-directory + - cmake --build . --config %configuration% + - Release\basic_sirius_static_test.exe # to disable deployment deploy: off diff --git a/.travis/docker/build_install_sirius.sh b/.travis/docker/build_install_sirius.sh index 05e94779..e147a770 100755 --- a/.travis/docker/build_install_sirius.sh +++ b/.travis/docker/build_install_sirius.sh @@ -51,6 +51,7 @@ make -j4 sirius # execute sirius ./src/sirius -h +./src/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif # install project make -j4 install diff --git a/.travis/linux/build.sh b/.travis/linux/build.sh index 6e63b865..d1965195 100755 --- a/.travis/linux/build.sh +++ b/.travis/linux/build.sh @@ -53,6 +53,19 @@ make -j4 sirius # execute sirius ./src/sirius -h + ./src/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif # install project -make -j4 install \ No newline at end of file +make -j4 install + +# test find_package(SIRIUS) +cd ${PROJECT_DIR} +mkdir ${PROJECT_DIR}/.build-test-find-package +cd ${PROJECT_DIR}/.build-test-find-package +cmake ../tests/find-package-test \ + -G "Unix Makefiles" \ + -DCMAKE_CXX_COMPILER="${CXX_COMPILER}" \ + -DSIRIUS_ROOT=${INSTALL_DIR} \ + -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} +make -j4 +./basic_sirius_static_test \ No newline at end of file diff --git a/.travis/osx/build.sh b/.travis/osx/build.sh index cd03a50b..c03cd1f5 100755 --- a/.travis/osx/build.sh +++ b/.travis/osx/build.sh @@ -50,6 +50,18 @@ make -j4 sirius # execute sirius ./src/sirius -h + ./src/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif # install project -make -j4 install \ No newline at end of file +make -j4 install + +# test find_package(SIRIUS) +cd ${PROJECT_DIR} +mkdir ${PROJECT_DIR}/.build-test-find-package +cd ${PROJECT_DIR}/.build-test-find-package +cmake ../tests/find-package-test \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE=Release \ + -DSIRIUS_ROOT=${INSTALL_DIR} +make -j4 +./basic_sirius_static_test \ No newline at end of file diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..6c6aa7cb --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 \ No newline at end of file diff --git a/cmake/ExtractVersion.cmake b/cmake/ExtractVersion.cmake index 5e6d7dec..8f3da73b 100644 --- a/cmake/ExtractVersion.cmake +++ b/cmake/ExtractVersion.cmake @@ -1,3 +1,24 @@ +# +# Copyright (C) 2018 CS - Systemes d'Information (CS-SI) +# +# This file is part of Sirius +# +# https://github.com/CS-SI/SIRIUS +# +# Sirius 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 3 of the License, or +# (at your option) any later version. +# +# Sirius 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 Sirius. If not, see . +# + find_package(Git QUIET) if(GIT_FOUND AND "${SIRIUS_VERSION}" STREQUAL "" AND "${SIRIUS_REVISION_COMMIT}" STREQUAL "") @@ -17,16 +38,18 @@ if(GIT_FOUND AND "${SIRIUS_VERSION}" STREQUAL "" endif() endif() -set(VERSION_REGEX "^([0-9]+)[.]([0-9]+)[.]([0-9]+)") - if ("${SIRIUS_VERSION}" STREQUAL "") - message(STATUS "No version found. Setting project version to '0.0.0'") - set(SIRIUS_VERSION "0.0.0") + # extract version from file VERSION + message(STATUS "No version found. Extract version from file 'VERSION'") + file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" SIRIUS_VERSION) endif() + set(SIRIUS_EXTENDED_VERSION "${SIRIUS_VERSION}") -string(REGEX MATCH ${VERSION_REGEX} SIRIUS_VERSION ${SIRIUS_VERSION}) + +set(VERSION_REGEX "^([0-9]+)[.]([0-9]+)[.]([0-9]+)") +string(REGEX MATCH ${VERSION_REGEX} SIRIUS_VERSION ${SIRIUS_EXTENDED_VERSION}) if ("${SIRIUS_REVISION_COMMIT}" STREQUAL "") - message(STATUS "No revision commit found. Setting project revision commit to 'sirius-no-revision-commit'") - set(SIRIUS_REVISION_COMMIT "sirius-no-revision-commit") + message(STATUS "No revision commit found. Setting project revision commit to 'unknown-revision-commit'") + set(SIRIUS_REVISION_COMMIT "unknown-revision-commit") endif() \ No newline at end of file diff --git a/cmake/FindSIRIUS.cmake b/cmake/FindSIRIUS.cmake index 7d2d1bfb..1d0662ac 100644 --- a/cmake/FindSIRIUS.cmake +++ b/cmake/FindSIRIUS.cmake @@ -30,44 +30,74 @@ # # :: # -# SIRIUS_INCLUDE_DIRS - where to find sirius.h, etc. -# SIRIUS_LIBRARIES - List of libraries when using sirius. -# SIRIUS_FOUND - True if sirius found. - -find_package(PkgConfig) +# SIRIUS_INCLUDE_DIRS - where to find sirius.h, etc. +# SIRIUS_LIBRARIES - List of libraries when using sirius. +# SIRIUS_STATIC_LIBRARIES - List of static libraries when using sirius. +# SIRIUS_FOUND - True if sirius found. find_path(SIRIUS_INCLUDE_DIR sirius/sirius.h HINTS ${SIRIUS_ROOT} - ${PC_SIRIUS_INCLUDE_DIRS} PATH_SUFFIXES include/sirius) find_library(SIRIUS_LIBRARY NAMES - libsirius + sirius # linux/os x naming (default library prefix is lib) + libsirius # windows naming (default library prefix is '') + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + lib/sirius) + +if (WIN32) + find_file(SIRIUS_DLL_LIBRARY + NAMES libsirius.dll + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + lib/sirius) +endif() + +find_library(SIRIUS_STATIC_LIBRARY + NAMES + sirius-static # linux/os x naming (default library prefix is lib) + libsirius-static # windows naming (default library prefix is '') HINTS ${SIRIUS_ROOT} - ${PC_SIRIUS_LIBRARIES} PATH_SUFFIXES lib/sirius) set(SIRIUS_LIBRARIES ${SIRIUS_LIBRARY}) +set(SIRIUS_STATIC_LIBRARIES ${SIRIUS_STATIC_LIBRARY}) set(SIRIUS_INCLUDE_DIRS ${SIRIUS_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SIRIUS DEFAULT_MSG SIRIUS_LIBRARY SIRIUS_INCLUDE_DIR) +find_package_handle_standard_args(SIRIUS DEFAULT_MSG SIRIUS_LIBRARY SIRIUS_STATIC_LIBRARY SIRIUS_INCLUDE_DIR) -mark_as_advanced(SIRIUS_INCLUDE_DIR SIRIUS_LIBRARY) +mark_as_advanced(SIRIUS_INCLUDE_DIR SIRIUS_LIBRARY SIRIUS_STATIC_LIBRARY) if (SIRIUS_FOUND) - if(NOT TARGET sirius) - add_library(sirius SHARED IMPORTED) - set_target_properties(sirius PROPERTIES + if(NOT(TARGET sirius::shared) AND NOT(TARGET sirius::static)) + add_library(sirius::shared SHARED IMPORTED GLOBAL) + set_target_properties(sirius::shared PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${SIRIUS_INCLUDE_DIRS}) + if (WIN32) + set_target_properties(sirius::shared PROPERTIES + IMPORTED_LOCATION ${SIRIUS_DLL_LIBRARY} + IMPORTED_IMPLIB ${SIRIUS_LIBRARIES}) + else() + set_target_properties(sirius::shared PROPERTIES + IMPORTED_LOCATION ${SIRIUS_LIBRARIES}) + endif() + + add_library(sirius::static STATIC IMPORTED GLOBAL) + set_target_properties(sirius::static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${SIRIUS_INCLUDE_DIRS} - IMPORTED_LOCATION ${SIRIUS_LIBRARIES}) + IMPORTED_LOCATION ${SIRIUS_STATIC_LIBRARIES}) + message(STATUS "find_package(SIRIUS) provides CMake targets: sirius::shared and sirius::static") endif() endif () diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9815fbac..cdbdaf6f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,22 +23,38 @@ cmake_minimum_required(VERSION 3.2) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius) -set(SIRIUS_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/sirius/sirius.h.in) -set(SIRIUS_CONFIG_OUT ${CMAKE_CURRENT_BINARY_DIR}/include/sirius/sirius.h) - -configure_file(${SIRIUS_CONFIG_IN} ${SIRIUS_CONFIG_OUT} @ONLY) +message(STATUS "Generating sirius/sirius.h") +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sirius/sirius.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/sirius/sirius.h + @ONLY) + +# configuration definitions that will populate sirius/config.h +if (ENABLE_LOGS) + set(SIRIUS_ENABLE_LOGS 1) +endif() +if (ENABLE_CACHE_OPTIMIZATION) + set(SIRIUS_ENABLE_CACHE_OPTIMIZATION 1) +endif() +if (ENABLE_GSL_CONTRACTS) + message(STATUS "Enable GSL contract violation policy: ON (terminate on violation)") + set(GSL_TERMINATE_ON_CONTRACT_VIOLATION 1) +else() + message(STATUS "Enable GSL contract violation policy: OFF") + set(GSL_UNENFORCED_ON_CONTRACT_VIOLATION 1) +endif() + +message(STATUS "Generating sirius/config.h") +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sirius/config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/sirius/config.h + @ONLY) # sirius shared library (use as a model for compilation options, definitions and link libraries properties of libsirius-objects and libsirius-static) # TODO: when using cmake 3.12, add all those settings into libsirius-objects only and link libsirius and libsirius-static with libsirius-objects (https://cmake.org/cmake/help/v3.12/release/3.12.html#index-0-command:target_link_libraries) add_library(libsirius SHARED $) -if (${ENABLE_LOGS}) - # build with logs - target_compile_definitions(libsirius PUBLIC SIRIUS_ENABLE_LOGS=1) -endif () -if (${ENABLE_CACHE_OPTIMIZATION}) - # build with cache - target_compile_definitions(libsirius PUBLIC SIRIUS_ENABLE_CACHE_OPTIMIZATION=1) -endif () +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + # export symbols for windows + set_target_properties(libsirius PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE) +endif() target_include_directories(libsirius PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} @@ -57,13 +73,7 @@ target_link_libraries(libsirius-static $) -target_compile_definitions(libsirius-objects PUBLIC $) -target_compile_options(libsirius-objects PUBLIC $) - -target_sources(libsirius-objects PRIVATE +add_library(libsirius-objects OBJECT EXCLUDE_FROM_ALL sirius/sirius.h sirius/exception.h @@ -115,10 +125,15 @@ target_sources(libsirius-objects PRIVATE sirius/utils/lru_cache.h sirius/utils/numeric.h sirius/utils/numeric.cc) +set_target_properties(libsirius-objects PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(libsirius-objects PUBLIC $) +target_compile_definitions(libsirius-objects PUBLIC $) +target_compile_options(libsirius-objects PUBLIC $) # copy sirius include directory into install directory -install(FILES ${SIRIUS_CONFIG_OUT} - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius/sirius) +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius + DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius + FILES_MATCHING PATTERN "*.h" PATTERN "*.txx") install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sirius DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius FILES_MATCHING PATTERN "*.h" PATTERN "*.txx") diff --git a/src/sirius/config.h.in b/src/sirius/config.h.in new file mode 100644 index 00000000..1a43d18a --- /dev/null +++ b/src/sirius/config.h.in @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#ifndef SIRIUS_CONFIG_H_ +#define SIRIUS_CONFIG_H_ + +// This file contains Sirius configuration + +// define SIRIUS_ENABLE_LOGS to enable logs +#cmakedefine SIRIUS_ENABLE_LOGS + +// define SIRIUS_ENABLE_CACHE_OPTIMIZATIONS to enable cache optimization +#cmakedefine SIRIUS_ENABLE_CACHE_OPTIMIZATION + +// define GSL_TERMINATE_ON_CONTRACT_VIOLATION to terminate execution on GSL contract violation +#cmakedefine GSL_TERMINATE_ON_CONTRACT_VIOLATION + +// define GSL_UNENFORCED_ON_CONTRACT_VIOLATION to disable GSL contract violation checking +#cmakedefine GSL_UNENFORCED_ON_CONTRACT_VIOLATION + +#endif // SIRIUS_CONFIG_H_ \ No newline at end of file diff --git a/src/sirius/fftw/fftw.cc b/src/sirius/fftw/fftw.cc index 00bdd73e..f25cc98d 100644 --- a/src/sirius/fftw/fftw.cc +++ b/src/sirius/fftw/fftw.cc @@ -25,6 +25,8 @@ #include +#include "sirius/config.h" + #include "sirius/fftw/exception.h" #include "sirius/fftw/wrapper.h" diff --git a/src/sirius/filter.cc b/src/sirius/filter.cc index 21f19b93..a998f2a0 100644 --- a/src/sirius/filter.cc +++ b/src/sirius/filter.cc @@ -23,6 +23,8 @@ #include +#include "sirius/config.h" + #include "sirius/exception.h" #include "sirius/frequency_resampler_factory.h" #include "sirius/i_frequency_resampler.h" diff --git a/src/sirius/utils/gsl.h b/src/sirius/utils/gsl.h index 660f3494..bf2e2fef 100644 --- a/src/sirius/utils/gsl.h +++ b/src/sirius/utils/gsl.h @@ -22,6 +22,8 @@ #ifndef SIRIUS_UTILS_GSL_H_ #define SIRIUS_UTILS_GSL_H_ +#include "sirius/config.h" + #include namespace sirius { diff --git a/src/sirius/utils/log.h b/src/sirius/utils/log.h index 03e94c6c..923b391e 100644 --- a/src/sirius/utils/log.h +++ b/src/sirius/utils/log.h @@ -24,6 +24,8 @@ #include +#include "sirius/config.h" + #ifdef SIRIUS_ENABLE_LOGS #include diff --git a/tests/find-package-test/CMakeLists.txt b/tests/find-package-test/CMakeLists.txt new file mode 100644 index 00000000..2e481e09 --- /dev/null +++ b/tests/find-package-test/CMakeLists.txt @@ -0,0 +1,71 @@ +# +# Copyright (C) 2018 CS - Systemes d'Information (CS-SI) +# +# This file is part of Sirius +# +# https://github.com/CS-SI/SIRIUS +# +# Sirius 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 3 of the License, or +# (at your option) any later version. +# +# Sirius 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 Sirius. If not, see . +# + +# This project aims to test find_package(SIRIUS) helper: +# * find SIRIUS shared and static libraries from Sirius install directory (set SIRIUS_ROOT variable) +# * generate small test executables and link with Sirius static and shared libraries + +cmake_minimum_required(VERSION 3.2) + +if(POLICY CMP0074) + # find_package() uses _ROOT variables: https://cmake.org/cmake/help/git-stage/policy/CMP0074.html + cmake_policy(SET CMP0074 NEW) +endif() + +project(TestFindPackage CXX) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# path to additional cmake modules +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake") + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/BuildType.cmake) + +# FFTW3 headers are required +find_package(FFTW3 REQUIRED) +if (WIN32) + add_library(fftw3 UNKNOWN IMPORTED GLOBAL) +else() + add_library(fftw3 SHARED IMPORTED GLOBAL) +endif() +set_target_properties(fftw3 PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIR} + IMPORTED_LOCATION ${FFTW3_LIBRARY}) + +# set CXX flags +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # GCC or Clang + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") + if (ENABLE_CLANG_LIBCPP) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + endif() +endif() + +find_package(SIRIUS REQUIRED) + +add_executable(basic_sirius_shared_test + basic_sirius_test.cc) +target_link_libraries(basic_sirius_shared_test sirius::shared fftw3) + +add_executable(basic_sirius_static_test + basic_sirius_test.cc) +target_link_libraries(basic_sirius_static_test sirius::static fftw3) \ No newline at end of file diff --git a/tests/find-package-test/basic_sirius_test.cc b/tests/find-package-test/basic_sirius_test.cc new file mode 100644 index 00000000..4b9a6a3d --- /dev/null +++ b/tests/find-package-test/basic_sirius_test.cc @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#include + +#include "sirius/filter.h" +#include +#include "sirius/image.h" +#include + +int main(int, char**) { + LOG_SET_LEVEL(trace); + + auto zoom_ratio_2_1 = sirius::ZoomRatio::Create(2, 1); + + auto dummy_image = sirius::Image({5, 5}); + + sirius::IFrequencyResampler::UPtr freq_resampler = + sirius::FrequencyResamplerFactory::Create( + sirius::ImageDecompositionPolicies::kRegular, + sirius::FrequencyZoomStrategies::kZeroPadding); + + sirius::Image zoomed_image_2_1 = + freq_resampler->Compute(zoom_ratio_2_1, dummy_image, {0, 0, 0, 0}); +} \ No newline at end of file diff --git a/third_party/gsl/CMakeLists.txt b/third_party/gsl/CMakeLists.txt index 40234f7c..88a320f2 100644 --- a/third_party/gsl/CMakeLists.txt +++ b/third_party/gsl/CMakeLists.txt @@ -24,10 +24,4 @@ cmake_minimum_required(VERSION 3.2) add_library(gsl INTERFACE) target_include_directories(gsl INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/gsl/include) -if (ENABLE_GSL_CONTRACTS) - message(STATUS "Enable GSL contract violation policy: ON (terminate on violation)") - target_compile_definitions(gsl INTERFACE GSL_TERMINATE_ON_CONTRACT_VIOLATION=1) -else() - message(STATUS "Enable GSL contract violation policy: OFF") - target_compile_definitions(gsl INTERFACE GSL_UNENFORCED_ON_CONTRACT_VIOLATION=1) -endif() \ No newline at end of file +target_compile_options(gsl INTERFACE $<$>:-Wno-unknown-attributes>) \ No newline at end of file From e29cfea4c702ac08bf0c558d8f920bcfe57a0d7c Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Tue, 4 Sep 2018 17:11:12 +0200 Subject: [PATCH 08/21] improve FFTW3 and SIRIUS find_package helpers --- CMakeLists.txt | 10 +- cmake/FindFFTW3.cmake | 131 ++++++++++++++++++++++--- cmake/FindSIRIUS.cmake | 33 ++++--- src/CMakeLists.txt | 2 +- tests/CMakeLists.txt | 2 +- tests/find-package-test/CMakeLists.txt | 12 +-- third_party/CMakeLists.txt | 23 ++--- 7 files changed, 154 insertions(+), 59 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1646f8fb..5f245c72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,10 +64,10 @@ message(STATUS "Enable GSL contracts: ${ENABLE_GSL_CONTRACTS}") message(STATUS "Enable documentation: ${ENABLE_DOCUMENTATION}") message(STATUS "Enable unit tests: ${ENABLE_UNIT_TESTS}") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - message(STATUS "Enable windows static runtime: ${ENABLE_WINDOWS_STATIC_RUNTIME}") + message(STATUS "Enable windows static runtime: ${ENABLE_WINDOWS_STATIC_RUNTIME}") endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - message(STATUS "Force use of libc++: ${ENABLE_CLANG_LIBCPP}") + message(STATUS "Force use of libc++: ${ENABLE_CLANG_LIBCPP}") endif() set(CMAKE_CXX_STANDARD 14) @@ -98,14 +98,14 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") endforeach(flag_var) endif() endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIRIUS_CXX_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIRIUS_CXX_FLAGS}") find_package(Threads) add_subdirectory(third_party) add_subdirectory(src) -if (${ENABLE_UNIT_TESTS}) +if (ENABLE_UNIT_TESTS) enable_testing() add_subdirectory(tests) @@ -116,7 +116,7 @@ if (${ENABLE_UNIT_TESTS}) file(COPY data/filters DESTINATION tests) endif() -if (${ENABLE_DOCUMENTATION}) +if (ENABLE_DOCUMENTATION) find_package(Doxygen) if(DOXYGEN_FOUND) set(DOXYFILE_CONFIG_IN ${CMAKE_CURRENT_SOURCE_DIR}/doc/doxygen/doxyfile.in) diff --git a/cmake/FindFFTW3.cmake b/cmake/FindFFTW3.cmake index 27b3b880..9188083b 100644 --- a/cmake/FindFFTW3.cmake +++ b/cmake/FindFFTW3.cmake @@ -23,7 +23,7 @@ # FindFFTW3 # --------- # -# Find fftw3 +# Find fftw3 library # # Find the native FFTW3 headers and libraries (double precision). # @@ -32,12 +32,16 @@ # FFTW3_INCLUDE_DIRS - where to find fftw3.h, etc. # FFTW3_LIBRARIES - List of libraries when using fftw3. # FFTW3_FOUND - True if fftw3 found. - -# this implementation is inspired by +# +# if fftw3 library is found (mandatory), this helper provides CMake target FFTW3::fftw3 +# if fftw3f library is found (optional), this helper provides variable FFTW3F_LIBRARIES and CMake target FFTW3::fftw3f +# if fftw3l library is found (optional), this helper provides variable FFTW3L_LIBRARIES and CMake target FFTW3::fftw3l +# if fftw3q library is found (optional), this helper provides variable FFTW3Q_LIBRARIES and CMake target FFTW3::fftw3Q +# +# this implementation is slightly inspired by # https://github.com/InsightSoftwareConsortium/ITK/blob/master/CMake/FindFFTW.cmake -find_package(PkgConfig) - +find_package(PkgConfig QUIET) pkg_check_modules(PC_FFTW3 fftw3) set(FFTW_LIB_SEARCHPATH @@ -49,18 +53,121 @@ if (${FFTW_INCLUDE_PATH}) get_filename_component(FFTW_INSTALL_BASE_PATH ${FFTW_INCLUDE_PATH} PATH) list(APPEND FFTW_LIB_SEARCHPATH ${FFTW_INSTALL_BASE_PATH}/lib - ${FFTW_INSTALL_BASE_PATH}/lib64) + ${FFTW_INSTALL_BASE_PATH}/lib64 + ${FFTW_INSTALL_BASE_PATH}/bin) endif() -find_path(FFTW3_INCLUDE_DIR fftw3.h HINTS ${FFTW_LIB_SEARCHPATH}) -find_library(FFTW3_LIBRARY NAMES fftw3 HINTS ${FFTW_LIB_SEARCHPATH} ) +find_path(FFTW3_INCLUDE_DIR + NAMES fftw3.h + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3_LIBRARY + NAMES fftw3 + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3F_LIBRARY + NAMES fftw3f + HINTS ${FFTW_LIB_SEARCHPATH}) + +find_library(FFTW3L_LIBRARY + NAMES fftw3l + HINTS ${FFTW_LIB_SEARCHPATH}) -set(FFTW3_LIBRARIES ${FFTW3_LIBRARY} ) -set(FFTW3_INCLUDE_DIRS ${FFTW3_INCLUDE_DIR} ) +find_library(FFTW3Q_LIBRARY + NAMES fftw3q + HINTS ${FFTW_LIB_SEARCHPATH}) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + find_file(FFTW3_DLL_LIBRARY + NAMES fftw3.dll + HINTS ${FFTW_LIB_SEARCHPATH}) + find_file(FFTW3F_DLL_LIBRARY + NAMES fftw3f.dll + HINTS ${FFTW_LIB_SEARCHPATH}) + find_file(FFTW3L_DLL_LIBRARY + NAMES fftw3l.dll + HINTS ${FFTW_LIB_SEARCHPATH}) + find_file(FFTW3Q_DLL_LIBRARY + NAMES fftw3q.dll + HINTS ${FFTW_LIB_SEARCHPATH}) +endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(FFTW3 DEFAULT_MSG FFTW3_LIBRARY FFTW3_INCLUDE_DIR ) +find_package_handle_standard_args(FFTW3 DEFAULT_MSG FFTW3_LIBRARY FFTW3_INCLUDE_DIR) + +mark_as_advanced(FFTW3_INCLUDE_DIR FFTW3_LIBRARY) + +if (FFTW3_FOUND) + set(FFTW3_LIBRARIES ${FFTW3_LIBRARY}) + set(FFTW3_INCLUDE_DIRS ${FFTW3_INCLUDE_DIR}) + if (NOT TARGET FFTW3::fftw3) + # double precision + add_library(FFTW3::fftw3 SHARED IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3 PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS}) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(FFTW3::fftw3 PROPERTIES + IMPORTED_LOCATION ${FFTW3_DLL_LIBRARY} + IMPORTED_IMPLIB ${FFTW3_LIBRARIES}) + else() + set_target_properties(FFTW3::fftw3 PROPERTIES + IMPORTED_LOCATION ${FFTW3_LIBRARIES}) + endif() + list(APPEND FFTW3_TARGETS "FFTW3::fftw3") + endif() -mark_as_advanced(FFTW3_INCLUDE_DIR FFTW3_LIBRARY ) + if (FFTW3F_LIBRARY AND NOT(TARGET FFTW3::fftw3f)) + # float precision + set(FFTW3F_LIBRARIES ${FFTW3F_LIBRARY}) + add_library(FFTW3::fftw3f SHARED IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3f PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS}) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(FFTW3::fftw3f PROPERTIES + IMPORTED_LOCATION ${FFTW3F_DLL_LIBRARY} + IMPORTED_IMPLIB ${FFTW3F_LIBRARIES}) + else() + set_target_properties(FFTW3::fftw3f PROPERTIES + IMPORTED_LOCATION ${FFTW3F_LIBRARIES}) + endif() + list(APPEND FFTW3_TARGETS "FFTW3::fftw3f") + endif() + + if (FFTW3L_LIBRARY AND NOT(TARGET FFTW3::fftw3l)) + # long double precision + set(FFTW3L_LIBRARIES ${FFTW3F_LIBRARY}) + add_library(FFTW3::fftw3l SHARED IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3l PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS}) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(FFTW3::fftw3l PROPERTIES + IMPORTED_LOCATION ${FFTW3L_DLL_LIBRARY} + IMPORTED_IMPLIB ${FFTW3L_LIBRARIES}) + else() + set_target_properties(FFTW3::fftw3l PROPERTIES + IMPORTED_LOCATION ${FFTW3L_LIBRARIES}) + endif() + list(APPEND FFTW3_TARGETS "FFTW3::fftw3l") + endif() + + if (FFTW3Q_LIBRARY AND NOT(TARGET FFTW3::fftw3q)) + # quad precision + set(FFTW3Q_LIBRARIES ${FFTW3F_LIBRARY}) + add_library(FFTW3::fftw3q SHARED IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3q PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS}) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(FFTW3::fftw3q PROPERTIES + IMPORTED_LOCATION ${FFTW3Q_DLL_LIBRARY} + IMPORTED_IMPLIB ${FFTW3Q_LIBRARIES}) + else() + set_target_properties(FFTW3::fftw3q PROPERTIES + IMPORTED_LOCATION ${FFTW3Q_LIBRARIES}) + endif() + list(APPEND FFTW3_TARGETS "FFTW3::fftw3q") + endif() +endif() +string(REPLACE ";" ", " FFTW3_TARGETS_STR "${FFTW3_TARGETS}") +message(STATUS "find_package(FFTW3) provides CMake targets: ${FFTW3_TARGETS_STR}") diff --git a/cmake/FindSIRIUS.cmake b/cmake/FindSIRIUS.cmake index 1d0662ac..c84895dd 100644 --- a/cmake/FindSIRIUS.cmake +++ b/cmake/FindSIRIUS.cmake @@ -34,6 +34,9 @@ # SIRIUS_LIBRARIES - List of libraries when using sirius. # SIRIUS_STATIC_LIBRARIES - List of static libraries when using sirius. # SIRIUS_FOUND - True if sirius found. +# +# If sirius library is found, this helper provides CMake targets: SIRIUS::libsirius and SIRIUS::libsirius-static +# find_path(SIRIUS_INCLUDE_DIR sirius/sirius.h @@ -51,7 +54,7 @@ find_library(SIRIUS_LIBRARY PATH_SUFFIXES lib/sirius) -if (WIN32) +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") find_file(SIRIUS_DLL_LIBRARY NAMES libsirius.dll HINTS @@ -69,10 +72,6 @@ find_library(SIRIUS_STATIC_LIBRARY PATH_SUFFIXES lib/sirius) -set(SIRIUS_LIBRARIES ${SIRIUS_LIBRARY}) -set(SIRIUS_STATIC_LIBRARIES ${SIRIUS_STATIC_LIBRARY}) -set(SIRIUS_INCLUDE_DIRS ${SIRIUS_INCLUDE_DIR}) - include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SIRIUS DEFAULT_MSG SIRIUS_LIBRARY SIRIUS_STATIC_LIBRARY SIRIUS_INCLUDE_DIR) @@ -80,24 +79,28 @@ find_package_handle_standard_args(SIRIUS DEFAULT_MSG SIRIUS_LIBRARY SIRIUS_STATI mark_as_advanced(SIRIUS_INCLUDE_DIR SIRIUS_LIBRARY SIRIUS_STATIC_LIBRARY) if (SIRIUS_FOUND) - if(NOT(TARGET sirius::shared) AND NOT(TARGET sirius::static)) - add_library(sirius::shared SHARED IMPORTED GLOBAL) - set_target_properties(sirius::shared PROPERTIES + set(SIRIUS_LIBRARIES ${SIRIUS_LIBRARY}) + set(SIRIUS_STATIC_LIBRARIES ${SIRIUS_STATIC_LIBRARY}) + set(SIRIUS_INCLUDE_DIRS ${SIRIUS_INCLUDE_DIR}) + if(NOT TARGET SIRIUS::libsirius) + add_library(SIRIUS::libsirius SHARED IMPORTED GLOBAL) + set_target_properties(SIRIUS::libsirius PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${SIRIUS_INCLUDE_DIRS}) - if (WIN32) - set_target_properties(sirius::shared PROPERTIES + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(SIRIUS::libsirius PROPERTIES IMPORTED_LOCATION ${SIRIUS_DLL_LIBRARY} IMPORTED_IMPLIB ${SIRIUS_LIBRARIES}) else() - set_target_properties(sirius::shared PROPERTIES + set_target_properties(SIRIUS::libsirius PROPERTIES IMPORTED_LOCATION ${SIRIUS_LIBRARIES}) endif() - - add_library(sirius::static STATIC IMPORTED GLOBAL) - set_target_properties(sirius::static PROPERTIES + endif() + if (NOT TARGET SIRIUS::libsirius-static) + add_library(SIRIUS::libsirius-static STATIC IMPORTED GLOBAL) + set_target_properties(SIRIUS::libsirius-static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${SIRIUS_INCLUDE_DIRS} IMPORTED_LOCATION ${SIRIUS_STATIC_LIBRARIES}) - message(STATUS "find_package(SIRIUS) provides CMake targets: sirius::shared and sirius::static") endif() + message(STATUS "find_package(SIRIUS) provides CMake targets: SIRIUS::libsirius and SIRIUS::libsirius-static") endif () diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cdbdaf6f..ee0543a2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,7 +60,7 @@ target_include_directories(libsirius PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/include) set_target_properties(libsirius PROPERTIES PREFIX "") -target_link_libraries(libsirius PUBLIC fftw3 spdlog gsl) +target_link_libraries(libsirius PUBLIC FFTW3::fftw3 spdlog gsl) install(TARGETS libsirius DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sirius) # sirius static library diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 61570240..71b48b9f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -36,7 +36,7 @@ target_link_libraries(unit_test_lib file(GLOB unit_tests *_tests.cc) -# group test targets into one to easify build +# group test targets into one to make build easier add_custom_target(build_tests) # create unit test target from test file diff --git a/tests/find-package-test/CMakeLists.txt b/tests/find-package-test/CMakeLists.txt index 2e481e09..23719460 100644 --- a/tests/find-package-test/CMakeLists.txt +++ b/tests/find-package-test/CMakeLists.txt @@ -42,14 +42,6 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/BuildType.cmake) # FFTW3 headers are required find_package(FFTW3 REQUIRED) -if (WIN32) - add_library(fftw3 UNKNOWN IMPORTED GLOBAL) -else() - add_library(fftw3 SHARED IMPORTED GLOBAL) -endif() -set_target_properties(fftw3 PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIR} - IMPORTED_LOCATION ${FFTW3_LIBRARY}) # set CXX flags if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") @@ -64,8 +56,8 @@ find_package(SIRIUS REQUIRED) add_executable(basic_sirius_shared_test basic_sirius_test.cc) -target_link_libraries(basic_sirius_shared_test sirius::shared fftw3) +target_link_libraries(basic_sirius_shared_test SIRIUS::libsirius FFTW3::fftw3) add_executable(basic_sirius_static_test basic_sirius_test.cc) -target_link_libraries(basic_sirius_static_test sirius::static fftw3) \ No newline at end of file +target_link_libraries(basic_sirius_static_test SIRIUS::libsirius-static FFTW3::fftw3) \ No newline at end of file diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 3c1a2ee2..0329fd6b 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -24,30 +24,23 @@ cmake_minimum_required(VERSION 3.2) # create library from package dependencies find_package(FFTW3 REQUIRED) -if (WIN32) - add_library(fftw3 UNKNOWN IMPORTED GLOBAL) -else() - add_library(fftw3 SHARED IMPORTED GLOBAL) -endif() -set_target_properties(fftw3 PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIR} - IMPORTED_LOCATION ${FFTW3_LIBRARY}) - add_subdirectory(spdlog) add_subdirectory(gsl) if (ENABLE_SIRIUS_EXECUTABLE OR ENABLE_UNIT_TESTS) find_package(GDAL REQUIRED) - if (WIN32) - add_library(gdal UNKNOWN IMPORTED GLOBAL) - else() - add_library(gdal SHARED IMPORTED GLOBAL) - endif() + add_library(gdal SHARED IMPORTED GLOBAL) set_target_properties(gdal PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${GDAL_INCLUDE_DIR} - IMPORTED_LOCATION ${GDAL_LIBRARY} INTERFACE_COMPILE_DEFINITIONS CPL_LOG=${ENABLE_LOGS}) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(gdal PROPERTIES + IMPORTED_IMPLIB ${GDAL_LIBRARY}) + else() + set_target_properties(gdal PROPERTIES + IMPORTED_LOCATION ${GDAL_LIBRARY}) + endif() endif () if (ENABLE_SIRIUS_EXECUTABLE) From 8aa25118dadeb8f5c3b7129dc073586cdb7b7888 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Wed, 5 Sep 2018 16:40:18 +0200 Subject: [PATCH 09/21] add USE_CXX_STATIC_RUNTIME CMake option and rename ENABLE_UNIT_TESTS CMake option to BUILD_TESTING * test on appveyor * test on travis (linux - GCC and Clang) --- .appveyor.yml | 11 ++- .travis.yml | 47 +++++++++ .travis/docker/build_install_sirius.sh | 2 +- .travis/linux/build.sh | 5 +- .travis/osx/build.sh | 2 +- CMakeLists.txt | 29 +++--- README.md | 8 +- cmake/FindFFTW3.cmake | 129 ++++++++++++++----------- src/CMakeLists.txt | 2 +- third_party/CMakeLists.txt | 4 +- 10 files changed, 160 insertions(+), 79 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index f14e0c19..be9ffa33 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -38,10 +38,14 @@ before_build: - cd %APPVEYOR_BUILD_FOLDER% - git fetch --tags - mkdir %APPVEYOR_BUILD_FOLDER%\.build + - mkdir %APPVEYOR_BUILD_FOLDER%\.build-static-runtime - mkdir %APPVEYOR_BUILD_FOLDER%\.install-directory - - cd .build + - cd %APPVEYOR_BUILD_FOLDER%/.build - appveyor AddMessage "Creating Sirius Visual Studio solution..." - - cmake .. -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/.install-directory -DENABLE_CACHE_OPTIMIZATION=ON -DENABLE_GSL_CONTRACTS=ON -DENABLE_LOGS=ON -DENABLE_UNIT_TESTS=ON -DENABLE_DOCUMENTATION=OFF + - cmake .. -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/.install-directory -DENABLE_CACHE_OPTIMIZATION=ON -DENABLE_GSL_CONTRACTS=ON -DENABLE_LOGS=ON -DBUILD_TESTING=ON -DENABLE_DOCUMENTATION=OFF + - cd %APPVEYOR_BUILD_FOLDER%/.build-static-runtime + - appveyor AddMessage "Creating Sirius Visual Studio solution (static runtime)..." + - cmake .. -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/.install-directory -DENABLE_CACHE_OPTIMIZATION=ON -DENABLE_GSL_CONTRACTS=ON -DENABLE_LOGS=ON -DBUILD_TESTING=ON -DENABLE_DOCUMENTATION=OFF -DUSE_CXX_STATIC_RUNTIME=ON build: Script @@ -57,6 +61,9 @@ build_script: - cmake --build . --target sirius --config %configuration% -- /m - appveyor AddMessage "Installing sirius..." - cmake --build . --target install --config %configuration% -- /m + - cd %APPVEYOR_BUILD_FOLDER%/.build-static-runtime + - appveyor AddMessage "Compiling sirius (static runtime)..." + - cmake --build . --target sirius test_script: - cd %APPVEYOR_BUILD_FOLDER%/.build diff --git a/.travis.yml b/.travis.yml index 7c6f8568..c4abe01e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,6 +72,19 @@ matrix: - libfftw3-dev - libgdal-dev + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 GCC 7 - C++ static runtime" + env: CXX_COMPILER=/usr/bin/g++-7 USE_CXX_STATIC_RUNTIME=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - g++-7 + - libfftw3-dev + - libgdal-dev + - <<: *ubuntu-trusty-template name: "Ubuntu Trusty 14.04 Clang 4.0 - libc++" env: CXX_COMPILER=/usr/bin/clang++-4.0 ENABLE_CLANG_LIBCPP=ON @@ -121,6 +134,23 @@ matrix: - libfftw3-dev - libgdal-dev + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 6.0 - libc++ - C++ static runtime" + env: CXX_COMPILER=/usr/bin/clang++-6.0 ENABLE_CLANG_LIBCPP=ON USE_CXX_STATIC_RUNTIME=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-6.0 + - libc++-dev + - libfftw3-dev + - libgdal-dev + - <<: *ubuntu-trusty-template name: "Ubuntu Trusty 14.04 Clang 4.0 - stdlibc++ 5" env: CXX_COMPILER=/usr/bin/clang++-4.0 ENABLE_CLANG_LIBCPP=OFF @@ -170,6 +200,23 @@ matrix: - libfftw3-dev - libgdal-dev + - <<: *ubuntu-trusty-template + name: "Ubuntu Trusty 14.04 Clang 6.0 - stdlibc++ 7 - C++ static runtime" + env: CXX_COMPILER=/usr/bin/clang++-6.0 ENABLE_CLANG_LIBCPP=OFF USE_CXX_STATIC_RUNTIME=ON + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-6.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + - sourceline: 'ppa:ubuntugis/ubuntugis-unstable' + packages: + - clang-6.0 + - libstdc++-7-dev + - libfftw3-dev + - libgdal-dev + # Docker - ¢os-7-template name: "CentOS Docker" diff --git a/.travis/docker/build_install_sirius.sh b/.travis/docker/build_install_sirius.sh index e147a770..dca69583 100755 --- a/.travis/docker/build_install_sirius.sh +++ b/.travis/docker/build_install_sirius.sh @@ -31,7 +31,7 @@ cmake3 ${PROJECT_DIR} \ -DENABLE_CACHE_OPTIMIZATION=ON \ -DENABLE_GSL_CONTRACTS=OFF \ -DENABLE_LOGS=ON \ - -DENABLE_UNIT_TESTS=ON \ + -DBUILD_TESTING=ON \ -DENABLE_DOCUMENTATION=ON # build sirius shared library diff --git a/.travis/linux/build.sh b/.travis/linux/build.sh index d1965195..672d3f67 100755 --- a/.travis/linux/build.sh +++ b/.travis/linux/build.sh @@ -32,9 +32,10 @@ cmake ${PROJECT_DIR} \ -DENABLE_CACHE_OPTIMIZATION=ON \ -DENABLE_GSL_CONTRACTS=ON \ -DENABLE_LOGS=ON \ - -DENABLE_UNIT_TESTS=ON \ + -DBUILD_TESTING=ON \ -DENABLE_DOCUMENTATION=OFF \ - -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} + -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} \ + -DUSE_CXX_STATIC_RUNTIME=${USE_CXX_STATIC_RUNTIME} # build sirius shared library make -j4 libsirius diff --git a/.travis/osx/build.sh b/.travis/osx/build.sh index c03cd1f5..91be1921 100755 --- a/.travis/osx/build.sh +++ b/.travis/osx/build.sh @@ -30,7 +30,7 @@ cmake ${PROJECT_DIR} \ -DENABLE_CACHE_OPTIMIZATION=ON \ -DENABLE_GSL_CONTRACTS=ON \ -DENABLE_LOGS=ON \ - -DENABLE_UNIT_TESTS=ON \ + -DBUILD_TESTING=ON \ -DENABLE_DOCUMENTATION=OFF # build sirius shared library diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f245c72..c9e7b306 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,13 +39,11 @@ option(ENABLE_CACHE_OPTIMIZATION "Enable cache optimization (FFTW plan, Filter F option(ENABLE_LOGS "Enable logs" ON) option(ENABLE_GSL_CONTRACTS "Enable GSL contracts" OFF) option(ENABLE_DOCUMENTATION "Enable documentation generation" OFF) -option(ENABLE_UNIT_TESTS "Enable unit test targets" OFF) +option(BUILD_TESTING "Enable unit tests" OFF) +option(USE_CXX_STATIC_RUNTIME "Build targets with C++ static runtime library" OFF) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - option(ENABLE_WINDOWS_STATIC_RUNTIME "Build libraries and executables with static runtime" OFF) -endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - option(ENABLE_CLANG_LIBCPP "Force use of libc++" OFF) + option(ENABLE_CLANG_LIBCPP "Force use of libc++" OFF) endif() if (CMAKE_BUILD_TYPE STREQUAL Debug) @@ -62,19 +60,19 @@ message(STATUS "Enable cache: ${ENABLE_CACHE_OPTIMIZATION}") message(STATUS "Enable logs: ${ENABLE_LOGS}") message(STATUS "Enable GSL contracts: ${ENABLE_GSL_CONTRACTS}") message(STATUS "Enable documentation: ${ENABLE_DOCUMENTATION}") -message(STATUS "Enable unit tests: ${ENABLE_UNIT_TESTS}") -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - message(STATUS "Enable windows static runtime: ${ENABLE_WINDOWS_STATIC_RUNTIME}") -endif() +message(STATUS "Enable unit tests: ${BUILD_TESTING}") +message(STATUS "Link Sirius binaries with static C++ runtime library: ${USE_CXX_STATIC_RUNTIME}") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") message(STATUS "Force use of libc++: ${ENABLE_CLANG_LIBCPP}") endif() set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) -# set CXX flags +# set CXX global flags set(SIRIUS_CXX_FLAGS "") + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # GCC or Clang set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} -Wall -Wextra") @@ -87,7 +85,13 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} /D_USE_MATH_DEFINES") # disable 4251 (from gdal): 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} /wd4251") - if (ENABLE_WINDOWS_STATIC_RUNTIME) +endif() + +if (USE_CXX_STATIC_RUNTIME) + # build with C++ static runtime flags + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") + elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # extracted from https://gitlab.kitware.com/cmake/community/wikis/FAQ#dynamic-replace foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE @@ -98,6 +102,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") endforeach(flag_var) endif() endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIRIUS_CXX_FLAGS}") find_package(Threads) @@ -105,7 +110,7 @@ find_package(Threads) add_subdirectory(third_party) add_subdirectory(src) -if (ENABLE_UNIT_TESTS) +if (BUILD_TESTING) enable_testing() add_subdirectory(tests) diff --git a/README.md b/README.md index b1c5c32f..a12a8a85 100644 --- a/README.md +++ b/README.md @@ -97,9 +97,9 @@ Sirius is using [CMake] to build its libraries and executables. * `ENABLE_CACHE_OPTIMIZATION`: set to `ON` to build with cache optimization for FFTW and Filter * `ENABLE_GSL_CONTRACTS`: set to `ON` to build with GSL contracts (e.g. bounds checking). This option should be `OFF` on release mode. * `ENABLE_LOGS`: set to `ON` if you want to build Sirius with the logs -* `ENABLE_UNIT_TESTS`: set to `ON` if you want to build the unit tests +* `BUILD_TESTING`: set to `ON` if you want to build the unit tests * `ENABLE_DOCUMENTATION`: set to `ON` if you want to build the documentation -* `ENABLE_WINDOWS_STATIC_RUNTIME`: set to `ON` to statically link Sirius binaries with Windows runtime (Windows only) +* `USE_CXX_STATIC_RUNTIME`: set to `ON` to statically link Sirius binaries with C++ runtime library Sirius version can be extracted from `git describe --tags` and revision commit from `git rev-parse HEAD`. If version and revision commit are not provided, [CMake] will try to extract them with the latter git commands. @@ -116,7 +116,7 @@ cmake .. -DCMAKE_BUILD_TYPE=Release \ -DENABLE_CACHE_OPTIMIZATION=ON \ -DENABLE_GSL_CONTRACTS=OFF \ -DENABLE_LOGS=ON \ - -DENABLE_UNIT_TESTS=OFF \ + -DBUILD_TESTING=OFF \ -DENABLE_DOCUMENTATION=ON cmake --build . --target sirius cmake --build . --target doc @@ -156,7 +156,7 @@ cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows ^ -DENABLE_CACHE_OPTIMIZATION=ON ^ -DENABLE_GSL_CONTRACTS=OFF ^ -DENABLE_LOGS=ON ^ - -DENABLE_UNIT_TESTS=OFF ^ + -DBUILD_TESTING=OFF ^ -DENABLE_DOCUMENTATION=OFF cmake --build . --target sirius --config Release ``` diff --git a/cmake/FindFFTW3.cmake b/cmake/FindFFTW3.cmake index 9188083b..6575dd1d 100644 --- a/cmake/FindFFTW3.cmake +++ b/cmake/FindFFTW3.cmake @@ -35,8 +35,11 @@ # # if fftw3 library is found (mandatory), this helper provides CMake target FFTW3::fftw3 # if fftw3f library is found (optional), this helper provides variable FFTW3F_LIBRARIES and CMake target FFTW3::fftw3f +# if fftw3f static library is found (optional), this helper provides variable FFTW3F_STATIC_LIBRARIES and CMake target FFTW3::fftw3f-static # if fftw3l library is found (optional), this helper provides variable FFTW3L_LIBRARIES and CMake target FFTW3::fftw3l +# if fftw3l static library is found (optional), this helper provides variable FFTW3L_STATIC_LIBRARIES and CMake target FFTW3::fftw3l-static # if fftw3q library is found (optional), this helper provides variable FFTW3Q_LIBRARIES and CMake target FFTW3::fftw3Q +# if fftw3q static library is found (optional), this helper provides variable FFTW3Q_STATIC_LIBRARIES and CMake target FFTW3::fftw3Q-static # # this implementation is slightly inspired by # https://github.com/InsightSoftwareConsortium/ITK/blob/master/CMake/FindFFTW.cmake @@ -65,32 +68,33 @@ find_library(FFTW3_LIBRARY NAMES fftw3 HINTS ${FFTW_LIB_SEARCHPATH}) +find_library(FFTW3_STATIC_LIBRARY + NAMES libfftw3.a + HINTS ${FFTW_LIB_SEARCHPATH}) + find_library(FFTW3F_LIBRARY NAMES fftw3f HINTS ${FFTW_LIB_SEARCHPATH}) +find_library(FFTW3F_STATIC_LIBRARY + NAMES libfftw3f.a + HINTS ${FFTW_LIB_SEARCHPATH}) + find_library(FFTW3L_LIBRARY NAMES fftw3l HINTS ${FFTW_LIB_SEARCHPATH}) +find_library(FFTW3L_STATIC_LIBRARY + NAMES libfftw3l.a + HINTS ${FFTW_LIB_SEARCHPATH}) + find_library(FFTW3Q_LIBRARY NAMES fftw3q HINTS ${FFTW_LIB_SEARCHPATH}) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - find_file(FFTW3_DLL_LIBRARY - NAMES fftw3.dll - HINTS ${FFTW_LIB_SEARCHPATH}) - find_file(FFTW3F_DLL_LIBRARY - NAMES fftw3f.dll - HINTS ${FFTW_LIB_SEARCHPATH}) - find_file(FFTW3L_DLL_LIBRARY - NAMES fftw3l.dll - HINTS ${FFTW_LIB_SEARCHPATH}) - find_file(FFTW3Q_DLL_LIBRARY - NAMES fftw3q.dll - HINTS ${FFTW_LIB_SEARCHPATH}) -endif() +find_library(FFTW3Q_STATIC_LIBRARY + NAMES libfftw3q.a + HINTS ${FFTW_LIB_SEARCHPATH}) include(FindPackageHandleStandardArgs) @@ -98,75 +102,92 @@ find_package_handle_standard_args(FFTW3 DEFAULT_MSG FFTW3_LIBRARY FFTW3_INCLUDE_ mark_as_advanced(FFTW3_INCLUDE_DIR FFTW3_LIBRARY) +set(_FFTW3_LIBRARY_TYPE SHARED) +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set(_FFTW3_LIBRARY_TYPE UNKNOWN) +endif() + if (FFTW3_FOUND) set(FFTW3_LIBRARIES ${FFTW3_LIBRARY}) set(FFTW3_INCLUDE_DIRS ${FFTW3_INCLUDE_DIR}) if (NOT TARGET FFTW3::fftw3) # double precision - add_library(FFTW3::fftw3 SHARED IMPORTED GLOBAL) + add_library(FFTW3::fftw3 ${_FFTW3_LIBRARY_TYPE} IMPORTED GLOBAL) set_target_properties(FFTW3::fftw3 PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS}) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set_target_properties(FFTW3::fftw3 PROPERTIES - IMPORTED_LOCATION ${FFTW3_DLL_LIBRARY} - IMPORTED_IMPLIB ${FFTW3_LIBRARIES}) - else() - set_target_properties(FFTW3::fftw3 PROPERTIES - IMPORTED_LOCATION ${FFTW3_LIBRARIES}) - endif() + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3_LIBRARIES}) list(APPEND FFTW3_TARGETS "FFTW3::fftw3") endif() + if (FFTW3_STATIC_LIBRARY AND NOT(TARGET FFTW3::fftw3-static)) + # double precision + set(FFTW3_STATIC_LIBRARIES ${FFTW3_STATIC_LIBRARY}) + add_library(FFTW3::fftw3-static STATIC IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3-static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3_STATIC_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3-static") + endif() + if (FFTW3F_LIBRARY AND NOT(TARGET FFTW3::fftw3f)) # float precision set(FFTW3F_LIBRARIES ${FFTW3F_LIBRARY}) - add_library(FFTW3::fftw3f SHARED IMPORTED GLOBAL) + add_library(FFTW3::fftw3f ${_FFTW3_LIBRARY_TYPE} IMPORTED GLOBAL) set_target_properties(FFTW3::fftw3f PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS}) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set_target_properties(FFTW3::fftw3f PROPERTIES - IMPORTED_LOCATION ${FFTW3F_DLL_LIBRARY} - IMPORTED_IMPLIB ${FFTW3F_LIBRARIES}) - else() - set_target_properties(FFTW3::fftw3f PROPERTIES - IMPORTED_LOCATION ${FFTW3F_LIBRARIES}) - endif() + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3F_LIBRARIES}) list(APPEND FFTW3_TARGETS "FFTW3::fftw3f") endif() + if (FFTW3F_STATIC_LIBRARY AND NOT(TARGET FFTW3::fftw3f-static)) + # float precision + set(FFTW3F_STATIC_LIBRARIES ${FFTW3F_STATIC_LIBRARY}) + add_library(FFTW3::fftw3f-static STATIC IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3f-static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3F_STATIC_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3f-static") + endif() + if (FFTW3L_LIBRARY AND NOT(TARGET FFTW3::fftw3l)) # long double precision set(FFTW3L_LIBRARIES ${FFTW3F_LIBRARY}) - add_library(FFTW3::fftw3l SHARED IMPORTED GLOBAL) + add_library(FFTW3::fftw3l ${_FFTW3_LIBRARY_TYPE} IMPORTED GLOBAL) set_target_properties(FFTW3::fftw3l PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS}) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set_target_properties(FFTW3::fftw3l PROPERTIES - IMPORTED_LOCATION ${FFTW3L_DLL_LIBRARY} - IMPORTED_IMPLIB ${FFTW3L_LIBRARIES}) - else() - set_target_properties(FFTW3::fftw3l PROPERTIES - IMPORTED_LOCATION ${FFTW3L_LIBRARIES}) - endif() + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3L_LIBRARIES}) list(APPEND FFTW3_TARGETS "FFTW3::fftw3l") endif() + if (FFTW3L_STATIC_LIBRARY AND NOT(TARGET FFTW3::fftw3l-static)) + # long double precision + set(FFTW3L_STATIC_LIBRARIES ${FFTW3L_STATIC_LIBRARY}) + add_library(FFTW3::fftw3l-static STATIC IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3l-static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3L_STATIC_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3l-static") + endif() + if (FFTW3Q_LIBRARY AND NOT(TARGET FFTW3::fftw3q)) # quad precision set(FFTW3Q_LIBRARIES ${FFTW3F_LIBRARY}) - add_library(FFTW3::fftw3q SHARED IMPORTED GLOBAL) + add_library(FFTW3::fftw3q ${_FFTW3_LIBRARY_TYPE} IMPORTED GLOBAL) set_target_properties(FFTW3::fftw3q PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS}) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set_target_properties(FFTW3::fftw3q PROPERTIES - IMPORTED_LOCATION ${FFTW3Q_DLL_LIBRARY} - IMPORTED_IMPLIB ${FFTW3Q_LIBRARIES}) - else() - set_target_properties(FFTW3::fftw3q PROPERTIES - IMPORTED_LOCATION ${FFTW3Q_LIBRARIES}) - endif() + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3Q_LIBRARIES}) list(APPEND FFTW3_TARGETS "FFTW3::fftw3q") endif() + + if (FFTW3Q_STATIC_LIBRARY AND NOT(TARGET FFTW3::fftw3q-static)) + # quad precision + set(FFTW3Q_STATIC_LIBRARIES ${FFTW3Q_STATIC_LIBRARY}) + add_library(FFTW3::fftw3q-static STATIC IMPORTED GLOBAL) + set_target_properties(FFTW3::fftw3q-static PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${FFTW3_INCLUDE_DIRS} + IMPORTED_LOCATION ${FFTW3Q_STATIC_LIBRARIES}) + list(APPEND FFTW3_TARGETS "FFTW3::fftw3q-static") + endif() endif() string(REPLACE ";" ", " FFTW3_TARGETS_STR "${FFTW3_TARGETS}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee0543a2..0a011e9b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -146,7 +146,7 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/spdlog/spdlog/inclu install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/gsl/gsl/include/gsl DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius) -if (ENABLE_SIRIUS_EXECUTABLE OR ENABLE_UNIT_TESTS) +if (ENABLE_SIRIUS_EXECUTABLE OR BUILD_TESTING) add_library(libsirius-gdal-static STATIC EXCLUDE_FROM_ALL # gdal sirius/gdal/debug.h diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 0329fd6b..c4ee4783 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -27,7 +27,7 @@ find_package(FFTW3 REQUIRED) add_subdirectory(spdlog) add_subdirectory(gsl) -if (ENABLE_SIRIUS_EXECUTABLE OR ENABLE_UNIT_TESTS) +if (ENABLE_SIRIUS_EXECUTABLE OR BUILD_TESTING) find_package(GDAL REQUIRED) add_library(gdal SHARED IMPORTED GLOBAL) @@ -47,6 +47,6 @@ if (ENABLE_SIRIUS_EXECUTABLE) add_subdirectory(cxxopts) endif() -if (ENABLE_UNIT_TESTS) +if (BUILD_TESTING) add_subdirectory(catch) endif() From 685e3f1ae11561a91d7849dd0faeac8a3915dd51 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Thu, 6 Sep 2018 11:02:08 +0200 Subject: [PATCH 10/21] update install directory structure * move runtime artifacts into bin directory (executable, dll) * move library and archive artifacts into lib directory (so, a, lib) * update FindSIRIUS.cmake --- cmake/FindSIRIUS.cmake | 6 +++--- src/CMakeLists.txt | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cmake/FindSIRIUS.cmake b/cmake/FindSIRIUS.cmake index c84895dd..fb0da91b 100644 --- a/cmake/FindSIRIUS.cmake +++ b/cmake/FindSIRIUS.cmake @@ -52,7 +52,7 @@ find_library(SIRIUS_LIBRARY HINTS ${SIRIUS_ROOT} PATH_SUFFIXES - lib/sirius) + lib) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") find_file(SIRIUS_DLL_LIBRARY @@ -60,7 +60,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") HINTS ${SIRIUS_ROOT} PATH_SUFFIXES - lib/sirius) + bin) endif() find_library(SIRIUS_STATIC_LIBRARY @@ -70,7 +70,7 @@ find_library(SIRIUS_STATIC_LIBRARY HINTS ${SIRIUS_ROOT} PATH_SUFFIXES - lib/sirius) + lib) include(FindPackageHandleStandardArgs) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a011e9b..e84e52e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -61,7 +61,10 @@ target_include_directories(libsirius PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include) set_target_properties(libsirius PROPERTIES PREFIX "") target_link_libraries(libsirius PUBLIC FFTW3::fftw3 spdlog gsl) -install(TARGETS libsirius DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sirius) +install(TARGETS libsirius + ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) # sirius static library add_library(libsirius-static STATIC $) @@ -70,7 +73,10 @@ target_include_directories(libsirius-static PUBLIC $) target_compile_options(libsirius-static PUBLIC $) target_link_libraries(libsirius-static $) -install(TARGETS libsirius-static DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/sirius) +install(TARGETS libsirius-static + ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) # sirius objects library add_library(libsirius-objects OBJECT EXCLUDE_FROM_ALL @@ -179,5 +185,8 @@ if (ENABLE_SIRIUS_EXECUTABLE) cxxopts Threads::Threads) - install(TARGETS sirius DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + install(TARGETS sirius + ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib + LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) endif() \ No newline at end of file From ec466a3328515aa3302d811e0c27dae45f26bd0b Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Fri, 7 Sep 2018 16:59:03 +0200 Subject: [PATCH 11/21] export libsirius and libsirius-static targets at install enable find_package(SIRIUS CONFIG) with SIRIUS_DIR CMake variable set to /path/INSTALL_DIR/share/cmake --- .appveyor.yml | 12 +++-- .travis.yml | 4 ++ .travis/linux/build.sh | 20 ++++++-- .travis/osx/build.sh | 19 +++++-- CMakeLists.txt | 23 ++++++++- src/CMakeLists.txt | 71 +++++++++++++++++--------- src/cmake/Config.cmake.in | 4 ++ tests/find-package-test/CMakeLists.txt | 14 +++-- third_party/gsl/CMakeLists.txt | 15 +++++- third_party/spdlog/CMakeLists.txt | 12 ++++- 10 files changed, 152 insertions(+), 42 deletions(-) create mode 100644 src/cmake/Config.cmake.in diff --git a/.appveyor.yml b/.appveyor.yml index be9ffa33..7351e123 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -63,7 +63,7 @@ build_script: - cmake --build . --target install --config %configuration% -- /m - cd %APPVEYOR_BUILD_FOLDER%/.build-static-runtime - appveyor AddMessage "Compiling sirius (static runtime)..." - - cmake --build . --target sirius + - cmake --build . --target sirius --config %configuration% -- /m test_script: - cd %APPVEYOR_BUILD_FOLDER%/.build @@ -75,8 +75,14 @@ test_script: - appveyor AddMessage "Testing find_package(SIRIUS)..." - mkdir %APPVEYOR_BUILD_FOLDER%\.build-test-find-package - cd %APPVEYOR_BUILD_FOLDER%\.build-test-find-package - - cmake ..\tests\find-package-test -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DSIRIUS_ROOT=%APPVEYOR_BUILD_FOLDER%/.install-directory - - cmake --build . --config %configuration% + - cmake ..\tests\find-package-test -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DSIRIUS_ROOT=%APPVEYOR_BUILD_FOLDER%/.install-directory -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=OFF + - cmake --build . --config %configuration% -- /m + - Release\basic_sirius_static_test.exe + - appveyor AddMessage "Testing find_package(SIRIUS CONFIG)..." + - mkdir %APPVEYOR_BUILD_FOLDER%\.build-test-find-package-config + - cd %APPVEYOR_BUILD_FOLDER%\.build-test-find-package-config + - cmake ..\tests\find-package-test -DVCPKG_TARGET_TRIPLET=%VCPKG_TRIPLET% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%/scripts/buildsystems/vcpkg.cmake -G "%CMAKE_GENERATOR%" -DSIRIUS_DIR=%APPVEYOR_BUILD_FOLDER%/.install-directory/share/cmake -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=ON + - cmake --build . --config %configuration% -- /m - Release\basic_sirius_static_test.exe # to disable deployment diff --git a/.travis.yml b/.travis.yml index c4abe01e..846765b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,10 @@ stages: - test - deploy +env: + global: + - CMAKE_BUILD_TYPE=Release + matrix: include: # OS X diff --git a/.travis/linux/build.sh b/.travis/linux/build.sh index 672d3f67..80f1e6fe 100755 --- a/.travis/linux/build.sh +++ b/.travis/linux/build.sh @@ -25,7 +25,7 @@ cd ${BUILD_DIR} cmake ${PROJECT_DIR} \ -G "Unix Makefiles" \ -DCMAKE_CXX_COMPILER="${CXX_COMPILER}" \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ @@ -60,13 +60,27 @@ make -j4 sirius make -j4 install # test find_package(SIRIUS) -cd ${PROJECT_DIR} mkdir ${PROJECT_DIR}/.build-test-find-package cd ${PROJECT_DIR}/.build-test-find-package cmake ../tests/find-package-test \ -G "Unix Makefiles" \ -DCMAKE_CXX_COMPILER="${CXX_COMPILER}" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ -DSIRIUS_ROOT=${INSTALL_DIR} \ - -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} + -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} \ + -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=OFF +make -j4 +./basic_sirius_static_test + +# test find_package(SIRIUS CONFIG) +mkdir ${PROJECT_DIR}/.build-test-find-package-config +cd ${PROJECT_DIR}/.build-test-find-package-config +cmake ../tests/find-package-test \ + -G "Unix Makefiles" \ + -DCMAKE_CXX_COMPILER="${CXX_COMPILER}" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DSIRIUS_DIR=${INSTALL_DIR}/share/cmake \ + -DENABLE_CLANG_LIBCPP=${ENABLE_CLANG_LIBCPP} \ + -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=ON make -j4 ./basic_sirius_static_test \ No newline at end of file diff --git a/.travis/osx/build.sh b/.travis/osx/build.sh index 91be1921..561c11f9 100755 --- a/.travis/osx/build.sh +++ b/.travis/osx/build.sh @@ -23,7 +23,7 @@ cd ${BUILD_DIR} # create makefile project cmake ${PROJECT_DIR} \ -G "Unix Makefiles" \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \ -DSIRIUS_VERSION="${SIRIUS_VERSION}" \ -DSIRIUS_REVISION_COMMIT="${SIRIUS_REVISION_COMMIT}" \ @@ -56,12 +56,23 @@ make -j4 sirius make -j4 install # test find_package(SIRIUS) -cd ${PROJECT_DIR} mkdir ${PROJECT_DIR}/.build-test-find-package cd ${PROJECT_DIR}/.build-test-find-package cmake ../tests/find-package-test \ -G "Unix Makefiles" \ - -DCMAKE_BUILD_TYPE=Release \ - -DSIRIUS_ROOT=${INSTALL_DIR} + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DSIRIUS_ROOT=${INSTALL_DIR} \ + -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=OFF +make -j4 +./basic_sirius_static_test + +# test find_package(SIRIUS CONFIG) +mkdir ${PROJECT_DIR}/.build-test-find-package-config +cd ${PROJECT_DIR}/.build-test-find-package-config +cmake ../tests/find-package-test \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \ + -DSIRIUS_DIR=${INSTALL_DIR}/share/cmake \ + -DENABLE_FIND_PACKAGE_SIRIUS_CONFIG=ON make -j4 ./basic_sirius_static_test \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index c9e7b306..fe1ceec3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ set(SIRIUS_REVISION_COMMIT "" CACHE STRING "Sirius revision commit") include(cmake/BuildType.cmake) include(cmake/ExtractVersion.cmake) -project(Sirius VERSION ${SIRIUS_VERSION} LANGUAGES CXX) +project(SIRIUS VERSION ${SIRIUS_VERSION} LANGUAGES CXX) option(ENABLE_SIRIUS_EXECUTABLE "Enable Sirius executable target" ON) option(ENABLE_CACHE_OPTIMIZATION "Enable cache optimization (FFTW plan, Filter FFT)" ON) @@ -105,11 +105,32 @@ endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIRIUS_CXX_FLAGS}") +# define GNU standard installation directories (CMAKE_INSTALL_XXXDIR) +include(GNUInstallDirs) + +# define common variables for install directory structure +set(SIRIUS_CONFIG_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake") +set(SIRIUS_CONFIG_CMAKE_MODULES_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake-modules") +set(SIRIUS_CONFIG_INCLUDE_DIR "${CMAKE_INSTALL_INCLUDEDIR}/sirius") +set(SIRIUS_CONFIG_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") +set(SIRIUS_CONFIG_NAMESPACE "${PROJECT_NAME}::") + find_package(Threads) add_subdirectory(third_party) add_subdirectory(src) +# copy find_package as module for SIRIUS and FFTW3 +install(FILES cmake/FindSIRIUS.cmake DESTINATION ${SIRIUS_CONFIG_CMAKE_MODULES_INSTALL_DIR}) +install(FILES cmake/FindFFTW3.cmake DESTINATION ${SIRIUS_CONFIG_CMAKE_MODULES_INSTALL_DIR}) + +# add namespace "SIRIUS::" to exported targets +install( + EXPORT "${SIRIUS_CONFIG_TARGETS_EXPORT_NAME}" + NAMESPACE "${SIRIUS_CONFIG_NAMESPACE}" + DESTINATION "${SIRIUS_CONFIG_CMAKE_INSTALL_DIR}" +) + if (BUILD_TESTING) enable_testing() add_subdirectory(tests) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e84e52e0..dae96937 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,26 +57,20 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") endif() target_include_directories(libsirius PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/include) + $ + $ + $) set_target_properties(libsirius PROPERTIES PREFIX "") target_link_libraries(libsirius PUBLIC FFTW3::fftw3 spdlog gsl) -install(TARGETS libsirius - ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) # sirius static library add_library(libsirius-static STATIC $) set_target_properties(libsirius-static PROPERTIES PREFIX "") -target_include_directories(libsirius-static PUBLIC $) +target_include_directories(libsirius-static PUBLIC + $) target_compile_definitions(libsirius-static PUBLIC $) target_compile_options(libsirius-static PUBLIC $) target_link_libraries(libsirius-static $) -install(TARGETS libsirius-static - ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) # sirius objects library add_library(libsirius-objects OBJECT EXCLUDE_FROM_ALL @@ -132,25 +126,53 @@ add_library(libsirius-objects OBJECT EXCLUDE_FROM_ALL sirius/utils/numeric.h sirius/utils/numeric.cc) set_target_properties(libsirius-objects PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(libsirius-objects PUBLIC $) +target_include_directories(libsirius-objects PUBLIC + $) target_compile_definitions(libsirius-objects PUBLIC $) target_compile_options(libsirius-objects PUBLIC $) +# generate find package config mode +include(CMakePackageConfigHelpers) + +set(SIRIUS_CMAKE_GENERATED_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/cmake-generated-config") +set(SIRIUS_CONFIG_VERSION "${SIRIUS_CMAKE_GENERATED_CONFIG}/${PROJECT_NAME}ConfigVersion.cmake") +set(SIRIUS_CONFIG_PROJECT "${SIRIUS_CMAKE_GENERATED_CONFIG}/${PROJECT_NAME}Config.cmake") + +# generate SIRIUSConfigVersion.cmake file +write_basic_package_version_file( + "${SIRIUS_CONFIG_VERSION}" COMPATIBILITY SameMajorVersion +) + +# generate SIRIUSConfig.cmake file +configure_package_config_file( + "cmake/Config.cmake.in" + "${SIRIUS_CONFIG_PROJECT}" + INSTALL_DESTINATION "${SIRIUS_CONFIG_CMAKE_INSTALL_DIR}" +) + +# copy SIRIUSConfig.cmake and SIRIUSConfigVersion.cmake into install directory +install( + FILES + "${SIRIUS_CONFIG_PROJECT}" + "${SIRIUS_CONFIG_VERSION}" + DESTINATION "${SIRIUS_CONFIG_CMAKE_INSTALL_DIR}" +) + # copy sirius include directory into install directory install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius + DESTINATION ${SIRIUS_CONFIG_INCLUDE_DIR} FILES_MATCHING PATTERN "*.h" PATTERN "*.txx") install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sirius - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius + DESTINATION ${SIRIUS_CONFIG_INCLUDE_DIR} FILES_MATCHING PATTERN "*.h" PATTERN "*.txx") -# copy spdlog library into install directory -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/spdlog/spdlog/include/spdlog - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius) - -# copy gsl library into install directory -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/gsl/gsl/include/gsl - DESTINATION ${CMAKE_INSTALL_PREFIX}/include/sirius) +# install and export libsirius and libsirius-static +install(TARGETS libsirius libsirius-static + EXPORT ${SIRIUS_CONFIG_TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${SIRIUS_CONFIG_INCLUDE_DIR}") if (ENABLE_SIRIUS_EXECUTABLE OR BUILD_TESTING) add_library(libsirius-gdal-static STATIC EXCLUDE_FROM_ALL @@ -186,7 +208,8 @@ if (ENABLE_SIRIUS_EXECUTABLE) Threads::Threads) install(TARGETS sirius - ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + EXPORT ${SIRIUS_CONFIG_TARGETS_EXPORT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() \ No newline at end of file diff --git a/src/cmake/Config.cmake.in b/src/cmake/Config.cmake.in new file mode 100644 index 00000000..22a08db0 --- /dev/null +++ b/src/cmake/Config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@SIRIUS_CONFIG_TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@") \ No newline at end of file diff --git a/tests/find-package-test/CMakeLists.txt b/tests/find-package-test/CMakeLists.txt index 23719460..e2f7e6c2 100644 --- a/tests/find-package-test/CMakeLists.txt +++ b/tests/find-package-test/CMakeLists.txt @@ -30,16 +30,17 @@ if(POLICY CMP0074) cmake_policy(SET CMP0074 NEW) endif() +option(ENABLE_CLANG_LIBCPP "Force use of libc++" OFF) +option(ENABLE_FIND_PACKAGE_SIRIUS_CONFIG "Force use of mode config of find_package(SIRIUS)" OFF) + project(TestFindPackage CXX) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# path to additional cmake modules +# provide FFTW3 find_package helper list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake") -include(${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/BuildType.cmake) - # FFTW3 headers are required find_package(FFTW3 REQUIRED) @@ -52,7 +53,12 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STRE endif() endif() -find_package(SIRIUS REQUIRED) +set (SIRIUS_FIND_PACKAGE "MODULE") +if (ENABLE_FIND_PACKAGE_SIRIUS_CONFIG) + set(SIRIUS_FIND_PACKAGE "CONFIG") +endif () + +find_package(SIRIUS ${SIRIUS_FIND_PACKAGE} REQUIRED) add_executable(basic_sirius_shared_test basic_sirius_test.cc) diff --git a/third_party/gsl/CMakeLists.txt b/third_party/gsl/CMakeLists.txt index 88a320f2..8b6ab028 100644 --- a/third_party/gsl/CMakeLists.txt +++ b/third_party/gsl/CMakeLists.txt @@ -22,6 +22,17 @@ cmake_minimum_required(VERSION 3.2) add_library(gsl INTERFACE) -target_include_directories(gsl INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/gsl/include) +target_include_directories(gsl INTERFACE + $ + $ +) -target_compile_options(gsl INTERFACE $<$>:-Wno-unknown-attributes>) \ No newline at end of file +target_compile_options(gsl INTERFACE $<$>:-Wno-unknown-attributes>) + +# copy gsl library into install directory +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/gsl/include/gsl + DESTINATION ${SIRIUS_CONFIG_INCLUDE_DIR}) + +install(TARGETS gsl + EXPORT "${SIRIUS_CONFIG_TARGETS_EXPORT_NAME}" + INCLUDES DESTINATION "${SIRIUS_CONFIG_INCLUDE_DIR}") \ No newline at end of file diff --git a/third_party/spdlog/CMakeLists.txt b/third_party/spdlog/CMakeLists.txt index afb6a36c..90eb727d 100644 --- a/third_party/spdlog/CMakeLists.txt +++ b/third_party/spdlog/CMakeLists.txt @@ -22,4 +22,14 @@ cmake_minimum_required(VERSION 3.2) add_library(spdlog INTERFACE) -target_include_directories(spdlog INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/spdlog/include) +target_include_directories(spdlog INTERFACE + $ + $) + +# copy spdlog library into install directory +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/spdlog/include/spdlog + DESTINATION ${SIRIUS_CONFIG_INCLUDE_DIR}) + +install(TARGETS spdlog + EXPORT "${SIRIUS_CONFIG_TARGETS_EXPORT_NAME}" + INCLUDES DESTINATION "${SIRIUS_CONFIG_INCLUDE_DIR}") From 6a0d7cdb748dea8f37960d4ae02ac7f5d75e35e9 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Tue, 11 Sep 2018 11:26:26 +0200 Subject: [PATCH 12/21] add third party license summary and copy licenses into install directory --- CMakeLists.txt | 3 +++ LICENSE-3RD-PARTY.md | 26 ++++++++++++++++++++++++++ third_party/gsl/CMakeLists.txt | 6 +++++- third_party/spdlog/CMakeLists.txt | 4 ++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 LICENSE-3RD-PARTY.md diff --git a/CMakeLists.txt b/CMakeLists.txt index fe1ceec3..1c64ab42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,6 +131,9 @@ install( DESTINATION "${SIRIUS_CONFIG_CMAKE_INSTALL_DIR}" ) +install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}) +install(FILES LICENSE-3RD-PARTY.md DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}) + if (BUILD_TESTING) enable_testing() add_subdirectory(tests) diff --git a/LICENSE-3RD-PARTY.md b/LICENSE-3RD-PARTY.md new file mode 100644 index 00000000..6a1b7389 --- /dev/null +++ b/LICENSE-3RD-PARTY.md @@ -0,0 +1,26 @@ +All or part of third party software used by SIRIUS + +| Software | License | +|:----------------|:--------------------------------------------------------------| +| [FFTW3] | [GNU General Public License v3][FFTW3-license] | +| [GDAL] | [MIT][GDAL-license] | +| [spdlog] | [MIT][spdlog-license] | +| [cxxopts] | [MIT][cxxopts-license] | +| [GSL] | [MIT][GSL-license] | +| [cmake-modules] | [Boost Software License - Version 1.0][cmake-modules-license] | +| [Catch2] | [Boost Software License - Version 1.0][Catch2-license] | + +[FFTW3]: http://www.fftw.org/ +[FFTW3-license]: http://www.fftw.org/doc/License-and-Copyright.html +[GDAL]: https://gdal.org/ +[GDAL-license]: http://svn.osgeo.org/gdal/trunk/gdal/LICENSE.TXT +[spdlog]: https://github.com/gabime/spdlog +[spdlog-license]: https://github.com/gabime/spdlog/blob/v1.x/LICENSE +[cxxopts]: https://github.com/jarro2783/cxxopts +[cxxopts-license]: https://github.com/jarro2783/cxxopts/blob/master/LICENSE +[Catch2]: https://github.com/catchorg/Catch2 +[GSL]: https://github.com/Microsoft/GSL +[GSL-license]: https://github.com/Microsoft/GSL/blob/master/LICENSE +[Catch2-license]: https://github.com/catchorg/Catch2/blob/master/LICENSE.txt +[cmake-modules]: https://github.com/rpavlik/cmake-modules +[cmake-modules-license]: https://github.com/rpavlik/cmake-modules/blob/master/LICENSE_1_0.txt \ No newline at end of file diff --git a/third_party/gsl/CMakeLists.txt b/third_party/gsl/CMakeLists.txt index 8b6ab028..eee92de9 100644 --- a/third_party/gsl/CMakeLists.txt +++ b/third_party/gsl/CMakeLists.txt @@ -35,4 +35,8 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/gsl/include/gsl install(TARGETS gsl EXPORT "${SIRIUS_CONFIG_TARGETS_EXPORT_NAME}" - INCLUDES DESTINATION "${SIRIUS_CONFIG_INCLUDE_DIR}") \ No newline at end of file + INCLUDES DESTINATION "${SIRIUS_CONFIG_INCLUDE_DIR}") + +install(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/gsl/LICENSE + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/third_party/gsl) \ No newline at end of file diff --git a/third_party/spdlog/CMakeLists.txt b/third_party/spdlog/CMakeLists.txt index 90eb727d..4e61ffb7 100644 --- a/third_party/spdlog/CMakeLists.txt +++ b/third_party/spdlog/CMakeLists.txt @@ -33,3 +33,7 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/spdlog/include/spdlog install(TARGETS spdlog EXPORT "${SIRIUS_CONFIG_TARGETS_EXPORT_NAME}" INCLUDES DESTINATION "${SIRIUS_CONFIG_INCLUDE_DIR}") + +install(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/spdlog/LICENSE + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/third_party/spdlog") \ No newline at end of file From e282241b172df1f71e454a6b4e5eee76089e6ffb Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Tue, 11 Sep 2018 16:14:19 +0200 Subject: [PATCH 13/21] add CHANGELOG.md --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..ac11e30d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# Sirius Changelog + +## 0.2.0 + +### Features +* export `libsirius` and `libsirius-static` CMake targets when installing Sirius: use `find_package(SIRIUS CONFIG)` with `-DSIRIUS_DIR=/path/to/install/directory/share/cmake` to find Sirius libraries (#41) + +### Bug fixes +* Windows support: Visual Studio 2017 (#41) +* OS X support: Xcode 8.3 and 9.4 (#41) +* Linux support: GCC 5, 6 and 7 and Clang 4, 5 and 6 (#41) + + +## 0.1.0 (09/08/2018) + +### Features +* Frequency resampling + * [Zero padding upsampling][zero-padding-upsampling] + * [Periodization upsampling][periodization-upsampling] +* [Periodic plus smooth image decomposition][periodic-plus-smooth-image-decomposition] +* [Filtering][image-filtering] +* Stream processing + +[zero-padding-upsampling]: https://cs-si.github.io/SIRIUS/html/upsampling/sinc_as_zpd.html +[periodization-upsampling]: https://cs-si.github.io/SIRIUS/html/upsampling/user_kernel.html#when-sirius-uses-a-filter-to-upsample-the-spectrum-is-periodized-instead-of-zero-padded +[periodic-plus-smooth-image-decomposition]: https://hal.archives-ouvertes.fr/file/index/docid/388020/filename/2009-11.pdf +[image-filtering]: https://cs-si.github.io/SIRIUS/html/upsampling/user_kernel.html \ No newline at end of file From 4060ac8a10820f8dba008bb187adf3ec004d2482 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Wed, 19 Sep 2018 17:23:47 +0200 Subject: [PATCH 14/21] update library naming * add debug postfix (d) to libsirius and libsirius-static binary filenames * rename libsirius-static binary file to libsirius.a (Linux and OS X) or libsirius_s.lib (Windows) --- CMakeLists.txt | 4 +++ cmake/FindSIRIUS.cmake | 61 +++++++++++++++++++++++++++++++++----- src/CMakeLists.txt | 44 ++++++++++++++++++++++----- third_party/CMakeLists.txt | 18 ----------- 4 files changed, 94 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c64ab42..788391f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ option(ENABLE_GSL_CONTRACTS "Enable GSL contracts" OFF) option(ENABLE_DOCUMENTATION "Enable documentation generation" OFF) option(BUILD_TESTING "Enable unit tests" OFF) option(USE_CXX_STATIC_RUNTIME "Build targets with C++ static runtime library" OFF) +option(DISABLE_INSTALL_HEADERS "Disable install headers" OFF) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") option(ENABLE_CLANG_LIBCPP "Force use of libc++" OFF) @@ -65,11 +66,14 @@ message(STATUS "Link Sirius binaries with static C++ runtime library: ${USE_CXX_ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") message(STATUS "Force use of libc++: ${ENABLE_CLANG_LIBCPP}") endif() +message(STATUS "Disable install headers: ${DISABLE_INSTALL_HEADERS}") set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_DEBUG_POSTFIX "d") + # set CXX global flags set(SIRIUS_CXX_FLAGS "") diff --git a/cmake/FindSIRIUS.cmake b/cmake/FindSIRIUS.cmake index fb0da91b..abeeaf6e 100644 --- a/cmake/FindSIRIUS.cmake +++ b/cmake/FindSIRIUS.cmake @@ -52,7 +52,8 @@ find_library(SIRIUS_LIBRARY HINTS ${SIRIUS_ROOT} PATH_SUFFIXES - lib) + lib + lib64) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") find_file(SIRIUS_DLL_LIBRARY @@ -63,14 +64,44 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") bin) endif() +find_library(SIRIUS_DEBUG_LIBRARY + NAMES + siriusd # linux/os x naming (default library prefix is lib) + libsiriusd # windows naming (default library prefix is '') + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + lib + lib64) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + find_file(SIRIUS_DEBUG_DLL_LIBRARY + NAMES libsiriusd.dll + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + bin) +endif() + find_library(SIRIUS_STATIC_LIBRARY NAMES - sirius-static # linux/os x naming (default library prefix is lib) - libsirius-static # windows naming (default library prefix is '') + libsirius.a + libsirius_s # windows naming (default library prefix is '') HINTS ${SIRIUS_ROOT} PATH_SUFFIXES - lib) + lib + lib64) + +find_library(SIRIUS_DEBUG_STATIC_LIBRARY + NAMES + libsirius.a + libsirius_s # windows naming (default library prefix is '') + HINTS + ${SIRIUS_ROOT} + PATH_SUFFIXES + lib + lib64) include(FindPackageHandleStandardArgs) @@ -88,18 +119,32 @@ if (SIRIUS_FOUND) INTERFACE_INCLUDE_DIRECTORIES ${SIRIUS_INCLUDE_DIRS}) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set_target_properties(SIRIUS::libsirius PROPERTIES - IMPORTED_LOCATION ${SIRIUS_DLL_LIBRARY} - IMPORTED_IMPLIB ${SIRIUS_LIBRARIES}) + IMPORTED_LOCATION_RELEASE ${SIRIUS_DLL_LIBRARY} + IMPORTED_IMPLIB_RELEASE ${SIRIUS_LIBRARIES}) + if (SIRIUS_DEBUG_LIBRARY) + set_target_properties(SIRIUS::libsirius PROPERTIES + IMPORTED_LOCATION_DEBUG ${SIRIUS_DEBUG_DLL_LIBRARY} + IMPORTED_IMPLIB_DEBUG ${SIRIUS_DEBUG_LIBRARIES}) + endif() else() set_target_properties(SIRIUS::libsirius PROPERTIES - IMPORTED_LOCATION ${SIRIUS_LIBRARIES}) + IMPORTED_LOCATION_RELEASE ${SIRIUS_LIBRARIES}) + if (SIRIUS_DEBUG_LIBRARY) + set_target_properties(SIRIUS::libsirius PROPERTIES + IMPORTED_LOCATION_DEBUG ${SIRIUS_DEBUG_LIBRARIES}) + endif() endif() endif() if (NOT TARGET SIRIUS::libsirius-static) add_library(SIRIUS::libsirius-static STATIC IMPORTED GLOBAL) set_target_properties(SIRIUS::libsirius-static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${SIRIUS_INCLUDE_DIRS} - IMPORTED_LOCATION ${SIRIUS_STATIC_LIBRARIES}) + IMPORTED_LOCATION_RELEASE ${SIRIUS_STATIC_LIBRARIES}) + + if (SIRIUS_DEBUG_LIBRARY) + set_target_properties(SIRIUS::libsirius-static PROPERTIES + IMPORTED_LOCATION_DEBUG ${SIRIUS_DEBUG_STATIC_LIBRARIES}) + endif() endif() message(STATUS "find_package(SIRIUS) provides CMake targets: SIRIUS::libsirius and SIRIUS::libsirius-static") endif () diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dae96937..585e9644 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,8 @@ cmake_minimum_required(VERSION 3.2) +find_package(FFTW3 REQUIRED) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius) message(STATUS "Generating sirius/sirius.h") @@ -66,6 +68,11 @@ target_link_libraries(libsirius PUBLIC FFTW3::fftw3 spdlog gsl) # sirius static library add_library(libsirius-static STATIC $) set_target_properties(libsirius-static PROPERTIES PREFIX "") +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(libsirius-static PROPERTIES OUTPUT_NAME "libsirius_s") +else() + set_target_properties(libsirius-static PROPERTIES OUTPUT_NAME "libsirius") +endif() target_include_directories(libsirius-static PUBLIC $) target_compile_definitions(libsirius-static PUBLIC $) @@ -158,13 +165,22 @@ install( DESTINATION "${SIRIUS_CONFIG_CMAKE_INSTALL_DIR}" ) -# copy sirius include directory into install directory -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius - DESTINATION ${SIRIUS_CONFIG_INCLUDE_DIR} - FILES_MATCHING PATTERN "*.h" PATTERN "*.txx") -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sirius - DESTINATION ${SIRIUS_CONFIG_INCLUDE_DIR} - FILES_MATCHING PATTERN "*.h" PATTERN "*.txx") +if (NOT DISABLE_INSTALL_HEADERS) + # copy sirius include directory into install directory + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/sirius + DESTINATION ${SIRIUS_CONFIG_INCLUDE_DIR} + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.txx") + install(DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR}/sirius + DESTINATION + ${SIRIUS_CONFIG_INCLUDE_DIR} + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.txx" + PATTERN "gdal*" EXCLUDE) +endif() # install and export libsirius and libsirius-static install(TARGETS libsirius libsirius-static @@ -175,6 +191,20 @@ install(TARGETS libsirius libsirius-static INCLUDES DESTINATION "${SIRIUS_CONFIG_INCLUDE_DIR}") if (ENABLE_SIRIUS_EXECUTABLE OR BUILD_TESTING) + find_package(GDAL REQUIRED) + + add_library(gdal SHARED IMPORTED) + set_target_properties(gdal PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${GDAL_INCLUDE_DIR} + INTERFACE_COMPILE_DEFINITIONS CPL_LOG=${ENABLE_LOGS}) + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + set_target_properties(gdal PROPERTIES + IMPORTED_IMPLIB ${GDAL_LIBRARY}) + else() + set_target_properties(gdal PROPERTIES + IMPORTED_LOCATION ${GDAL_LIBRARY}) + endif() + add_library(libsirius-gdal-static STATIC EXCLUDE_FROM_ALL # gdal sirius/gdal/debug.h diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index c4ee4783..36ca9405 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -22,27 +22,9 @@ cmake_minimum_required(VERSION 3.2) # create library from package dependencies -find_package(FFTW3 REQUIRED) - add_subdirectory(spdlog) add_subdirectory(gsl) -if (ENABLE_SIRIUS_EXECUTABLE OR BUILD_TESTING) - find_package(GDAL REQUIRED) - - add_library(gdal SHARED IMPORTED GLOBAL) - set_target_properties(gdal PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${GDAL_INCLUDE_DIR} - INTERFACE_COMPILE_DEFINITIONS CPL_LOG=${ENABLE_LOGS}) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set_target_properties(gdal PROPERTIES - IMPORTED_IMPLIB ${GDAL_LIBRARY}) - else() - set_target_properties(gdal PROPERTIES - IMPORTED_LOCATION ${GDAL_LIBRARY}) - endif() -endif () - if (ENABLE_SIRIUS_EXECUTABLE) add_subdirectory(cxxopts) endif() From e91009eea4e46462868fdcaa185763f9a775430a Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Mon, 1 Oct 2018 15:10:23 +0200 Subject: [PATCH 15/21] update CMakeLists * group targets by folders (bin, lib, test) * improve test targets --- .appveyor.yml | 6 +++--- .travis/docker/build_install_sirius.sh | 6 +++--- .travis/linux/build.sh | 6 +++--- .travis/osx/build.sh | 6 +++--- CMakeLists.txt | 29 ++++++++++++++++---------- src/CMakeLists.txt | 16 +++++++++++--- tests/CMakeLists.txt | 23 +++++++++++++++++++- 7 files changed, 65 insertions(+), 27 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7351e123..24ce50d1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -68,10 +68,10 @@ build_script: test_script: - cd %APPVEYOR_BUILD_FOLDER%/.build - appveyor AddMessage "Running tests..." - - ctest --output-on-failure + - ctest -C %configuration% --output-on-failure - appveyor AddMessage "Running sirius..." - - src\%configuration%\sirius.exe -h - - src\%configuration%\sirius.exe -v trace -r 2 ..\data\input\lena.jpg lena_z2.tif + - bin\sirius.exe -h + - bin\sirius.exe -v trace -r 2 ..\data\input\lena.jpg lena_z2.tif - appveyor AddMessage "Testing find_package(SIRIUS)..." - mkdir %APPVEYOR_BUILD_FOLDER%\.build-test-find-package - cd %APPVEYOR_BUILD_FOLDER%\.build-test-find-package diff --git a/.travis/docker/build_install_sirius.sh b/.travis/docker/build_install_sirius.sh index dca69583..0e2df874 100755 --- a/.travis/docker/build_install_sirius.sh +++ b/.travis/docker/build_install_sirius.sh @@ -44,14 +44,14 @@ make -j4 libsirius-static make -j4 build_tests # run tests -ctest --output-on-failure +ctest -C Release --output-on-failure # build sirius executable make -j4 sirius # execute sirius -./src/sirius -h -./src/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif +./bin/sirius -h +./bin/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif # install project make -j4 install diff --git a/.travis/linux/build.sh b/.travis/linux/build.sh index 80f1e6fe..2fb05c64 100755 --- a/.travis/linux/build.sh +++ b/.travis/linux/build.sh @@ -47,14 +47,14 @@ make -j4 libsirius-static make -j4 build_tests # run tests -ctest --output-on-failure +ctest -C ${CMAKE_BUILD_TYPE} --output-on-failure # build sirius executable make -j4 sirius # execute sirius -./src/sirius -h - ./src/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif +./bin/sirius -h + ./bin/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif # install project make -j4 install diff --git a/.travis/osx/build.sh b/.travis/osx/build.sh index 561c11f9..f0e687d2 100755 --- a/.travis/osx/build.sh +++ b/.travis/osx/build.sh @@ -43,14 +43,14 @@ make -j4 libsirius-static make -j4 build_tests # run tests -ctest --output-on-failure +ctest -C ${CMAKE_BUILD_TYPE} --output-on-failure # build sirius executable make -j4 sirius # execute sirius -./src/sirius -h - ./src/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif +./bin/sirius -h + ./bin/sirius -v trace -r 2 ../data/input/lena.jpg lena_z2.tif # install project make -j4 install diff --git a/CMakeLists.txt b/CMakeLists.txt index 788391f0..7b0711a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,8 +72,6 @@ set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_DEBUG_POSTFIX "d") - # set CXX global flags set(SIRIUS_CXX_FLAGS "") @@ -109,9 +107,24 @@ endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SIRIUS_CXX_FLAGS}") +# add debug postfix to library output names +set(CMAKE_DEBUG_POSTFIX "d") + +# allow target organizations into folders +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + # define GNU standard installation directories (CMAKE_INSTALL_XXXDIR) include(GNUInstallDirs) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") +foreach(config DEBUG RELEASE) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config} "${CMAKE_CURRENT_BINARY_DIR}/lib") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config} "${CMAKE_CURRENT_BINARY_DIR}/lib") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config} "${CMAKE_CURRENT_BINARY_DIR}/bin") +endforeach() + # define common variables for install directory structure set(SIRIUS_CONFIG_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake") set(SIRIUS_CONFIG_CMAKE_MODULES_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake-modules") @@ -139,14 +152,8 @@ install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}) install(FILES LICENSE-3RD-PARTY.md DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}) if (BUILD_TESTING) - enable_testing() + include(CTest) add_subdirectory(tests) - - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests/input - ${CMAKE_CURRENT_BINARY_DIR}/tests/filters - ${CMAKE_CURRENT_BINARY_DIR}/tests/output) - file(COPY data/input DESTINATION tests) - file(COPY data/filters DESTINATION tests) endif() if (ENABLE_DOCUMENTATION) @@ -176,8 +183,8 @@ if (ENABLE_DOCUMENTATION) # else() # message(STATUS "Cannot build pdf manual (LaTeX not found)") # endif() - # copy doxygen documentation into install directory - install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html + # copy doxygen documentation into install directory + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_PREFIX}/doc/api-doc) else() message(STATUS "Doxygen is needed to build the documentation.") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 585e9644..e4dedb20 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -62,12 +62,16 @@ target_include_directories(libsirius PUBLIC $ $ $) -set_target_properties(libsirius PROPERTIES PREFIX "") +set_target_properties(libsirius PROPERTIES + PREFIX "" + FOLDER "lib") target_link_libraries(libsirius PUBLIC FFTW3::fftw3 spdlog gsl) # sirius static library add_library(libsirius-static STATIC $) -set_target_properties(libsirius-static PROPERTIES PREFIX "") +set_target_properties(libsirius-static PROPERTIES + PREFIX "" + FOLDER "lib") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set_target_properties(libsirius-static PROPERTIES OUTPUT_NAME "libsirius_s") else() @@ -132,7 +136,9 @@ add_library(libsirius-objects OBJECT EXCLUDE_FROM_ALL sirius/utils/lru_cache.h sirius/utils/numeric.h sirius/utils/numeric.cc) -set_target_properties(libsirius-objects PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_target_properties(libsirius-objects PROPERTIES + POSITION_INDEPENDENT_CODE ON + FOLDER lib) target_include_directories(libsirius-objects PUBLIC $) target_compile_definitions(libsirius-objects PUBLIC $) @@ -221,6 +227,7 @@ if (ENABLE_SIRIUS_EXECUTABLE OR BUILD_TESTING) sirius/gdal/types.h sirius/gdal/wrapper.h sirius/gdal/wrapper.cc) + set_target_properties(libsirius-gdal-static PROPERTIES FOLDER lib) target_link_libraries(libsirius-gdal-static PUBLIC gdal libsirius-static) endif () @@ -232,6 +239,9 @@ if (ENABLE_SIRIUS_EXECUTABLE) main.cc) + set_target_properties(sirius PROPERTIES + FOLDER bin + DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) target_link_libraries(sirius libsirius-gdal-static cxxopts diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 71b48b9f..45499140 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,14 +21,29 @@ cmake_minimum_required(VERSION 3.2) +# unit_test_init_data copies tests data into the expected directories +add_custom_target(unit_test_init_data ALL + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/input" + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/filters" + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/output" + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_SOURCE_DIR}/data/input" + "${CMAKE_CURRENT_BINARY_DIR}/input" + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_SOURCE_DIR}/data/filters" + "${CMAKE_CURRENT_BINARY_DIR}/filters") +set_target_properties(unit_test_init_data PROPERTIES FOLDER test) + # unit_test_main contains catch2 main (speed up unit test compilation) add_library(unit_test_main STATIC EXCLUDE_FROM_ALL catch_main.cc) +set_target_properties(unit_test_main PROPERTIES FOLDER test) target_link_libraries(unit_test_main catch) add_library(unit_test_lib STATIC EXCLUDE_FROM_ALL utils.h utils.cc) target_include_directories(unit_test_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +set_target_properties(unit_test_lib PROPERTIES FOLDER test) target_link_libraries(unit_test_lib libsirius-static libsirius-gdal-static @@ -38,16 +53,22 @@ file(GLOB unit_tests *_tests.cc) # group test targets into one to make build easier add_custom_target(build_tests) +set_target_properties(build_tests PROPERTIES FOLDER test) +add_dependencies(build_tests unit_test_init_data) # create unit test target from test file # need CMake regeneration to take into account new unit tests foreach(unit_test_file ${unit_tests}) string(REGEX REPLACE ".*/(.*).cc" "\\1" unit_test "${unit_test_file}") add_executable(${unit_test} EXCLUDE_FROM_ALL ${unit_test_file}) + set_target_properties(${unit_test} PROPERTIES + FOLDER test + DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) target_link_libraries(${unit_test} libsirius-static unit_test_lib unit_test_main) - add_test(${unit_test} ${unit_test}) + add_test(NAME ${unit_test} COMMAND ${unit_test} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_dependencies(build_tests ${unit_test}) endforeach() From 14bf8d31d903e8de1aa0d702ac9c44df911c35a1 Mon Sep 17 00:00:00 2001 From: Herve Ly-Sunnaram Date: Mon, 1 Oct 2018 17:08:39 +0200 Subject: [PATCH 16/21] refactor streaming * generic stream transformation * update resampling API * generic image decomposition (cherry picked from commit 73fb7c69205a6ea7bdf2624b6d5687091eeda183) --- CMakeLists.txt | 2 +- INTERNALS.md | 25 +- README.md | 82 ++--- cmake/FindSIRIUS.cmake | 1 - src/CMakeLists.txt | 57 +-- src/main.cc | 337 ++++++++---------- src/sirius/filter.cc | 71 ++-- src/sirius/filter.h | 63 ++-- src/sirius/frequency_resampler_factory.cc | 55 +-- src/sirius/frequency_resampler_factory.h | 23 +- src/sirius/gdal/i_input_stream.h | 55 +++ .../i_output_stream.h} | 34 +- src/sirius/{ => gdal}/image_streamer.h | 59 +-- .../image_streamer.txx} | 93 +++-- src/sirius/gdal/input_stream.cc | 97 ++--- src/sirius/gdal/input_stream.h | 27 +- src/sirius/gdal/output_stream.cc | 51 +++ ...ampled_output_stream.h => output_stream.h} | 32 +- src/sirius/gdal/resampled_output_stream.cc | 78 ---- src/sirius/gdal/resampling/input_stream.cc | 71 ++++ src/sirius/gdal/resampling/input_stream.h | 74 ++++ src/sirius/gdal/resampling/output_stream.cc | 72 ++++ src/sirius/gdal/resampling/output_stream.h | 65 ++++ src/sirius/gdal/stream_block.h | 6 +- src/sirius/i_frequency_resampler.h | 16 +- src/sirius/image.cc | 8 - src/sirius/image.h | 9 +- .../periodic_smooth_policy.h | 25 +- .../periodic_smooth_policy.txx | 123 ++----- src/sirius/image_decomposition/policies.h | 42 +++ .../image_decomposition/regular_policy.h | 22 +- .../image_decomposition/regular_policy.txx | 44 +++ .../frequency_resampler.h | 26 +- .../frequency_resampler.txx | 66 ++-- src/sirius/resampling/interpolator.cc | 98 +++++ src/sirius/resampling/interpolator.h | 39 ++ src/sirius/resampling/parameters.h | 52 +++ .../upsampling/periodization_upsampling.cc} | 21 +- .../upsampling/periodization_upsampling.h} | 23 +- .../upsampling/zero_padding_upsampling.cc} | 22 +- .../upsampling/zero_padding_upsampling.h} | 20 +- src/sirius/types.cc | 4 - src/sirius/types.h | 6 +- tests/CMakeLists.txt | 2 +- tests/filter_tests.cc | 60 ++-- tests/find-package-test/basic_sirius_test.cc | 10 +- tests/frequency_resampler_tests.cc | 138 +++---- tests/streamer_tests.cc | 71 ++++ tests/utils.cc | 13 + tests/utils.h | 2 + 50 files changed, 1520 insertions(+), 972 deletions(-) create mode 100644 src/sirius/gdal/i_input_stream.h rename src/sirius/{resampler/image_decomposition/regular_policy.txx => gdal/i_output_stream.h} (56%) rename src/sirius/{ => gdal}/image_streamer.h (63%) rename src/sirius/{image_streamer.cc => gdal/image_streamer.txx} (67%) create mode 100644 src/sirius/gdal/output_stream.cc rename src/sirius/gdal/{resampled_output_stream.h => output_stream.h} (58%) delete mode 100644 src/sirius/gdal/resampled_output_stream.cc create mode 100644 src/sirius/gdal/resampling/input_stream.cc create mode 100644 src/sirius/gdal/resampling/input_stream.h create mode 100644 src/sirius/gdal/resampling/output_stream.cc create mode 100644 src/sirius/gdal/resampling/output_stream.h rename src/sirius/{resampler => }/image_decomposition/periodic_smooth_policy.h (62%) rename src/sirius/{resampler => }/image_decomposition/periodic_smooth_policy.txx (59%) create mode 100644 src/sirius/image_decomposition/policies.h rename src/sirius/{resampler => }/image_decomposition/regular_policy.h (62%) create mode 100644 src/sirius/image_decomposition/regular_policy.txx rename src/sirius/{resampler => resampling}/frequency_resampler.h (69%) rename src/sirius/{resampler => resampling}/frequency_resampler.txx (70%) create mode 100644 src/sirius/resampling/interpolator.cc create mode 100644 src/sirius/resampling/interpolator.h create mode 100644 src/sirius/resampling/parameters.h rename src/sirius/{resampler/zoom_strategy/periodization_strategy.cc => resampling/upsampling/periodization_upsampling.cc} (91%) rename src/sirius/{resampler/zoom_strategy/periodization_strategy.h => resampling/upsampling/periodization_upsampling.h} (68%) rename src/sirius/{resampler/zoom_strategy/zero_padding_strategy.cc => resampling/upsampling/zero_padding_upsampling.cc} (86%) rename src/sirius/{resampler/zoom_strategy/zero_padding_strategy.h => resampling/upsampling/zero_padding_upsampling.h} (68%) create mode 100644 tests/streamer_tests.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b0711a4..97057fad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ set(SIRIUS_CXX_FLAGS "") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # GCC or Clang - set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} -Wall -Wextra") + set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} -Wall -Wextra -Werror") if (ENABLE_CLANG_LIBCPP) set(SIRIUS_CXX_FLAGS "${SIRIUS_CXX_FLAGS} -stdlib=libc++") endif() diff --git a/INTERNALS.md b/INTERNALS.md index 4dc772af..aa4f8384 100644 --- a/INTERNALS.md +++ b/INTERNALS.md @@ -90,24 +90,37 @@ C++11 standard guarantees that a unique instance will be initialized and that th The `FrequencyResampler` is the combination of two algorithms: * an image decomposition algorithm -* a zoom algorithm +* an upsampling algorithm Sirius provides an `IFrequencyResampler` factory to compose the resampler requested by a client. API clients should only deal with `IFrequencyResampler` interface and should not be concerned by `IFrequencyResampler` implementations. ### Policy/Strategy -`FrequencyResampler` is the composition of an image decomposition algorithm and a zoom algorithm. +`FrequencyResampler` is the composition of an image decomposition algorithm and an upsampling algorithm. The policy/strategy pattern is used to adapt the internal behavior of this class. -An algorithm implementation must comply with an an algorithm type implicit interface. +An algorithm implementation must comply with an implicit algorithm interface. -E.g, zoom algorithms should comply with: +E.g, processing algorithms (upsampling) should comply with: ```cpp -class ZoomStrategy { +class ProcessorStrategy { public: - Image Zoom(int zoom, const Image& padded_image, const Filter& filter) const; + Image Process(const Image& image, const transformation::Parameters& transformation_parameters) const; +}; +``` + +Interpolation algorithms should comply with: + +```cpp +template +class ImageDecompositionPolicy : private ImageProcessor, private ImageInterpolator { + public: + Image DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const; }; ``` diff --git a/README.md b/README.md index a12a8a85..c35c279d 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Sirius is using [CMake] to build its libraries and executables. * [spdlog v1.1.0] * [cxxopts v2.1.1] * [GSL v2.0.0] -* [catch v2.2.3] +* [catch v2.3.0] * [cmake-modules] ### Options @@ -210,7 +210,6 @@ Usage: no value is provided) streaming options: - --stream Enable stream mode --block-width arg Initial width of a stream block (default: 256) --block-height arg Initial height of a stream block (default: @@ -221,30 +220,15 @@ Usage: (8 max) (default: 1) ``` -#### Processing mode options +#### Stream mode -##### Regular mode - -Regular mode (default mode) will put the whole image in memory and then processed it. **This mode should only be used on small image**. - -The following command line will zoom in the image `/path/to/input-file.tif` by 4/3 with the periodic plus smooth image decomposition, apply the filter `/path/to/filter-image-4-3.tif` to the zoomed image and write the result into `/path/to/output-file.tif`. - - -```sh -./sirius -r 4:3 \ - --filter /path/to/filter-image-4-3.tif \ - /path/to/input-file.tif /path/to/output-file.tif -``` - -##### Stream mode - -Stream mode is activated with the option `--stream`. It will cut the image into multiple blocks of small size (default block size is 256x256). Each block will be processed separately and result blocks will be aggregated to generate the output image. **This mode must be used on large image.** +Input image will be split into multiple blocks of small size (default block size is 256x256). Each block will be processed separately and result blocks will be aggregated to generate the output image. Stream mode can be run in mono-threaded context (`--parallel-workers=1`) or in multi-threaded context (`--parallel-workers=N` where N is the requested number of threads which will compute the resampling). ```sh -./sirius -r 4:3 \ - --stream --parallel-workers=4 \ +./sirius --parallel-workers=4 \ + -r 4:3 \ --filter /path/to/filter-image-4-3.tif \ /path/to/input-file.tif /path/to/output-file.tif ``` @@ -313,10 +297,10 @@ The following command line will zoom in `input/lena.jpg` by 2 using periodic plu data/input/lena.jpg /tmp/lena_z2.jpg ``` -The following command line will zoom in `input/sentinel2_20m.tif` by 2 using stream mode and 8 workers, periodic plus smooth image decomposition, periodization upsampling and filter for upsampling 2. +The following command line will zoom in `input/sentinel2_20m.tif` by 2 using 8 workers, periodic plus smooth image decomposition, periodization upsampling and filter for upsampling 2. ```sh -./sirius --stream --parallel-workers=8 \ +./sirius --parallel-workers=8 \ -r 2 \ --filter data/filters/ZOOM_2.tif \ data/input/sentinel2_20m.tif /tmp/sentinel2_20m_z2.tif @@ -340,10 +324,10 @@ The following command line will zoom out `input/disparity.png` by 1/2 using peri data/input/disparity.png /tmp/disparity_z1_2.jpg ``` -The following command line will zoom out `input/sentinel2_10m.tif` by 1/2 using using stream mode and 8 workers, periodic plus smooth image decomposition and filter for downsampling 1/2. +The following command line will zoom out `input/sentinel2_10m.tif` by 1/2 using 8 workers, periodic plus smooth image decomposition and filter for downsampling 1/2. ```sh -./sirius --stream --parallel-workers=8 \ +./sirius --parallel-workers=8 \ -r 1:2 \ --filter data/filters/ZOOM_1_2.tif \ data/input/sentinel2_10m.tif /tmp/sentinel2_10m_z1_2.tif @@ -357,39 +341,42 @@ The main interface to compute a frequency resampling is `IFrequencyResampler` an `IFrequencyResampler` objects are instantiated by the `FrequencyResamplerFactory`. -#### Example without filter +#### Resampling + +##### Example without filter ```cpp -#include "sirius/filter.h" #include "sirius/frequency_resampler_factory.h" #include "sirius/image.h" #include "sirius/types.h" +#include "sirius/resampling/parameters.h" // create an image sirius::Image image = {...}; -// configure the zoom ratio -sirius::ZoomRatio zoom_ratio = sirius::ZoomRatio::Create(7, 5); +// configure resampling parameters +sirius::resampling::Parameters resampling_params{sirius::ZoomRatio::Create(7, 5)}; // compose a frequency resampler from sirius::ImageDecompositionPolicies and -// sirius::FrequencyZoomStrategies enums +// sirius::FrequencyUpsamplingStrategies enums sirius::IFrequencyResampler::UPtr freq_resampler = sirius::FrequencyResamplerFactory::Create( sirius::ImageDecompositionPolicies::kPeriodicSmooth, - sirius::FrequencyZoomStrategies::kZeroPadding); + sirius::FrequencyUpsamplingStrategies::kZeroPadding); // compute the resampled image sirius::Image resampled_image = freq_resampler->Compute( - zoom_ratio, image, {}, {}); + image, {}, resampling_params); ``` -#### Example with filter +##### Example with filter ```cpp #include "sirius/filter.h" #include "sirius/frequency_resampler_factory.h" #include "sirius/image.h" #include "sirius/types.h" +#include "sirius/resampling/parameters.h" // load an image sirius::Image image = {...}; @@ -401,21 +388,25 @@ sirius::Image filter_image = {...}; sirius::ZoomRatio zoom_ratio = sirius::ZoomRatio::Create(7, 5); // create a filter from an image file -sirius::Filter filter = sirius::Filter::Create(filter_image, zoom_ratio); +sirius::Filter::UPtr filter = sirius::Filter::Create(filter_image, zoom_ratio); -// compose a frequency resampler from sirius::ImageDecompositionPolicies and -// sirius::FrequencyZoomStrategies enums +// configure resampling parameters +sirius::resampling::Parameters resampling_params{zoom_ratio, + filter.get()}; + +// compose a frequency resampler from sirius::image_decomposition::Policies and +// sirius::FrequencyUpsamplingStrategies enums sirius::IFrequencyResampler::UPtr freq_resampler = sirius::FrequencyResamplerFactory::Create( - sirius::ImageDecompositionPolicies::kPeriodicSmooth, - sirius::FrequencyZoomStrategies::kPeriodization); + sirius::image_decomposition::Policies::kPeriodicSmooth, + sirius::FrequencyUpsamplingStrategies::kPeriodization); // compute the resampled image sirius::Image resampled_image = freq_resampler->Compute( - zoom_ratio, image, filter.padding(), filter); + image, filter->padding(), resampling_params); ``` -#### Thread safety +##### Thread safety Compute a resampled image with Sirius is thread safe so it is possible to use the same `IFrequencyResampler` object in a multi-threaded context. @@ -423,16 +414,9 @@ Process an image with a `Filter` object is also thread safe so you can reuse the ## Unit tests -Running tests requires data features (input image, filters) which are available [here][Sirius test data features]. - -You need to execute the tests in the root directory of those data features. Expected directory tree is: -``` -ROOT_DATA_FEATURES/input - /filters - /output -``` +Unit tests can be enabled by providing the [CMake] option `-DBUILD_TESTING=ON`. -`frequency_resampler_tests` and `functional_tests` will create output images in the directory `ROOT_DATA_FEATURES/output` +To run the tests, build the target `build_tests` and run `ctest -C BUILD_TYPE` where `BUILD_TYPE` is `Debug`, `RelWithDebInfo`, `Release` or `MinSizeRel`. ## Acknowledgement diff --git a/cmake/FindSIRIUS.cmake b/cmake/FindSIRIUS.cmake index abeeaf6e..a905d950 100644 --- a/cmake/FindSIRIUS.cmake +++ b/cmake/FindSIRIUS.cmake @@ -148,4 +148,3 @@ if (SIRIUS_FOUND) endif() message(STATUS "find_package(SIRIUS) provides CMake targets: SIRIUS::libsirius and SIRIUS::libsirius-static") endif () - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4dedb20..57581438 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,30 +91,32 @@ add_library(libsirius-objects OBJECT EXCLUDE_FROM_ALL sirius/types.h sirius/types.cc - sirius/image.h - sirius/image.cc sirius/filter.h sirius/filter.cc + sirius/image.h + sirius/image.cc sirius/i_frequency_resampler.h sirius/frequency_resampler_factory.h sirius/frequency_resampler_factory.cc - # resampler - sirius/resampler/frequency_resampler.h - sirius/resampler/frequency_resampler.txx + # image_decomposition + sirius/image_decomposition/periodic_smooth_policy.h + sirius/image_decomposition/periodic_smooth_policy.txx + sirius/image_decomposition/regular_policy.h + sirius/image_decomposition/regular_policy.txx - # resampler zoom strategies - sirius/resampler/zoom_strategy/periodization_strategy.h - sirius/resampler/zoom_strategy/periodization_strategy.cc - sirius/resampler/zoom_strategy/zero_padding_strategy.h - sirius/resampler/zoom_strategy/zero_padding_strategy.cc + # resampling + sirius/resampling/frequency_resampler.h + sirius/resampling/frequency_resampler.txx + sirius/resampling/interpolator.h + sirius/resampling/interpolator.cc - # resampler image decomposition policies - sirius/resampler/image_decomposition/regular_policy.h - sirius/resampler/image_decomposition/regular_policy.txx - sirius/resampler/image_decomposition/periodic_smooth_policy.h - sirius/resampler/image_decomposition/periodic_smooth_policy.txx + # resampling upsampling strategies + sirius/resampling/upsampling/periodization_upsampling.h + sirius/resampling/upsampling/periodization_upsampling.cc + sirius/resampling/upsampling/zero_padding_upsampling.h + sirius/resampling/upsampling/zero_padding_upsampling.cc # fftw sirius/fftw/exception.h @@ -213,32 +215,31 @@ if (ENABLE_SIRIUS_EXECUTABLE OR BUILD_TESTING) add_library(libsirius-gdal-static STATIC EXCLUDE_FROM_ALL # gdal - sirius/gdal/debug.h - sirius/gdal/debug.cc sirius/gdal/error_code.h sirius/gdal/error_code.cc sirius/gdal/exception.h sirius/gdal/exception.cc - sirius/gdal/stream_block.h + sirius/gdal/image_streamer.h + sirius/gdal/image_streamer.txx sirius/gdal/input_stream.h sirius/gdal/input_stream.cc - sirius/gdal/resampled_output_stream.h - sirius/gdal/resampled_output_stream.cc + sirius/gdal/output_stream.h + sirius/gdal/output_stream.cc sirius/gdal/types.h sirius/gdal/wrapper.h - sirius/gdal/wrapper.cc) + sirius/gdal/wrapper.cc + + # gdal/resampling + sirius/gdal/resampling/input_stream.h + sirius/gdal/resampling/input_stream.cc + sirius/gdal/resampling/output_stream.h + sirius/gdal/resampling/output_stream.cc) set_target_properties(libsirius-gdal-static PROPERTIES FOLDER lib) target_link_libraries(libsirius-gdal-static PUBLIC gdal libsirius-static) endif () if (ENABLE_SIRIUS_EXECUTABLE) - add_executable(sirius - # main - sirius/image_streamer.h - sirius/image_streamer.cc - - main.cc) - + add_executable(sirius main.cc) set_target_properties(sirius PROPERTIES FOLDER bin DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) diff --git a/src/main.cc b/src/main.cc index 06100a05..43cdb442 100644 --- a/src/main.cc +++ b/src/main.cc @@ -32,19 +32,33 @@ #include "sirius/exception.h" #include "sirius/frequency_resampler_factory.h" -#include "sirius/image_streamer.h" #include "sirius/sirius.h" +#include "sirius/gdal/image_streamer.h" +#include "sirius/gdal/resampling/input_stream.h" +#include "sirius/gdal/resampling/output_stream.h" #include "sirius/gdal/wrapper.h" #include "sirius/utils/log.h" -#include "sirius/utils/numeric.h" -struct CliParameters { - // status - bool parsed = false; - bool help_requested = false; - std::string help_message; +struct CliOptions { + struct Resampling { + std::string ratio = "1:1"; + bool no_image_decomposition = false; + bool upsample_periodization = false; + bool upsample_zero_padding = false; + }; + struct Filter { + std::string path; + bool zero_pad_real_edges = false; + bool normalize = false; + sirius::Point hot_point = sirius::filter_default_hot_point; + }; + struct Stream { + sirius::Size block_size = {256, 256}; + bool no_block_resizing = false; + unsigned int parallel_workers = std::thread::hardware_concurrency(); + }; // mandatory arguments std::string input_image_path; @@ -53,197 +67,144 @@ struct CliParameters { // general options std::string verbosity_level = "info"; - // resampling options - std::string resampling_ratio = "1:1"; - bool no_image_decomposition = false; - bool upsample_periodization = false; - bool upsample_zero_padding = false; - - // filter options - std::string filter_path; - bool zero_pad_real_edges = false; - - // stream mode options - bool stream_mode = false; - int stream_block_height = 256; - int stream_block_width = 256; - bool stream_no_block_resizing = false; - bool filter_normalize = false; - int hot_point_x = -1; - int hot_point_y = -1; - unsigned int stream_parallel_workers = std::thread::hardware_concurrency(); - - bool HasStreamMode() const { - return stream_mode && stream_block_height > 0 && stream_block_width > 0; - } + Resampling resampling; + Filter filter; + Stream stream; - sirius::Size GetStreamBlockSize() const { - return {stream_block_height, stream_block_width}; - } + // status + bool parsed = false; + bool help_requested = false; + std::string help_message; }; -CliParameters GetCliParameters(int argc, char* argv[]); -void RunRegularMode(const sirius::IFrequencyResampler& frequency_resampler, - const sirius::Filter& filter, - const sirius::ZoomRatio& zoom_ratio, - const CliParameters& params); -void RunStreamMode(const sirius::IFrequencyResampler& frequency_resampler, - const sirius::Filter& filter, - const sirius::ZoomRatio& zoom_ratio, - const CliParameters& params); +template +void StreamTransformation(const CliOptions& cli_options, + const Transformer& transformer, + const typename Transformer::Parameters& parameters); +CliOptions GetCliOptions(int argc, char* argv[]); +int Resample(const CliOptions& options); int main(int argc, char* argv[]) { - CliParameters params = GetCliParameters(argc, argv); - if (params.help_requested || !params.parsed) { - std::cerr << params.help_message; - return params.help_requested ? 0 : 1; + CliOptions options = GetCliOptions(argc, argv); + if (options.help_requested || !options.parsed) { + std::cerr << options.help_message; + return options.help_requested ? 0 : 1; } - if (params.input_image_path.empty() || params.output_image_path.empty()) { - std::cerr << params.help_message << std::endl; + if (options.input_image_path.empty() || options.output_image_path.empty()) { + std::cerr << options.help_message << std::endl; std::cerr << "sirius: input and/or output arguments are missing" << std::endl; return 1; } - sirius::utils::SetVerbosityLevel(params.verbosity_level); + sirius::utils::SetVerbosityLevel(options.verbosity_level); LOG("sirius", info, "Sirius {} - {}", sirius::kVersion, sirius::kGitCommit); try { - auto zoom_ratio = sirius::ZoomRatio::Create(params.resampling_ratio); - LOG("sirius", info, "resampling ratio: {}:{}", - zoom_ratio.input_resolution(), zoom_ratio.output_resolution()); - - // filter parameters - sirius::PaddingType padding_type = sirius::PaddingType::kMirrorPadding; - if (params.zero_pad_real_edges) { - LOG("sirius", info, "filter: border zero padding"); - padding_type = sirius::PaddingType::kZeroPadding; - } else { - LOG("sirius", info, "filter: mirror padding"); - } - - if (params.filter_normalize) { - LOG("sirius", info, "filter: normalize"); - } + return Resample(options); + } catch (const std::exception& e) { + std::cerr << "sirius: exception while computing resampling: " + << e.what() << std::endl; + return 1; + } +} - sirius::Filter filter; - if (!params.filter_path.empty()) { - LOG("sirius", info, "filter path: {}", params.filter_path); - sirius::Point hp(params.hot_point_x, params.hot_point_y); - filter = sirius::Filter::Create( - sirius::gdal::Load(params.filter_path), zoom_ratio, hp, - padding_type, params.filter_normalize); - } +int Resample(const CliOptions& options) { + sirius::ZoomRatio resampling_ratio = + sirius::ZoomRatio::Create(options.resampling.ratio); + LOG("sirius", info, "resampling ratio: {}:{}", + resampling_ratio.input_resolution(), + resampling_ratio.output_resolution()); + + // filter parameters + sirius::PaddingType padding_type = sirius::PaddingType::kMirrorPadding; + if (options.filter.zero_pad_real_edges) { + LOG("sirius", info, "filter: border zero padding"); + padding_type = sirius::PaddingType::kZeroPadding; + } else { + LOG("sirius", info, "filter: mirror padding"); + } - // resampling parameters - sirius::ImageDecompositionPolicies image_decomposition_policy = - sirius::ImageDecompositionPolicies::kPeriodicSmooth; - sirius::FrequencyZoomStrategies zoom_strategy = - sirius::FrequencyZoomStrategies::kPeriodization; + if (options.filter.normalize) { + LOG("sirius", info, "filter: normalize"); + } - if (params.no_image_decomposition) { - LOG("sirius", info, "image decomposition: none"); - image_decomposition_policy = - sirius::ImageDecompositionPolicies::kRegular; - } else { - LOG("sirius", info, "image decomposition: periodic plus smooth"); - } + sirius::Filter::UPtr filter; + if (!options.filter.path.empty()) { + LOG("sirius", info, "filter path: {}", options.filter.path); + sirius::Point hp(options.filter.hot_point.x, + options.filter.hot_point.y); + filter = sirius::Filter::Create(sirius::gdal::Load(options.filter.path), + resampling_ratio, hp, padding_type, + options.filter.normalize); + } - if (zoom_ratio.ratio() > 1) { - // choose the upsampling algorithm only if ratio > 1 - if (params.upsample_periodization && !filter.IsLoaded()) { - LOG("sirius", error, - "filter is required with periodization upsampling"); - return 1; - } else if (params.upsample_zero_padding || !filter.IsLoaded()) { - LOG("sirius", info, "upsampling: zero padding"); - zoom_strategy = sirius::FrequencyZoomStrategies::kZeroPadding; - if (filter.IsLoaded()) { - LOG("sirius", warn, - "filter will be used with zero padding upsampling"); - } - } else { - LOG("sirius", info, "upsampling: periodization"); - zoom_strategy = sirius::FrequencyZoomStrategies::kPeriodization; - } - } + // resampling parameters + sirius::image_decomposition::Policies image_decomposition_policy = + sirius::image_decomposition::Policies::kPeriodicSmooth; + sirius::FrequencyUpsamplingStrategies upsampling_strategy = + sirius::FrequencyUpsamplingStrategies::kPeriodization; - auto frequency_resampler = sirius::FrequencyResamplerFactory::Create( - image_decomposition_policy, zoom_strategy); + if (options.resampling.no_image_decomposition) { + LOG("sirius", info, "image decomposition: none"); + image_decomposition_policy = + sirius::image_decomposition::Policies::kRegular; + } else { + LOG("sirius", info, "image decomposition: periodic plus smooth"); + } - if (!params.HasStreamMode()) { - RunRegularMode(*frequency_resampler, filter, zoom_ratio, params); + if (resampling_ratio.ratio() > 1) { + // choose the upsampling algorithm only if ratio > 1 + if (options.resampling.upsample_periodization && !filter) { + LOG("sirius", error, + "filter is required with periodization upsampling"); + return 1; + } else if (options.resampling.upsample_zero_padding || !filter) { + LOG("sirius", info, "upsampling: zero padding"); + upsampling_strategy = + sirius::FrequencyUpsamplingStrategies::kZeroPadding; + if (filter) { + LOG("sirius", warn, + "filter will be used with zero padding upsampling"); + } } else { - RunStreamMode(*frequency_resampler, filter, zoom_ratio, params); + LOG("sirius", info, "upsampling: periodization"); + upsampling_strategy = + sirius::FrequencyUpsamplingStrategies::kPeriodization; } - } catch (const std::exception& e) { - std::cerr << "sirius: exception while computing resampling: " - << e.what() << std::endl; - return 1; } - return 0; -} + auto frequency_resampler = sirius::FrequencyResamplerFactory::Create( + image_decomposition_policy, upsampling_strategy); + + StreamTransformation( + options, *frequency_resampler, {resampling_ratio, filter.get()}); -void RunRegularMode(const sirius::IFrequencyResampler& frequency_resampler, - const sirius::Filter& filter, - const sirius::ZoomRatio& zoom_ratio, - const CliParameters& params) { - LOG("sirius", info, "regular mode"); - auto input_image = sirius::gdal::Load(params.input_image_path); - LOG("sirius", info, "input image '{}' ({}x{})", params.input_image_path, - input_image.size.row, input_image.size.col); - - auto resampled_geo_ref = sirius::gdal::ComputeResampledGeoReference( - params.input_image_path, zoom_ratio); - - auto resampled_image = frequency_resampler.Compute( - zoom_ratio, input_image, filter.padding(), filter); - LOG("sirius", info, "resampled image '{}' ({}x{})", - params.output_image_path, resampled_image.size.row, - resampled_image.size.col); - sirius::gdal::Save(resampled_image, params.output_image_path, - resampled_geo_ref); + return 0; } -void RunStreamMode(const sirius::IFrequencyResampler& frequency_resampler, - const sirius::Filter& filter, - const sirius::ZoomRatio& zoom_ratio, - const CliParameters& params) { - LOG("sirius", info, "streaming mode"); +template +void StreamTransformation(const CliOptions& options, + const Transformer& transformer, + const typename Transformer::Parameters& parameters) { unsigned int max_parallel_workers = - std::max(std::min(params.stream_parallel_workers, + std::max(std::min(options.stream.parallel_workers, std::thread::hardware_concurrency()), 1u); - auto stream_block_size = params.GetStreamBlockSize(); - - // improve stream_block_size if requested or required - if (!zoom_ratio.IsRealZoom()) { - if (!params.stream_no_block_resizing) { - stream_block_size = sirius::utils::GenerateDyadicSize( - stream_block_size, zoom_ratio.input_resolution(), - filter.padding_size()); - LOG("sirius", warn, "stream block resized to dyadic size: {}x{}", - stream_block_size.row, stream_block_size.col); - } - } else { - // real zoom needs specific block size (row and col should be multiple - // of input resolution and output resolution) - stream_block_size = sirius::utils::GenerateZoomCompliantSize( - stream_block_size, zoom_ratio); - LOG("sirius", warn, "stream block resized to comply with zoom: {}x{}", - stream_block_size.row, stream_block_size.col); - } - sirius::ImageStreamer streamer( - params.input_image_path, params.output_image_path, stream_block_size, - zoom_ratio, filter.Metadata(), max_parallel_workers); - streamer.Stream(frequency_resampler, filter); + + sirius::gdal::ImageStreamer + streamer(options.input_image_path, options.output_image_path, + options.stream.block_size, !options.stream.no_block_resizing, + parameters, max_parallel_workers); + streamer.Stream(transformer, parameters); } -CliParameters GetCliParameters(int argc, char* argv[]) { - CliParameters params; +CliOptions GetCliOptions(int argc, char* argv[]) { + CliOptions cli_options; std::stringstream description; description << "Sirius " << sirius::kVersion << " (" << sirius::kGitCommit << ")" << std::endl @@ -265,85 +226,83 @@ CliParameters GetCliParameters(int argc, char* argv[]) { ("h,help", "Show help") ("v,verbosity", "Set verbosity level (trace,debug,info,warn,err,critical,off)", - cxxopts::value(params.verbosity_level)->default_value("info")); + cxxopts::value(cli_options.verbosity_level)->default_value("info")); options.add_options("resampling") ("r,resampling-ratio", "Resampling ratio as input:output, " "allowed format: I (equivalent to I:1), I:O", - cxxopts::value(params.resampling_ratio)->default_value("1:1")) + cxxopts::value(cli_options.resampling.ratio)->default_value("1:1")) ("no-image-decomposition", "Do not decompose the input image " "(default: periodic plus smooth image decomposition)", - cxxopts::value(params.no_image_decomposition)) + cxxopts::value(cli_options.resampling.no_image_decomposition)) ("upsample-periodization", "Force periodization as upsampling algorithm " "(default algorithm if a filter is provided). " "A filter is required to use this algorithm", - cxxopts::value(params.upsample_periodization)) + cxxopts::value(cli_options.resampling.upsample_periodization)) ("upsample-zero-padding", "Force zero padding as upsampling algorithm " "(default algorithm if no filter is provided)", - cxxopts::value(params.upsample_zero_padding)); + cxxopts::value(cli_options.resampling.upsample_zero_padding)); options.add_options("filter") ("filter", "Path to the filter image to apply to the source or resampled image", - cxxopts::value(params.filter_path)) + cxxopts::value(cli_options.filter.path)) ("filter-normalize", "Normalize filter coefficients " "(default is no normalization)", - cxxopts::value(params.filter_normalize)) + cxxopts::value(cli_options.filter.normalize)) ("zero-pad-real-edges", "Force zero padding strategy on real input edges " "(default: mirror padding)", - cxxopts::value(params.zero_pad_real_edges)) + cxxopts::value(cli_options.filter.zero_pad_real_edges)) ("hot-point-x", "Hot point x coordinate " "(considered centered if no value is provided)", - cxxopts::value(params.hot_point_x)) + cxxopts::value(cli_options.filter.hot_point.x)) ("hot-point-y", "Hot point y coordinate " "(considered centered if no value is provided)", - cxxopts::value(params.hot_point_y)); + cxxopts::value(cli_options.filter.hot_point.y)); options.add_options("streaming") - ("stream", "Enable stream mode", - cxxopts::value(params.stream_mode)) ("block-width", "Initial width of a stream block", - cxxopts::value(params.stream_block_width)->default_value("256")) + cxxopts::value(cli_options.stream.block_size.col)->default_value("256")) ("block-height", "Initial height of a stream block", - cxxopts::value(params.stream_block_height)->default_value("256")) + cxxopts::value(cli_options.stream.block_size.row)->default_value("256")) ("no-block-resizing", "Disable block resizing optimization", - cxxopts::value(params.stream_no_block_resizing)) + cxxopts::value(cli_options.stream.no_block_resizing)) ("parallel-workers", stream_parallel_workers_desc.str(), - cxxopts::value(params.stream_parallel_workers) + cxxopts::value(cli_options.stream.parallel_workers) ->default_value("1") ->implicit_value("1")); options.add_options("positional arguments") - ("i,input", "Input image", cxxopts::value(params.input_image_path)) - ("o,output", "Output image", cxxopts::value(params.output_image_path)); + ("i,input", "Input image", cxxopts::value(cli_options.input_image_path)) + ("o,output", "Output image", cxxopts::value(cli_options.output_image_path)); // clang-format on options.parse_positional({"input", "output"}); - params.help_message = + cli_options.help_message = options.help({"", "resampling", "filter", "streaming"}); try { auto result = options.parse(argc, argv); - params.parsed = true; + cli_options.parsed = true; if (result.count("help")) { - params.help_requested = true; - return params; + cli_options.help_requested = true; + return cli_options; } } catch (const std::exception& e) { std::cerr << "sirius: cannot parse command line: " << e.what() << std::endl; - return params; + return cli_options; } - params.parsed = true; - return params; + cli_options.parsed = true; + return cli_options; } diff --git a/src/sirius/filter.cc b/src/sirius/filter.cc index a998f2a0..40c1b9a6 100644 --- a/src/sirius/filter.cc +++ b/src/sirius/filter.cc @@ -47,9 +47,9 @@ Image FrequencyShift(const Image& filter_image, const Point& hot_point, Image CenterFilterImage(const Image& filter_image, const Point& hot_point); -Filter Filter::Create(Image filter_image, const ZoomRatio& zoom_ratio, - const Point& hot_point, PaddingType padding_type, - bool normalize) { +Filter::UPtr Filter::Create(Image filter_image, const ZoomRatio& zoom_ratio, + const Point& hot_point, PaddingType padding_type, + bool normalize) { if (hot_point.x < -1 || hot_point.x >= filter_image.size.col || hot_point.y < -1 || hot_point.y >= filter_image.size.row) { LOG("filter", error, "Invalid hot point with coordinates {}, {}", @@ -93,10 +93,6 @@ Filter::Filter(Image&& filter_image, const Size& padding_size, fftw::ComplexUPtr Filter::Process(const Size& image_size, fftw::ComplexUPtr image_fft) const { - if (!IsLoaded()) { - return image_fft; - } - if (image_size.row < filter_.size.row || image_size.col < filter_.size.col) { LOG("filter", error, @@ -175,10 +171,10 @@ fftw::ComplexUPtr Filter::CreateFilterFFT(const Size& image_size) const { return fftw::FFT(shifted_values.get(), image_size); } -Filter Filter::CreateZoomOutFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point) { +Filter::UPtr Filter::CreateZoomOutFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point) { LOG("filter", info, "filter: downsampling"); if (hot_point.x != -1 && hot_point.y != -1) { filter_image = CenterFilterImage(filter_image, hot_point); @@ -191,17 +187,15 @@ Filter Filter::CreateZoomOutFilter(Image filter_image, ? (filter_image.size.col / 2) : (filter_image.size.col - 1) / 2; - return {std::move(filter_image), - {padding_row, padding_col}, - zoom_ratio, - padding_type, - hot_point}; + return std::make_unique(std::move(filter_image), + Size{padding_row, padding_col}, zoom_ratio, + padding_type, hot_point); } -Filter Filter::CreateZoomInFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point) { +Filter::UPtr Filter::CreateZoomInFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point) { LOG("filter", info, "filter: upsampling"); if (hot_point.x != -1 && hot_point.y != -1) { filter_image = CenterFilterImage(filter_image, hot_point); @@ -228,17 +222,15 @@ Filter Filter::CreateZoomInFilter(Image filter_image, static_cast(zoom_ratio.input_resolution()))); } - return {std::move(filter_image), - {padding_row, padding_col}, - zoom_ratio, - padding_type, - hot_point}; + return std::make_unique(std::move(filter_image), + Size{padding_row, padding_col}, zoom_ratio, + padding_type, hot_point); } -Filter Filter::CreateRealZoomFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point) { +Filter::UPtr Filter::CreateRealZoomFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point) { LOG("filter", info, "filter: float upsampling factor (upsample filter to input " "resolution)"); @@ -263,31 +255,28 @@ Filter Filter::CreateRealZoomFilter(Image filter_image, padding_col = static_cast(filter_image.size.col / 2. * (1. / zoom_ratio.input_resolution())); } - return {std::move(filter_image), - {padding_row, padding_col}, - zoom_ratio, - padding_type, - hot_point}; + return std::make_unique(std::move(filter_image), + Size{padding_row, padding_col}, zoom_ratio, + padding_type, hot_point); } Image ZoomFilterImageToInputResolution(const Image& filter_image, const ZoomRatio& zoom_ratio) { LOG("filter", trace, "zoom filter to input resolution"); - ImageDecompositionPolicies image_decomposition_policy = - ImageDecompositionPolicies::kRegular; - FrequencyZoomStrategies zoom_strategy = - FrequencyZoomStrategies::kZeroPadding; + image_decomposition::Policies image_decomposition_policy = + image_decomposition::Policies::kRegular; + FrequencyUpsamplingStrategies zoom_strategy = + FrequencyUpsamplingStrategies::kZeroPadding; auto frequency_resampler = FrequencyResamplerFactory::Create( image_decomposition_policy, zoom_strategy); - auto new_zoom_ratio = ZoomRatio::Create(zoom_ratio.output_resolution(), 1); - Image shifted_filter(filter_image.size); utils::IFFTShift2D(filter_image.data.data(), filter_image.size, shifted_filter.data.data()); auto zoomed_filter = frequency_resampler->Compute( - new_zoom_ratio, shifted_filter, {0, 0, 0, 0}, {}); + shifted_filter, {0, 0, 0, 0}, + {ZoomRatio::Create(zoom_ratio.output_resolution(), 1)}); Image unshifted_zoomed_filter(zoomed_filter.size); // centered FFTShift so filter's hot point remains centered diff --git a/src/sirius/filter.h b/src/sirius/filter.h index f816efc8..412673a3 100644 --- a/src/sirius/filter.h +++ b/src/sirius/filter.h @@ -55,23 +55,36 @@ class Filter { using FilterFFTCache = utils::LRUCache; using FilterFFTCacheUPtr = std::unique_ptr; + public: + using UPtr = std::unique_ptr; + public: /** * \brief Filter which is adapted specifically for a particular zoom ratio * \param filter_image image of the filter * \param zoom_ratio ratio on which the filter must be applied - * \param hot_point hot point coordinates + * \param hot_point hot point of the filter * \param padding_type padding type * \param normalize normalize filter * * \throw sirius::Exception if the filter image cannot be loaded */ - static Filter Create(Image filter_image, const ZoomRatio& zoom_ratio, - const Point& hot_point = filter_default_hot_point, - PaddingType padding_type = PaddingType::kMirrorPadding, - bool normalize = false); + static UPtr Create(Image filter_image, const ZoomRatio& zoom_ratio, + const Point& hot_point = filter_default_hot_point, + PaddingType padding_type = PaddingType::kMirrorPadding, + bool normalize = false); - Filter() = default; + /** + * \brief Instanciate a filter + * \param filter_image image of the filter + * \param zoom_ratio ratio on which the filter must be applied + * \param hot_point hot point of the filter + * \param padding_type padding type + * \param normalize normalize filter + */ + Filter(Image&& filter_image, const Size& padding_size, + const ZoomRatio& zoom_ratio, PaddingType padding_type, + const Point& hot_point); ~Filter() = default; @@ -82,12 +95,6 @@ class Filter { Filter(Filter&&) = default; Filter& operator=(Filter&&) = default; - /** - * \brief Filter is loaded and ready to be applied on an image FFT - * \return bool - */ - bool IsLoaded() const { return filter_.IsLoaded(); } - /** * \brief Filter image size * \return Size @@ -121,16 +128,6 @@ class Filter { const Point& hot_point() const { return hot_point_; } - /** - * \brief Check that the filter can be applied on the given zoom ratio - * \param zoom_ratio - * \return bool - */ - bool CanBeApplied(const ZoomRatio& zoom_ratio) const { - return (zoom_ratio_.input_resolution() == - zoom_ratio.input_resolution()); - } - /** * \brief Apply the filter on the image_fft * @@ -146,22 +143,18 @@ class Filter { fftw::ComplexUPtr image_fft) const; private: - static Filter CreateZoomInFilter(Image filter_image, + static UPtr CreateZoomInFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point); + static UPtr CreateZoomOutFilter(Image filter_image, + const ZoomRatio& zoom_ratio, + PaddingType padding_type, + const Point& hot_point); + static UPtr CreateRealZoomFilter(Image filter_image, const ZoomRatio& zoom_ratio, PaddingType padding_type, const Point& hot_point); - static Filter CreateZoomOutFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point); - static Filter CreateRealZoomFilter(Image filter_image, - const ZoomRatio& zoom_ratio, - PaddingType padding_type, - const Point& hot_point); - - Filter(Image&& filter_image, const Size& padding_size, - const ZoomRatio& zoom_ratio, PaddingType padding_type, - const Point& hot_point); fftw::ComplexUPtr CreateFilterFFT(const Size& image_size) const; diff --git a/src/sirius/frequency_resampler_factory.cc b/src/sirius/frequency_resampler_factory.cc index d70d259c..aa1265f6 100644 --- a/src/sirius/frequency_resampler_factory.cc +++ b/src/sirius/frequency_resampler_factory.cc @@ -21,57 +21,57 @@ #include "sirius/frequency_resampler_factory.h" -#include "sirius/utils/log.h" +#include "sirius/image_decomposition/periodic_smooth_policy.h" +#include "sirius/image_decomposition/regular_policy.h" + +#include "sirius/resampling/frequency_resampler.h" +#include "sirius/resampling/upsampling/periodization_upsampling.h" +#include "sirius/resampling/upsampling/zero_padding_upsampling.h" -#include "sirius/resampler/frequency_resampler.h" -#include "sirius/resampler/image_decomposition/periodic_smooth_policy.h" -#include "sirius/resampler/image_decomposition/regular_policy.h" -#include "sirius/resampler/zoom_strategy/periodization_strategy.h" -#include "sirius/resampler/zoom_strategy/zero_padding_strategy.h" +#include "sirius/utils/log.h" namespace sirius { IFrequencyResampler::UPtr FrequencyResamplerFactory::Create( - ImageDecompositionPolicies image_decomposition, - FrequencyZoomStrategies zoom_strategy) { - using FrequencyResamplerRegularZeroPadding = resampler::FrequencyResampler< - resampler::ImageDecompositionRegularPolicy, - resampler::ZeroPaddingZoomStrategy>; + image_decomposition::Policies image_decomposition, + FrequencyUpsamplingStrategies zoom_strategy) { + using FrequencyResamplerRegularZeroPadding = + resampling::FrequencyResampler; using FrequencyResamplerRegularPeriodization = - resampler::FrequencyResampler< - resampler::ImageDecompositionRegularPolicy, - resampler::PeriodizationZoomStrategy>; + resampling::FrequencyResampler; using FrequencyResamplerPeriodicSmoothZeroPadding = - resampler::FrequencyResampler< - resampler::ImageDecompositionPeriodicSmoothPolicy, - resampler::ZeroPaddingZoomStrategy>; + resampling::FrequencyResampler< + image_decomposition::PeriodicSmoothPolicy, + resampling::ZeroPaddingUpsampling>; using FrequencyResamplerPeriodicSmoothPeriodization = - resampler::FrequencyResampler< - resampler::ImageDecompositionPeriodicSmoothPolicy, - resampler::PeriodizationZoomStrategy>; + resampling::FrequencyResampler< + image_decomposition::PeriodicSmoothPolicy, + resampling::PeriodizationUpsampling>; switch (image_decomposition) { - case ImageDecompositionPolicies::kRegular: + case image_decomposition::Policies::kRegular: switch (zoom_strategy) { - case FrequencyZoomStrategies::kZeroPadding: + case FrequencyUpsamplingStrategies::kZeroPadding: return std::make_unique< FrequencyResamplerRegularZeroPadding>(); - case FrequencyZoomStrategies::kPeriodization: + case FrequencyUpsamplingStrategies::kPeriodization: return std::make_unique< FrequencyResamplerRegularPeriodization>(); default: break; } break; - case ImageDecompositionPolicies::kPeriodicSmooth: + case image_decomposition::Policies::kPeriodicSmooth: switch (zoom_strategy) { - case FrequencyZoomStrategies::kZeroPadding: + case FrequencyUpsamplingStrategies::kZeroPadding: return std::make_unique< FrequencyResamplerPeriodicSmoothZeroPadding>(); - case FrequencyZoomStrategies::kPeriodization: + case FrequencyUpsamplingStrategies::kPeriodization: return std::make_unique< FrequencyResamplerPeriodicSmoothPeriodization>(); default: @@ -83,7 +83,8 @@ IFrequencyResampler::UPtr FrequencyResamplerFactory::Create( } LOG("frequency_resampler_factory", warn, - "combination of image decomposition and zoom strategy not implemented"); + "combination of image decomposition and upsampling strategy not " + "implemented"); return nullptr; } diff --git a/src/sirius/frequency_resampler_factory.h b/src/sirius/frequency_resampler_factory.h index c9f5dc52..350df0a9 100644 --- a/src/sirius/frequency_resampler_factory.h +++ b/src/sirius/frequency_resampler_factory.h @@ -23,23 +23,16 @@ #define SIRIUS_FREQUENCY_RESAMPLER_FACTORY_H_ #include "sirius/i_frequency_resampler.h" +#include "sirius/image_decomposition/policies.h" namespace sirius { -/** - * \brief Enum of supported image decomposition policies - */ -enum class ImageDecompositionPolicies { - kRegular = 0, /**< regular image decomposition */ - kPeriodicSmooth /**< periodic plus smooth image decomposition */ -}; - /** * \brief Enum of supported frequency zoom strategies */ -enum class FrequencyZoomStrategies { - kZeroPadding = 0, /**< zero padding zoom */ - kPeriodization /**< periodization zoom */ +enum class FrequencyUpsamplingStrategies { + kZeroPadding = 0, /**< zero padding upsampling */ + kPeriodization /**< periodization upsampling */ }; /** @@ -50,14 +43,14 @@ class FrequencyResamplerFactory { public: /** * \brief IFrequencyResampler factory - * \param image_decomposition cf. ImageDecompositionPolicies enum - * \param zoom_strategy cf. FrequencyZoomStrategies enum + * \param image_decomposition cf. Policies enum + * \param zoom_strategy cf. FrequencyUpsamplingStrategies enum * \return requested composition of FrequencyResampler | nullptr if not * available */ static IFrequencyResampler::UPtr Create( - ImageDecompositionPolicies image_decomposition, - FrequencyZoomStrategies zoom_strategy); + image_decomposition::Policies image_decomposition, + FrequencyUpsamplingStrategies zoom_strategy); }; } // namespace sirius diff --git a/src/sirius/gdal/i_input_stream.h b/src/sirius/gdal/i_input_stream.h new file mode 100644 index 00000000..06a52656 --- /dev/null +++ b/src/sirius/gdal/i_input_stream.h @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#ifndef SIRIUS_GDAL_I_INPUT_STREAM_H_ +#define SIRIUS_GDAL_I_INPUT_STREAM_H_ + +#include + +#include "sirius/types.h" + +#include "sirius/gdal/stream_block.h" + +namespace sirius { +namespace gdal { + +class IInputStream { + public: + virtual ~IInputStream() = default; + + /** + * \brief Read a block from the image + * \param ec error code if operation failed + * \return block read + */ + virtual StreamBlock Read(std::error_code& ec) = 0; + + /** + * \brief Indicate end of image + * \return boolean if end is reached + */ + virtual bool IsEnded() const = 0; +}; + +} // namespace gdal +} // namespace sirius + +#endif // SIRIUS_GDAL_I_INPUT_STREAM_H_ \ No newline at end of file diff --git a/src/sirius/resampler/image_decomposition/regular_policy.txx b/src/sirius/gdal/i_output_stream.h similarity index 56% rename from src/sirius/resampler/image_decomposition/regular_policy.txx rename to src/sirius/gdal/i_output_stream.h index 99ac5503..28d00992 100644 --- a/src/sirius/resampler/image_decomposition/regular_policy.txx +++ b/src/sirius/gdal/i_output_stream.h @@ -19,23 +19,31 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ -#define SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ +#ifndef SIRIUS_GDAL_I_OUTPUT_STREAM_H_ +#define SIRIUS_GDAL_I_OUTPUT_STREAM_H_ -#include "sirius/resampler/image_decomposition/regular_policy.h" +#include + +#include "sirius/types.h" + +#include "sirius/gdal/stream_block.h" namespace sirius { -namespace resampler { +namespace gdal { + +class IOutputStream { + public: + virtual ~IOutputStream() = default; -template -Image ImageDecompositionRegularPolicy::DecomposeAndZoom( - int zoom, const Image& padded_image, const Filter& filter) const { - // method inherited from ZoomStrategy - LOG("regular_decomposition", trace, "zoom image"); - return this->Zoom(zoom, padded_image, filter); -} + /** + * \brief Read a block from the image + * \param ec error code if operation failed + * \return block read + */ + virtual void Write(StreamBlock&& block, std::error_code& ec) = 0; +}; -} // namespace resampler +} // namespace gdal } // namespace sirius -#endif // SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ +#endif // SIRIUS_GDAL_I_OUTPUT_STREAM_H_ \ No newline at end of file diff --git a/src/sirius/image_streamer.h b/src/sirius/gdal/image_streamer.h similarity index 63% rename from src/sirius/image_streamer.h rename to src/sirius/gdal/image_streamer.h index 49f1ba80..24eff1dc 100644 --- a/src/sirius/image_streamer.h +++ b/src/sirius/gdal/image_streamer.h @@ -19,21 +19,25 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_IMAGE_STREAMER_H_ -#define SIRIUS_IMAGE_STREAMER_H_ +#ifndef SIRIUS_GDAL_IMAGE_STREAMER_H_ +#define SIRIUS_GDAL_IMAGE_STREAMER_H_ -#include "sirius/filter.h" -#include "sirius/i_frequency_resampler.h" +#include +#include #include "sirius/gdal/input_stream.h" -#include "sirius/gdal/resampled_output_stream.h" #include "sirius/gdal/wrapper.h" +#include "sirius/utils/concurrent_queue.h" +#include "sirius/utils/log.h" + namespace sirius { +namespace gdal { /** * \brief Image streamer with monothread or multithread strategies */ +template class ImageStreamer { public: /** @@ -42,26 +46,26 @@ class ImageStreamer { * \param input_path input image path * \param output_path output image path * \param block_size stream block size - * \param zoom_ratio zoom ratio - * \param filter_metadata filter metadata - * \param max_parallel_workers max parallel workers + * \param allow_block_resizing allow block resizing + * \param parameters transformation parameters * \param max_parallel_workers max parallel workers to compute the zoom on * stream blocks */ - ImageStreamer(const std::string& input_path, const std::string& output_path, - const Size& block_size, const ZoomRatio& zoom_ratio, - const FilterMetadata& filter_metadata, - unsigned int max_parallel_workers); + ImageStreamer( + const std::string& input_path, const std::string& output_path, + const Size& block_size, bool allow_block_resizing, + const typename Transformer::Parameters& transformer_parameters, + unsigned int max_parallel_workers); /** * \brief Stream the input image, compute the resampling and stream * output data * \param frequency_resampler requested frequency resampler to apply on * stream block - * \param filter filter to apply on the stream block + * \param parameters transformer parameters */ - void Stream(const IFrequencyResampler& frequency_resampler, - const Filter& filter); + void Stream(const Transformer& transformer, + const typename Transformer::Parameters& parameters); private: /** @@ -71,10 +75,11 @@ class ImageStreamer { * file * * \param frequency_resampler frequency zoom to apply on stream block - * \param filter filter to apply on stream block + * \param parameters transformer parameters */ - void RunMonothreadStream(const IFrequencyResampler& frequency_resampler, - const Filter& filter); + void RunMonothreadStream( + const Transformer& transformer, + const typename Transformer::Parameters& parameters); /** * \brief Stream image in multithreading mode @@ -86,19 +91,21 @@ class ImageStreamer { * compute the resampled blocks and feed an output queue * * \param frequency_resampler frequency zoom to apply on stream block - * \param filter filter to apply on stream block + * \param parameters transformer parameters */ - void RunMultithreadStream(const IFrequencyResampler& frequency_resampler, - const Filter& filter); + void RunMultithreadStream( + const Transformer& transformer, + const typename Transformer::Parameters& parameters); private: unsigned int max_parallel_workers_; - Size block_size_; - ZoomRatio zoom_ratio_; - gdal::InputStream input_stream_; - gdal::ResampledOutputStream output_stream_; + InputStream input_stream_; + OutputStream output_stream_; }; +} // namespace gdal } // namespace sirius -#endif // SIRIUS_IMAGE_STREAMER_H_ +#include "sirius/gdal/image_streamer.txx" + +#endif // SIRIUS_GDAL_IMAGE_STREAMER_H_ diff --git a/src/sirius/image_streamer.cc b/src/sirius/gdal/image_streamer.txx similarity index 67% rename from src/sirius/image_streamer.cc rename to src/sirius/gdal/image_streamer.txx index 9e64a5ac..fca8b946 100644 --- a/src/sirius/image_streamer.cc +++ b/src/sirius/gdal/image_streamer.txx @@ -19,46 +19,43 @@ * along with Sirius. If not, see . */ -#include "sirius/image_streamer.h" +#ifndef SIRIUS_GDAL_IMAGE_STREAMER_TXX_ +#define SIRIUS_GDAL_IMAGE_STREAMER_TXX_ -#include -#include - -#include "sirius/gdal/stream_block.h" - -#include "sirius/utils/concurrent_queue.h" -#include "sirius/utils/log.h" +#include "sirius/gdal/image_streamer.h" namespace sirius { - -ImageStreamer::ImageStreamer(const std::string& input_path, - const std::string& output_path, - const Size& block_size, - const ZoomRatio& zoom_ratio, - const FilterMetadata& filter_metadata, - unsigned int max_parallel_workers) - : max_parallel_workers_(max_parallel_workers), - block_size_(block_size), - zoom_ratio_(zoom_ratio), - input_stream_(input_path, block_size, filter_metadata.margin_size, - filter_metadata.padding_type), - output_stream_(input_path, output_path, zoom_ratio) {} - -void ImageStreamer::Stream(const IFrequencyResampler& frequency_resampler, - const Filter& filter) { - LOG("image_streamer", info, "stream block size: {}x{}", block_size_.row, - block_size_.col); +namespace gdal { + +template +ImageStreamer::ImageStreamer( + const std::string& input_path, const std::string& output_path, + const Size& block_size, bool allow_block_resizing, + const typename Transformer::Parameters& transformer_parameters, + unsigned int max_parallel_workers) + : max_parallel_workers_(std::min(max_parallel_workers, + std::thread::hardware_concurrency())), + input_stream_(input_path, block_size, allow_block_resizing, + transformer_parameters), + output_stream_(input_path, output_path, transformer_parameters) {} + +template +void ImageStreamer::Stream( + const Transformer& transformer, + const typename Transformer::Parameters& parameters) { if (max_parallel_workers_ == 1) { - RunMonothreadStream(frequency_resampler, filter); + RunMonothreadStream(transformer, parameters); } else { - RunMultithreadStream(frequency_resampler, filter); + RunMultithreadStream(transformer, parameters); } } -void ImageStreamer::RunMonothreadStream( - const IFrequencyResampler& frequency_resampler, const Filter& filter) { +template +void ImageStreamer::RunMonothreadStream( + const Transformer& transformer, + const typename Transformer::Parameters& parameters) { LOG("image_streamer", info, "start monothreaded streaming"); - while (!input_stream_.IsAtEnd()) { + while (!input_stream_.IsEnded()) { std::error_code read_ec; auto block = input_stream_.Read(read_ec); if (read_ec) { @@ -67,9 +64,8 @@ void ImageStreamer::RunMonothreadStream( break; } - block.buffer = frequency_resampler.Compute( - zoom_ratio_, block.buffer, block.padding, filter); - + block.buffer = transformer.Compute(block.buffer, + block.padding, parameters); std::error_code write_ec; output_stream_.Write(std::move(block), write_ec); if (write_ec) { @@ -81,19 +77,19 @@ void ImageStreamer::RunMonothreadStream( LOG("image_streamer", info, "end monothreaded streaming"); } -void ImageStreamer::RunMultithreadStream( - const IFrequencyResampler& frequency_resampler, const Filter& filter) { +template +void ImageStreamer:: + RunMultithreadStream(const Transformer& transformer, + const typename Transformer::Parameters& parameters) { LOG("image_streamer", info, "start multithreaded streaming"); // use block queues - utils::ConcurrentQueue input_queue( - max_parallel_workers_); - utils::ConcurrentQueue output_queue( - max_parallel_workers_); + utils::ConcurrentQueue input_queue(max_parallel_workers_); + utils::ConcurrentQueue output_queue(max_parallel_workers_); auto input_stream_task = [this, &input_queue]() { LOG("image_streamer", info, "start reading blocks"); - while (!input_stream_.IsAtEnd() && input_queue.IsActive()) { + while (!input_stream_.IsEnded() && input_queue.IsActive()) { std::error_code read_ec; auto block = input_stream_.Read(read_ec); if (read_ec) { @@ -115,8 +111,8 @@ void ImageStreamer::RunMultithreadStream( LOG("image_streamer", info, "end reading blocks"); }; - auto worker_task = [this, &input_queue, &output_queue, &frequency_resampler, - &filter]() { + auto worker_task = [&input_queue, &output_queue, &transformer, + ¶meters]() { try { while (input_queue.CanPop()) { std::error_code pop_input_ec; @@ -129,8 +125,8 @@ void ImageStreamer::RunMultithreadStream( break; } - block.buffer = frequency_resampler.Compute( - zoom_ratio_, block.buffer, block.padding, filter); + block.buffer = transformer.Compute( + block.buffer, block.padding, parameters); std::error_code push_output_ec; output_queue.Push(std::move(block), push_output_ec); @@ -174,7 +170,7 @@ void ImageStreamer::RunMultithreadStream( std::async(std::launch::async, output_stream_task); auto input_task_future = std::async(std::launch::async, input_stream_task); - LOG("image_streamer", info, "start zoom processing with {} workers", + LOG("image_streamer", info, "start transform processing with {} workers", max_parallel_workers_); using WorkerTasks = std::vector>; WorkerTasks worker_task_futures; @@ -191,11 +187,14 @@ void ImageStreamer::RunMultithreadStream( e.what()); } } - LOG("image_streamer", info, "end zoom processing"); + LOG("image_streamer", info, "end transform processing"); output_queue.Deactivate(); output_task_future.get(); input_task_future.get(); LOG("image_streamer", info, "end multithreaded streaming"); } +} // namespace gdal } // namespace sirius + +#endif // SIRIUS_GDAL_IMAGE_STREAMER_TXX_ \ No newline at end of file diff --git a/src/sirius/gdal/input_stream.cc b/src/sirius/gdal/input_stream.cc index 0becf9ae..77dc6923 100644 --- a/src/sirius/gdal/input_stream.cc +++ b/src/sirius/gdal/input_stream.cc @@ -64,53 +64,54 @@ StreamBlock InputStream::Read(std::error_code& ec) { int padded_block_h = block_size_.row + 2 * block_margin_size_.row; if (padded_block_w > w || padded_block_h > h) { - LOG("input_stream", critical, + LOG("input_stream", warn, "requested block size ({}x{}) is bigger than source image ({}x{}). " - "You should use regular processing", + "Read only one block", padded_block_w, padded_block_h, w, h); - ec = make_error_code(CPLE_ObjectNull); - return {}; - } - - // resize block if needed - if (row_idx_ + padded_block_h > h) { - // assign size that can be read - padded_block_h -= (row_idx_ + padded_block_h - h); - - if (padded_block_h < block_margin_size_.row) { - LOG("input_stream", error, - "block at coordinates ({}, {}) cannot be read because " - "available reading height {} is less than margin size {}", - row_idx_, col_idx_, padded_block_h, block_margin_size_.row); - ec = make_error_code(CPLE_ObjectNull); - return {}; - } - - if (padded_block_h > block_margin_size_.row + block_size_.row) { - // bottom margin is partly read. add missing margin - padded_block_h += - (row_idx_ + block_size_.row + 2 * block_margin_size_.row - h); - } else { - padded_block_h += block_margin_size_.row; - } - } - if (col_idx_ + padded_block_w > w) { - padded_block_w -= (col_idx_ + padded_block_w - w); - - if (padded_block_w < block_margin_size_.col) { - LOG("ReadBlock", error, - "block at coordinates {}, {}, cannot be read because available " - "reading width {} is less than margin size {}", - row_idx_, col_idx_, padded_block_w, block_margin_size_.col); - ec = make_error_code(CPLE_ObjectNull); - return {}; + padded_block_h = h + 2 * block_margin_size_.row; + padded_block_w = w + 2 * block_margin_size_.col; + } else { + // resize block if needed + if (row_idx_ + padded_block_h > h) { + // assign size that can be read + padded_block_h -= (row_idx_ + padded_block_h - h); + + if (padded_block_h < block_margin_size_.row) { + LOG("input_stream", error, + "block at coordinates ({}, {}) cannot be read because " + "available reading height {} is less than margin size {}", + row_idx_, col_idx_, padded_block_h, block_margin_size_.row); + ec = make_error_code(CPLE_ObjectNull); + return {}; + } + + if (padded_block_h > block_margin_size_.row + block_size_.row) { + // bottom margin is partly read. add missing margin + padded_block_h += (row_idx_ + block_size_.row + + 2 * block_margin_size_.row - h); + } else { + padded_block_h += block_margin_size_.row; + } } - - if (padded_block_w > block_size_.col + block_margin_size_.col) { - padded_block_w += - (col_idx_ + block_size_.col + 2 * block_margin_size_.col - w); - } else { - padded_block_w += block_margin_size_.col; + if (col_idx_ + padded_block_w > w) { + padded_block_w -= (col_idx_ + padded_block_w - w); + + if (padded_block_w < block_margin_size_.col) { + LOG("ReadBlock", error, + "block at coordinates {}, {}, cannot be read because " + "available " + "reading width {} is less than margin size {}", + row_idx_, col_idx_, padded_block_w, block_margin_size_.col); + ec = make_error_code(CPLE_ObjectNull); + return {}; + } + + if (padded_block_w > block_size_.col + block_margin_size_.col) { + padded_block_w += (col_idx_ + block_size_.col + + 2 * block_margin_size_.col - w); + } else { + padded_block_w += block_margin_size_.col; + } } } Padding block_padding; @@ -124,9 +125,9 @@ StreamBlock InputStream::Read(std::error_code& ec) { } // bottom padding needed - if (row_idx_ >= (h - block_size_.row - 2 * block_margin_size_.row)) { + if (row_idx_ >= (h - h_to_read)) { block_padding.bottom = block_margin_size_.row; - h_to_read -= (row_idx_ + padded_block_h - h); + h_to_read -= (row_idx_ + h_to_read - h); } // left padding needed @@ -136,9 +137,9 @@ StreamBlock InputStream::Read(std::error_code& ec) { } // right padding needed - if (col_idx_ >= (w - block_size_.col - 2 * block_margin_size_.col)) { + if (col_idx_ >= (w - w_to_read)) { block_padding.right = block_margin_size_.col; - w_to_read -= (col_idx_ + padded_block_w - w); + w_to_read -= (col_idx_ + w_to_read - w); } Image output_buffer({h_to_read, w_to_read}); diff --git a/src/sirius/gdal/input_stream.h b/src/sirius/gdal/input_stream.h index 7951f65d..611d1e4b 100644 --- a/src/sirius/gdal/input_stream.h +++ b/src/sirius/gdal/input_stream.h @@ -22,12 +22,8 @@ #ifndef SIRIUS_GDAL_INPUT_STREAM_H_ #define SIRIUS_GDAL_INPUT_STREAM_H_ -#include +#include "sirius/gdal/i_input_stream.h" -#include "sirius/image.h" -#include "sirius/types.h" - -#include "sirius/gdal/stream_block.h" #include "sirius/gdal/types.h" namespace sirius { @@ -36,8 +32,10 @@ namespace gdal { /** * \brief Stream an image in block */ -class InputStream { +class InputStream : public IInputStream { public: + InputStream() = default; + /** * \brief Instanciate an InputStreamer and set its block size * \param image_path path to the input image @@ -53,30 +51,21 @@ class InputStream { InputStream(const InputStream&) = delete; InputStream& operator=(const InputStream&) = delete; - InputStream(InputStream&&) = delete; - InputStream& operator=(InputStream&&) = delete; - - /** - * \brief Get the size of the input file - * \return input file size - */ - sirius::Size Size() { - return {input_dataset_->GetRasterYSize(), - input_dataset_->GetRasterXSize()}; - } + InputStream(InputStream&&) = default; + InputStream& operator=(InputStream&&) = default; /** * \brief Read a block from the image * \param ec error code if operation failed * \return block read */ - StreamBlock Read(std::error_code& ec); + StreamBlock Read(std::error_code& ec) override; /** * \brief Indicate end of image * \return boolean if end is reached */ - bool IsAtEnd() { return is_ended_; } + bool IsEnded() const override { return is_ended_; } private: gdal::DatasetUPtr input_dataset_; diff --git a/src/sirius/gdal/output_stream.cc b/src/sirius/gdal/output_stream.cc new file mode 100644 index 00000000..50d29604 --- /dev/null +++ b/src/sirius/gdal/output_stream.cc @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#include "sirius/gdal/output_stream.h" + +#include "sirius/gdal/error_code.h" +#include "sirius/gdal/wrapper.h" + +#include "sirius/utils/log.h" + +namespace sirius { +namespace gdal { + +OutputStream::OutputStream(gdal::DatasetUPtr output_dataset) + : output_dataset_(std::move(output_dataset)) {} + +void OutputStream::Write(StreamBlock&& block, std::error_code& ec) { + CPLErr err = output_dataset_->GetRasterBand(1)->RasterIO( + GF_Write, block.col_idx, block.row_idx, block.buffer.size.col, + block.buffer.size.row, const_cast(block.buffer.data.data()), + block.buffer.size.col, block.buffer.size.row, GDT_Float64, 0, 0, + NULL); + if (err) { + LOG("output_stream", error, + "GDAL error: {} - could not write to the given dataset", err); + ec = make_error_code(err); + return; + } + ec = make_error_code(CPLE_None); +} + +} // namespace gdal +} // namespace sirus diff --git a/src/sirius/gdal/resampled_output_stream.h b/src/sirius/gdal/output_stream.h similarity index 58% rename from src/sirius/gdal/resampled_output_stream.h rename to src/sirius/gdal/output_stream.h index 2409c8d1..028a31e3 100644 --- a/src/sirius/gdal/resampled_output_stream.h +++ b/src/sirius/gdal/output_stream.h @@ -19,14 +19,11 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_GDAL_RESAMPLED_OUTPUT_STREAM_H_ -#define SIRIUS_GDAL_RESAMPLED_OUTPUT_STREAM_H_ +#ifndef SIRIUS_GDAL_OUTPUT_STREAM_H_ +#define SIRIUS_GDAL_OUTPUT_STREAM_H_ -#include +#include "sirius/gdal/i_output_stream.h" -#include "sirius/types.h" - -#include "sirius/gdal/stream_block.h" #include "sirius/gdal/types.h" namespace sirius { @@ -35,31 +32,30 @@ namespace gdal { /** * \brief Write a resampled image by block */ -class ResampledOutputStream { +class OutputStream : public IOutputStream { public: - ResampledOutputStream(const std::string& input_path, - const std::string& output_path, - const ZoomRatio& zoom_ratio); + OutputStream() = default; + + OutputStream(gdal::DatasetUPtr output_dataset); - ~ResampledOutputStream() = default; - ResampledOutputStream(const ResampledOutputStream&) = delete; - ResampledOutputStream& operator=(const ResampledOutputStream&) = delete; - ResampledOutputStream(ResampledOutputStream&&) = delete; - ResampledOutputStream& operator=(ResampledOutputStream&&) = delete; + ~OutputStream() = default; + OutputStream(const OutputStream&) = delete; + OutputStream& operator=(const OutputStream&) = delete; + OutputStream(OutputStream&&) = default; + OutputStream& operator=(OutputStream&&) = default; /** * \brief Write a zoomed block in the output file * \param block block to write * \param ec error code if operation failed */ - void Write(StreamBlock&& block, std::error_code& ec); + void Write(StreamBlock&& block, std::error_code& ec) override; private: gdal::DatasetUPtr output_dataset_; - ZoomRatio zoom_ratio_; }; } // namespace gdal } // namespace sirius -#endif // SIRIUS_GDAL_RESAMPLED_OUTPUT_STREAM_H_ +#endif // SIRIUS_GDAL_OUTPUT_STREAM_H_ diff --git a/src/sirius/gdal/resampled_output_stream.cc b/src/sirius/gdal/resampled_output_stream.cc deleted file mode 100644 index 0a2f4903..00000000 --- a/src/sirius/gdal/resampled_output_stream.cc +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) - * - * This file is part of Sirius - * - * https://github.com/CS-SI/SIRIUS - * - * Sirius 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 3 of the License, or - * (at your option) any later version. - * - * Sirius 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 Sirius. If not, see . - */ - -#include "sirius/gdal/resampled_output_stream.h" - -#include "sirius/gdal/error_code.h" -#include "sirius/gdal/wrapper.h" - -#include "sirius/utils/log.h" - -namespace sirius { -namespace gdal { - -ResampledOutputStream::ResampledOutputStream(const std::string& input_path, - const std::string& output_path, - const ZoomRatio& zoom_ratio) - : zoom_ratio_(zoom_ratio) { - auto input_dataset = gdal::LoadDataset(input_path); - - int output_h = static_cast( - std::ceil(input_dataset->GetRasterYSize() * zoom_ratio_.ratio())); - int output_w = static_cast( - std::ceil(input_dataset->GetRasterXSize() * zoom_ratio_.ratio())); - - auto geo_ref = gdal::ComputeResampledGeoReference(input_path, zoom_ratio); - output_dataset_ = - gdal::CreateDataset(output_path, output_w, output_h, 1, geo_ref); - LOG("resampled_output_stream", info, "resampled image '{}' ({}x{})", - output_path, output_h, output_w); -} - -void ResampledOutputStream::Write(StreamBlock&& block, std::error_code& ec) { - int out_row_idx = static_cast( - std::floor(block.row_idx * zoom_ratio_.input_resolution() / - static_cast(zoom_ratio_.output_resolution()))); - int out_col_idx = static_cast( - std::floor(block.col_idx * zoom_ratio_.input_resolution() / - static_cast(zoom_ratio_.output_resolution()))); - - LOG("resampled_output_stream", debug, - "writing block ({},{}) to ({},{}) (size: {}x{})", block.row_idx, - block.col_idx, out_row_idx, out_col_idx, block.buffer.size.row, - block.buffer.size.col); - - CPLErr err = output_dataset_->GetRasterBand(1)->RasterIO( - GF_Write, out_col_idx, out_row_idx, block.buffer.size.col, - block.buffer.size.row, const_cast(block.buffer.data.data()), - block.buffer.size.col, block.buffer.size.row, GDT_Float64, 0, 0, - NULL); - if (err) { - LOG("resampled_output_stream", error, - "GDAL error: {} - could not write to the given dataset", err); - ec = make_error_code(err); - return; - } - ec = make_error_code(CPLE_None); -} - -} // namespace gdal -} // namespace sirius diff --git a/src/sirius/gdal/resampling/input_stream.cc b/src/sirius/gdal/resampling/input_stream.cc new file mode 100644 index 00000000..d9c72156 --- /dev/null +++ b/src/sirius/gdal/resampling/input_stream.cc @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#include "sirius/gdal/resampling/input_stream.h" + +#include "sirius/utils/numeric.h" + +namespace sirius { +namespace gdal { +namespace resampling { + +InputStream::InputStream( + const std::string& image_path, const sirius::Size& block_size, + bool allow_block_resizing, + const sirius::resampling::Parameters& resampling_parameters) { + auto stream_block_size = block_size; + + Size filter_padding_size = {0, 0}; + PaddingType padding_type = PaddingType::kMirrorPadding; + + if (resampling_parameters.filter) { + filter_padding_size = resampling_parameters.filter->padding_size(); + padding_type = resampling_parameters.filter->padding_type(); + } + + // improve stream_block_size if requested or required + if (!resampling_parameters.ratio.IsRealZoom()) { + if (allow_block_resizing) { + stream_block_size = sirius::utils::GenerateDyadicSize( + stream_block_size, + resampling_parameters.ratio.input_resolution(), + filter_padding_size); + LOG("resampled_input_stream", warn, + "stream block resized to dyadic size: {}x{}", + stream_block_size.row, stream_block_size.col); + } + } else { + // real zoom needs specific block size (row and col should be + // multiple + // of input resolution and output resolution) + stream_block_size = sirius::utils::GenerateZoomCompliantSize( + stream_block_size, resampling_parameters.ratio); + LOG("resampled_input_stream", warn, + "stream block resized to comply with zoom: {}x{}", + stream_block_size.row, stream_block_size.col); + } + input_stream_ = gdal::InputStream(image_path, stream_block_size, + filter_padding_size, padding_type); +} + +} // namespace resampling +} // namespace gdal +} // namespace sirius \ No newline at end of file diff --git a/src/sirius/gdal/resampling/input_stream.h b/src/sirius/gdal/resampling/input_stream.h new file mode 100644 index 00000000..02f85364 --- /dev/null +++ b/src/sirius/gdal/resampling/input_stream.h @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#ifndef SIRIUS_GDAL_RESAMPLING_INPUT_STREAM_H_ +#define SIRIUS_GDAL_RESAMPLING_INPUT_STREAM_H_ + +#include + +#include "sirius/image.h" +#include "sirius/types.h" + +#include "sirius/gdal/i_input_stream.h" +#include "sirius/gdal/input_stream.h" + +#include "sirius/resampling/parameters.h" + +namespace sirius { +namespace gdal { +namespace resampling { + +class InputStream : public IInputStream { + public: + InputStream(const std::string& image_path, const sirius::Size& block_size, + bool allow_block_resizing, + const sirius::resampling::Parameters& resampling_parameters); + + ~InputStream() = default; + InputStream(const InputStream&) = delete; + InputStream& operator=(const InputStream&) = delete; + InputStream(InputStream&&) = default; + InputStream& operator=(InputStream&&) = default; + + /** + * \brief Read a block from the image + * \param ec error code if operation failed + * \return block read + */ + StreamBlock Read(std::error_code& ec) override { + return input_stream_.Read(ec); + } + + /** + * \brief Indicate end of image + * \return boolean if end is reached + */ + bool IsEnded() const override { return input_stream_.IsEnded(); } + + private: + gdal::InputStream input_stream_; +}; + +} // namespace resampling +} // namespace gdal +} // namespace sirius + +#endif // SIRIUS_GDAL_RESAMPLING_INPUT_STREAM_H_ \ No newline at end of file diff --git a/src/sirius/gdal/resampling/output_stream.cc b/src/sirius/gdal/resampling/output_stream.cc new file mode 100644 index 00000000..03b3ef74 --- /dev/null +++ b/src/sirius/gdal/resampling/output_stream.cc @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#include "sirius/gdal/resampling/output_stream.h" + +#include "sirius/gdal/wrapper.h" + +namespace sirius { +namespace gdal { +namespace resampling { + +OutputStream::OutputStream( + const std::string& input_path, const std::string& output_path, + const sirius::resampling::Parameters& resampling_parameters) { + auto input_dataset = gdal::LoadDataset(input_path); + zoom_ratio_ = resampling_parameters.ratio; + + int output_h = + static_cast(std::ceil(input_dataset->GetRasterYSize() * zoom_ratio_.ratio())); + int output_w = + static_cast(std::ceil(input_dataset->GetRasterXSize() * zoom_ratio_.ratio())); + + auto geo_ref = + ComputeResampledGeoReference(input_path, resampling_parameters.ratio); + + auto output_dataset = + gdal::CreateDataset(output_path, output_w, output_h, 1, geo_ref); + LOG("resampled_output_stream", info, "resampled image '{}' ({}x{})", + output_path, output_h, output_w); + + output_stream_ = gdal::OutputStream(std::move(output_dataset)); +} + +void OutputStream::Write(StreamBlock&& block, std::error_code& ec) { + int out_row_idx = + static_cast(std::floor(block.row_idx * zoom_ratio_.input_resolution() / + static_cast(zoom_ratio_.output_resolution()))); + int out_col_idx = + static_cast(std::floor(block.col_idx * zoom_ratio_.input_resolution() / + static_cast(zoom_ratio_.output_resolution()))); + LOG("resampled_output_stream", debug, + "writing block ({},{}) to ({},{}) (size: {}x{})", block.row_idx, + block.col_idx, out_row_idx, out_col_idx, block.buffer.size.row, + block.buffer.size.col); + + block.row_idx = out_row_idx; + block.col_idx = out_col_idx; + + output_stream_.Write(std::move(block), ec); +} + +} // namespace resampling +} // namespace gdal +} // namespace sirius \ No newline at end of file diff --git a/src/sirius/gdal/resampling/output_stream.h b/src/sirius/gdal/resampling/output_stream.h new file mode 100644 index 00000000..f3c65251 --- /dev/null +++ b/src/sirius/gdal/resampling/output_stream.h @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#ifndef SIRIUS_GDAL_RESAMPLING_OUTPUT_STREAM_H_ +#define SIRIUS_GDAL_RESAMPLING_OUTPUT_STREAM_H_ + +#include + +#include "sirius/types.h" + +#include "sirius/gdal/i_output_stream.h" +#include "sirius/gdal/output_stream.h" + +#include "sirius/resampling/parameters.h" + +namespace sirius { +namespace gdal { +namespace resampling { + +class OutputStream : public IOutputStream { + public: + OutputStream(const std::string& input_path, const std::string& output_path, + const sirius::resampling::Parameters& resampling_parameters); + + ~OutputStream() = default; + OutputStream(const OutputStream&) = delete; + OutputStream& operator=(const OutputStream&) = delete; + OutputStream(OutputStream&&) = default; + OutputStream& operator=(OutputStream&&) = default; + + /** + * \brief Write a zoomed block in the output file + * \param block block to write + * \param ec error code if operation failed + */ + void Write(StreamBlock&& block, std::error_code& ec) override; + + private: + gdal::OutputStream output_stream_; + ZoomRatio zoom_ratio_; +}; + +} // namespace resampling +} // namespace gdal +} // namespace sirius + +#endif // SIRIUS_GDAL_RESAMPLING_OUTPUT_STREAM_H_ \ No newline at end of file diff --git a/src/sirius/gdal/stream_block.h b/src/sirius/gdal/stream_block.h index fd965cf5..541a5e82 100644 --- a/src/sirius/gdal/stream_block.h +++ b/src/sirius/gdal/stream_block.h @@ -19,8 +19,8 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_GDAL_STREAM_H_ -#define SIRIUS_GDAL_STREAM_H_ +#ifndef SIRIUS_GDAL_STREAM_BLOCK_H_ +#define SIRIUS_GDAL_STREAM_BLOCK_H_ #include "sirius/image.h" @@ -66,4 +66,4 @@ struct StreamBlock { } // namespace gdal } // namespace sirius -#endif // SIRIUS_GDAL_STREAM_H_ +#endif // SIRIUS_GDAL_STREAM_BLOCK_H_ diff --git a/src/sirius/i_frequency_resampler.h b/src/sirius/i_frequency_resampler.h index c050cdc0..1ae0ffab 100644 --- a/src/sirius/i_frequency_resampler.h +++ b/src/sirius/i_frequency_resampler.h @@ -26,9 +26,9 @@ #include #include -#include "sirius/filter.h" #include "sirius/image.h" -#include "sirius/types.h" + +#include "sirius/resampling/parameters.h" namespace sirius { @@ -39,6 +39,7 @@ namespace sirius { class IFrequencyResampler { public: using UPtr = std::unique_ptr; + using Parameters = resampling::Parameters; public: virtual ~IFrequencyResampler() = default; @@ -48,19 +49,16 @@ class IFrequencyResampler { * * \remark This method is thread safe * - * \param zoom_ratio zoom ratio * \param input image to zoom in/out * \param image_padding expected padding to add to the image to * comply with the filter - * \param filter optional filter to apply after the zoom transformation. - * The filter must be compatible with the requested ratio. - * \return Zoomed in/out image + * \param parameters resampling parameters + * \return Resampled image * * \throw sirius::Exception if a computing issue happens */ - virtual Image Compute(const ZoomRatio& zoom_ratio, const Image& input, - const Padding& image_padding, - const Filter& filter = {}) const = 0; + virtual Image Compute(const Image& input, const Padding& image_padding, + const Parameters& resampling_parameters) const = 0; }; } // namespace sirius diff --git a/src/sirius/image.cc b/src/sirius/image.cc index 270ec5d5..104b1ad1 100644 --- a/src/sirius/image.cc +++ b/src/sirius/image.cc @@ -30,14 +30,6 @@ namespace sirius { -Padding::Padding(int i_top, int i_bottom, int i_left, int i_right, - PaddingType i_type) - : top(i_top), - bottom(i_bottom), - left(i_left), - right(i_right), - type(i_type) {} - Image::Image(const Size& size) : size(size), data(size.CellCount(), 0) {} Image::Image(const Size& size, Buffer&& buf) diff --git a/src/sirius/image.h b/src/sirius/image.h index c18bff2c..129eba85 100644 --- a/src/sirius/image.h +++ b/src/sirius/image.h @@ -38,8 +38,13 @@ enum class PaddingType { */ struct Padding { Padding() = default; - Padding(int top, int bottom, int left, int right, - PaddingType type = PaddingType::kMirrorPadding); + constexpr Padding(int i_top, int i_bottom, int i_left, int i_right, + PaddingType i_type = PaddingType::kMirrorPadding) + : top(i_top), + bottom(i_bottom), + left(i_left), + right(i_right), + type(i_type) {} ~Padding() = default; Padding(const Padding&) = default; diff --git a/src/sirius/resampler/image_decomposition/periodic_smooth_policy.h b/src/sirius/image_decomposition/periodic_smooth_policy.h similarity index 62% rename from src/sirius/resampler/image_decomposition/periodic_smooth_policy.h rename to src/sirius/image_decomposition/periodic_smooth_policy.h index f1f5b581..ab2085a7 100644 --- a/src/sirius/resampler/image_decomposition/periodic_smooth_policy.h +++ b/src/sirius/image_decomposition/periodic_smooth_policy.h @@ -19,33 +19,32 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ -#define SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ +#ifndef SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ +#define SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ #include "sirius/filter.h" #include "sirius/image.h" namespace sirius { -namespace resampler { +namespace image_decomposition { /** * \brief Implementation of Periodic * plus Smooth image decomposition */ -template -class ImageDecompositionPeriodicSmoothPolicy : private ZoomStrategy { +template +class PeriodicSmoothPolicy : private ImageProcessor, private ImageInterpolator { public: - Image DecomposeAndZoom(int zoom, const Image& even_image, - const Filter& filter) const; - - private: - Image Interpolate2D(int zoom, const Image& even_image) const; + Image DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const; }; -} // namespace resampler +} // namespace image_decomposition } // namespace sirius -#include "sirius/resampler/image_decomposition/periodic_smooth_policy.txx" +#include "sirius/image_decomposition/periodic_smooth_policy.txx" -#endif // SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ +#endif // SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_H_ diff --git a/src/sirius/resampler/image_decomposition/periodic_smooth_policy.txx b/src/sirius/image_decomposition/periodic_smooth_policy.txx similarity index 59% rename from src/sirius/resampler/image_decomposition/periodic_smooth_policy.txx rename to src/sirius/image_decomposition/periodic_smooth_policy.txx index 6959a2d3..3d9af4d1 100644 --- a/src/sirius/resampler/image_decomposition/periodic_smooth_policy.txx +++ b/src/sirius/image_decomposition/periodic_smooth_policy.txx @@ -19,10 +19,10 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ -#define SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ +#ifndef SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ +#define SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ -#include "sirius/resampler/image_decomposition/periodic_smooth_policy.h" +#include "sirius/image_decomposition/periodic_smooth_policy.h" #include "sirius/fftw/fftw.h" #include "sirius/fftw/types.h" @@ -31,11 +31,14 @@ #include "sirius/utils/gsl.h" namespace sirius { -namespace resampler { - -template -Image ImageDecompositionPeriodicSmoothPolicy::DecomposeAndZoom( - int zoom, const Image& image, const Filter& filter) const { +namespace image_decomposition { + +template +Image PeriodicSmoothPolicy:: + DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const { // 1) compute intensity changes between two opposite borders LOG("periodic_smooth_decomposition", trace, "compute intensity changes"); Image border_intensity_changes(image.size); @@ -138,116 +141,52 @@ Image ImageDecompositionPeriodicSmoothPolicy::DecomposeAndZoom( auto periodic_part_image = fftw::IFFT(image.size, std::move(periodic_part_fft)); - // 7) apply zoom on periodic part - LOG("periodic_smooth_decomposition", trace, "zoom periodic part"); - // method inherited from ZoomStrategy - auto zoomed_image = this->Zoom(zoom, periodic_part_image, filter); + // 7) apply processor on periodic part + LOG("periodic_smooth_decomposition", trace, "process periodic part"); + // method inherited from ImageProcessor + auto processed_image = this->Process(periodic_part_image, parameters); - // 7) ifft smooth part + // 8) ifft smooth part LOG("periodic_smooth_decomposition", trace, "smooth part IFFT"); auto smooth_part_image = fftw::IFFT(image.size, std::move(smooth_part_fft)); - // 8) normalize smooth_part_image + // 9) normalize smooth_part_image LOG("periodic_smooth_decomposition", trace, "normalize smooth image part"); int image_cell_count = image.CellCount(); std::for_each( smooth_part_image.data.begin(), smooth_part_image.data.end(), [image_cell_count](double& cell) { cell /= image_cell_count; }); - // 9) interpolate 2d smooth part image + // 10) interpolate 2d smooth part image LOG("periodic_smooth_decomposition", trace, "interpolate smooth image part"); - auto interpolated_smooth_image = Interpolate2D(zoom, smooth_part_image); + // method inherited from ImageInterpolator + auto interpolated_smooth_image = + this->Interpolate2D(smooth_part_image, parameters); // 10) normalize periodic_part_image LOG("periodic_smooth_decomposition", trace, "normalize periodic image part"); std::for_each( - zoomed_image.data.begin(), zoomed_image.data.end(), + processed_image.data.begin(), processed_image.data.end(), [image_cell_count](double& cell) { cell /= image_cell_count; }); - // 11) sum periodic and smooth parts + // 12) sum periodic and smooth parts LOG("periodic_smooth_decomposition", trace, "sum periodic and smooth image parts"); - Image output_image(zoomed_image.size); - for (int i = 0; i < zoomed_image.size.row; i++) { - for (int j = 0; j < zoomed_image.size.col; j++) { - output_image.Set(i, j, zoomed_image.Get(i, j) + - interpolated_smooth_image.Get(i, j)); + Image output_image(processed_image.size); + for (int i = 0; i < processed_image.size.row; i++) { + for (int j = 0; j < processed_image.size.col; j++) { + output_image.Set(i, j, + processed_image.Get(i, j) + + interpolated_smooth_image.Get(i, j)); } } return output_image; } -template -Image ImageDecompositionPeriodicSmoothPolicy::Interpolate2D( - int zoom, const Image& image) const { - Image interpolated_im(image.size * zoom); - - std::vector BLN_kernel(4, 0); - Size img_mirror_size(image.size.row + 1, image.size.col + 1); - - std::vector img_mirror(img_mirror_size.CellCount(), 0); - auto img_mirror_span = gsl::as_multi_span(img_mirror); - for (int i = 0; i < image.size.row; i++) { - for (int j = 0; j < image.size.col; j++) { - img_mirror_span[i * (image.size.col + 1) + j] = image.Get(i, j); - } - } - - // duplicate last row - for (int j = 0; j < image.size.col + 1; j++) { - img_mirror_span[image.size.row * (image.size.col + 1) + j] = - img_mirror_span[(image.size.row - 1) * (image.size.col + 1) + j]; - } - - // duplicate last col - for (int i = 0; i < image.size.row + 1; i++) { - img_mirror_span[(i + 1) * (image.size.col + 1) - 1] = - img_mirror_span[(i + 1) * (image.size.col + 1) - 2]; - } - - for (int fx = 0; fx < zoom; fx++) { - for (int fy = 0; fy < zoom; fy++) { - BLN_kernel[0] = (1 - fx / static_cast(zoom)) * - (1 - fy / static_cast(zoom)); - BLN_kernel[1] = (1 - fx / static_cast(zoom)) * - (fy / static_cast(zoom)); - BLN_kernel[2] = (fx / static_cast(zoom)) * - (1 - fy / static_cast(zoom)); - BLN_kernel[3] = (fx / static_cast(zoom)) * - (fy / static_cast(zoom)); - - // convolve. BLN_kernel is already flipped - for (int i = fx; i < image.size.row * zoom; i += zoom) { - for (int j = fy; j < image.size.col * zoom; j += zoom) { - interpolated_im.Set( - i, j, - img_mirror_span[(i / zoom) * (image.size.col + 1) + - (j / zoom)] * - BLN_kernel[0] + - img_mirror_span[(i / zoom + 1) * - (image.size.col + 1) + - (j / zoom)] * - BLN_kernel[2] + - img_mirror_span[(i / zoom) * - (image.size.col + 1) + - (j / zoom + 1)] * - BLN_kernel[1] + - img_mirror_span[(i / zoom + 1) * - (image.size.col + 1) + - (j / zoom + 1)] * - BLN_kernel[3]); - } - } - } - } - - return interpolated_im; -} - -} // namespace resampler +} // namespace image_decomposition } // namespace sirius -#endif // SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ +#endif // SIRIUS_IMAGE_DECOMPOSITION_PERIODIC_SMOOTH_POLICY_TXX_ diff --git a/src/sirius/image_decomposition/policies.h b/src/sirius/image_decomposition/policies.h new file mode 100644 index 00000000..e124927f --- /dev/null +++ b/src/sirius/image_decomposition/policies.h @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#ifndef SIRIUS_IMAGE_DECOMPOSITION_POLICIES_H_ +#define SIRIUS_IMAGE_DECOMPOSITION_POLICIES_H_ + +#include "sirius/filter.h" +#include "sirius/image.h" + +namespace sirius { +namespace image_decomposition { + +/** + * \brief Enum of supported image decomposition policies + */ +enum class Policies { + kRegular = 0, /**< regular image decomposition */ + kPeriodicSmooth /**< periodic plus smooth image decomposition */ +}; + +} // namespace image_decomposition +} // namespace sirius + +#endif // SIRIUS_IMAGE_DECOMPOSITION_POLICIES_H_ diff --git a/src/sirius/resampler/image_decomposition/regular_policy.h b/src/sirius/image_decomposition/regular_policy.h similarity index 62% rename from src/sirius/resampler/image_decomposition/regular_policy.h rename to src/sirius/image_decomposition/regular_policy.h index 50f7402e..5428c67c 100644 --- a/src/sirius/resampler/image_decomposition/regular_policy.h +++ b/src/sirius/image_decomposition/regular_policy.h @@ -19,28 +19,30 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ -#define SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ +#ifndef SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ +#define SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ #include "sirius/filter.h" #include "sirius/image.h" namespace sirius { -namespace resampler { +namespace image_decomposition { /** * \brief Implementation of regular image decomposition */ -template -class ImageDecompositionRegularPolicy : private ZoomStrategy { +template +class RegularPolicy : private ImageProcessor { public: - Image DecomposeAndZoom(int zoom, const Image& padded_image, - const Filter& filter) const; + Image DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const; }; -} // namespace resampler +} // namespace image_decomposition } // namespace sirius -#include "sirius/resampler/image_decomposition/regular_policy.txx" +#include "sirius/image_decomposition/regular_policy.txx" -#endif // SIRIUS_RESAMPLER_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ +#endif // SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_H_ diff --git a/src/sirius/image_decomposition/regular_policy.txx b/src/sirius/image_decomposition/regular_policy.txx new file mode 100644 index 00000000..e83a2901 --- /dev/null +++ b/src/sirius/image_decomposition/regular_policy.txx @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2018 CS - Systemes d'Information (CS-SI) + * + * This file is part of Sirius + * + * https://github.com/CS-SI/SIRIUS + * + * Sirius 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 3 of the License, or + * (at your option) any later version. + * + * Sirius 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 Sirius. If not, see . + */ + +#ifndef SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ +#define SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ + +#include "sirius/image_decomposition/regular_policy.h" + +namespace sirius { +namespace image_decomposition { + +template +Image RegularPolicy:: + DecomposeAndProcess( + const Image& image, + const typename Transformation::Parameters& parameters) const { + // method inherited from ImageProcessor + LOG("regular_decomposition", trace, "process image"); + return this->Process(image, parameters); +} + +} // namespace image_decomposition +} // namespace sirius + +#endif // SIRIUS_IMAGE_DECOMPOSITION_REGULAR_POLICY_TXX_ diff --git a/src/sirius/resampler/frequency_resampler.h b/src/sirius/resampling/frequency_resampler.h similarity index 69% rename from src/sirius/resampler/frequency_resampler.h rename to src/sirius/resampling/frequency_resampler.h index 3bbc7244..3a437025 100644 --- a/src/sirius/resampler/frequency_resampler.h +++ b/src/sirius/resampling/frequency_resampler.h @@ -19,24 +19,29 @@ * along with Sirius. If not, see . */ -#ifndef SIRIUS_RESAMPLER_FREQUENCY_RESAMPLER_H_ -#define SIRIUS_RESAMPLER_FREQUENCY_RESAMPLER_H_ +#ifndef SIRIUS_RESAMPLING_FREQUENCY_RESAMPLER_H_ +#define SIRIUS_RESAMPLING_FREQUENCY_RESAMPLER_H_ #include "sirius/i_frequency_resampler.h" #include "sirius/image.h" #include "sirius/fftw/types.h" +#include "sirius/resampling/interpolator.h" + namespace sirius { -namespace resampler { +namespace resampling { /** * \brief Implementation of IFrequencyResampler */ -template