Mercurial > hg > orthanc
changeset 6577:344ae6b5db9b
workaround for the incompatibility of boost::math with MSVC <= 2015
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Tue, 27 Jan 2026 16:07:48 +0100 |
| parents | c621863916df |
| children | 0d650504d32c |
| files | OrthancFramework/Sources/CompatibilityMath.h OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp OrthancFramework/Sources/Images/ImageProcessing.cpp OrthancFramework/Sources/JobsEngine/JobInfo.cpp OrthancFramework/Sources/MetricsRegistry.cpp OrthancFramework/Sources/RestApi/RestApi.cpp OrthancFramework/UnitTestsSources/FrameworkTests.cpp OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp |
| diffstat | 10 files changed, 325 insertions(+), 60 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/CompatibilityMath.h Tue Jan 27 16:07:48 2026 +0100 @@ -0,0 +1,250 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2024-2026 Orthanc Team SRL, Belgium + * Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if defined(BOOST_MATH_ROUND_HPP) +# error You should not manually include <boost/math/special_functions/round.hpp> +#endif + +#include "Compatibility.h" + + +#ifdef __EMSCRIPTEN__ +/* + Avoid such errors: + ------------------ + + .../boost/math/special_functions/round.hpp:118:12: warning: implicit conversion from 'std::__2::numeric_limits<long long>::type' (aka 'long long') to 'float' changes value from 9223372036854775807 to 9223372036854775808 [-Wimplicit-int-float-conversion] + .../boost/math/special_functions/round.hpp:125:11: note: in instantiation of function template specialization 'boost::math::llround<float, boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> >' requested here + + .../boost/math/special_functions/round.hpp:86:12: warning: implicit conversion from 'std::__2::numeric_limits<int>::type' (aka 'int') to 'float' changes value from 2147483647 to 2147483648 [-Wimplicit-int-float-conversion] + .../boost/math/special_functions/round.hpp:93:11: note: in instantiation of function template specialization 'boost::math::iround<float, boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> >' requested here +*/ +# pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion" +#endif + + +#if 1 || defined(_MSC_VER) + +/** + * This is for compatibility with Microsoft Visual Studio <= 2015. + * Indeed, Boost 1.89.0 does not support MSVC 14.0, as can be seen in + * this file: + * https://www.boost.org/doc/libs/1_89_0/boost/math/tools/config.hpp + **/ + +#include <cmath> +#include <limits> + +namespace Orthanc +{ + namespace Math + { + namespace Internals + { + ORTHANC_FORCE_INLINE + float RoundFloat(float v) + { + if (v >= 0.0f) + { + return std::floor(v + 0.5f); + } + else + { + return std::ceil(v - 0.5f); + } + } + + ORTHANC_FORCE_INLINE + double RoundDouble(double v) + { + if (v >= 0.0) + { + return std::floor(v + 0.5); + } + else + { + return std::ceil(v - 0.5); + } + } + + template <typename T> + ORTHANC_FORCE_INLINE + bool RoundFloatWithChecks(T& target, + float source) + { + if (std::isnan(source) || + !std::isfinite(source)) + { + return false; + } + else + { + float rounded = RoundFloat(source); + if (rounded < static_cast<float>(std::numeric_limits<T>::min()) || + rounded > static_cast<float>(std::numeric_limits<T>::max())) + { + return false; + } + else + { + target = static_cast<T>(rounded); + return true; + } + } + } + + template <typename T> + ORTHANC_FORCE_INLINE + bool RoundDoubleWithChecks(T& target, + double source) + { + if (std::isnan(source) || + !std::isfinite(source)) + { + return false; + } + else + { + double rounded = RoundDouble(source); + if (rounded < static_cast<double>(std::numeric_limits<T>::min()) || + rounded > static_cast<double>(std::numeric_limits<T>::max())) + { + return false; + } + else + { + target = static_cast<T>(rounded); + return true; + } + } + } + } + + ORTHANC_FORCE_INLINE + int iround(float v) + { + return static_cast<int>(Internals::RoundFloat(v)); + } + + ORTHANC_FORCE_INLINE + int iround(double v) + { + return static_cast<int>(Internals::RoundDouble(v)); + } + + ORTHANC_FORCE_INLINE + long long llround(float v) + { + return static_cast<long long>(Internals::RoundFloat(v)); + } + + ORTHANC_FORCE_INLINE + long long llround(double v) + { + return static_cast<long long>(Internals::RoundDouble(v)); + } + + ORTHANC_FORCE_INLINE + bool llround(long long& target, + float source) + { + return Internals::RoundFloatWithChecks<long long>(target, source); + } + + ORTHANC_FORCE_INLINE + long long llround(long long& target, + double source) + { + return Internals::RoundDoubleWithChecks<long long>(target, source); + } + } +} + +#else + +#include <boost/math/special_functions/round.hpp> + +namespace Orthanc +{ + namespace Math + { + ORTHANC_FORCE_INLINE + int iround(float v) + { + return boost::math::iround<float>(v); + } + + ORTHANC_FORCE_INLINE + int iround(double v) + { + return boost::math::iround<double>(v); + } + + ORTHANC_FORCE_INLINE + long long llround(float v) + { + return boost::math::llround<float>(v); + } + + ORTHANC_FORCE_INLINE + long long llround(double v) + { + return boost::math::llround<double>(v); + } + + ORTHANC_FORCE_INLINE + bool llround(long long& target, + float source) + { + try + { + target = boost::math::llround<float>(source); + return true; + } + catch (boost::math::rounding_error&) + { + return false; + } + } + + ORTHANC_FORCE_INLINE + long long llround(long long& target, + double source) + { + try + { + target = boost::math::llround<double>(source); + return true; + } + catch (boost::math::rounding_error&) + { + return false; + } + } + } +} + +#endif
--- a/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp Tue Jan 27 13:15:50 2026 +0100 +++ b/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp Tue Jan 27 16:07:48 2026 +0100 @@ -25,13 +25,13 @@ #include "../PrecompiledHeaders.h" #include "DicomWebJsonVisitor.h" +#include "../CompatibilityMath.h" #include "../Logging.h" #include "../OrthancException.h" +#include "../SerializationToolbox.h" #include "../Toolbox.h" -#include "../SerializationToolbox.h" #include "FromDcmtkBridge.h" -#include <boost/math/special_functions/round.hpp> #include <boost/lexical_cast.hpp> @@ -329,10 +329,9 @@ Json::Value DicomWebJsonVisitor::FormatDouble(double value) { - try + long long a; + if (Math::llround(a, value)) { - long long a = boost::math::llround<double>(value); - double d = fabs(value - static_cast<double>(a)); if (d <= std::numeric_limits<double>::epsilon() * 100.0) @@ -344,7 +343,7 @@ return Json::Value(value); } } - catch (boost::math::rounding_error&) + else { // Can occur if "long long" is too small to receive this value // (e.g. infinity) @@ -352,12 +351,12 @@ } } - Json::Value DicomWebJsonVisitor::FormatDecimalString(double value, const std::string& originalString) + Json::Value DicomWebJsonVisitor::FormatDecimalString(double value, + const std::string& originalString) { - try + long long a; + if (Math::llround(a, value)) { - long long a = boost::math::llround<double>(value); - double d = fabs(value - static_cast<double>(a)); if (d <= std::numeric_limits<double>::epsilon() * 100.0) @@ -369,7 +368,7 @@ return Json::Value(originalString); // keep the original string to avoid rounding errors e.g, transforming "0.143" into 0.14299999999999 } } - catch (boost::math::rounding_error&) + else { // Can occur if "long long" is too small to receive this value // (e.g. infinity)
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Tue Jan 27 13:15:50 2026 +0100 +++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp Tue Jan 27 16:07:48 2026 +0100 @@ -138,7 +138,6 @@ #include <dcmtk/dcmdata/dcpxitem.h> -#include <boost/math/special_functions/round.hpp> #include <dcmtk/dcmdata/dcostrmb.h> #include <boost/algorithm/string/predicate.hpp>
--- a/OrthancFramework/Sources/Images/ImageProcessing.cpp Tue Jan 27 13:15:50 2026 +0100 +++ b/OrthancFramework/Sources/Images/ImageProcessing.cpp Tue Jan 27 16:07:48 2026 +0100 @@ -25,26 +25,15 @@ #include "../PrecompiledHeaders.h" #include "ImageProcessing.h" +#include "../CompatibilityMath.h" +#include "../OrthancException.h" #include "Image.h" #include "ImageTraits.h" #include "PixelTraits.h" -#include "../OrthancException.h" - -#ifdef __EMSCRIPTEN__ -/* - Avoid this error: - ----------------- - .../boost/math/special_functions/round.hpp:118:12: warning: implicit conversion from 'std::__2::numeric_limits<long long>::type' (aka 'long long') to 'float' changes value from 9223372036854775807 to 9223372036854775808 [-Wimplicit-int-float-conversion] - .../mnt/c/osi/dev/orthanc/Core/Images/ImageProcessing.cpp:333:28: note: in instantiation of function template specialization 'boost::math::llround<float>' requested here - .../mnt/c/osi/dev/orthanc/Core/Images/ImageProcessing.cpp:1006:9: note: in instantiation of function template specialization 'Orthanc::MultiplyConstantInternal<unsigned char, true>' requested here -*/ -#pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion" -#endif - -#include <boost/math/special_functions/round.hpp> #include <algorithm> #include <cassert> +#include <cmath> #include <limits> #include <list> #include <map> @@ -384,7 +373,7 @@ { assert(sizeof(long long) == sizeof(int64_t)); // The "round" operation is very costly - v = boost::math::llround(static_cast<float>(*p) * factor); + v = Math::llround(static_cast<float>(*p) * factor); } else { @@ -464,7 +453,7 @@ { // The "round" operation is very costly assert(sizeof(TargetType) < sizeof(int)); - *p = static_cast<TargetType>(boost::math::iround(v)); + *p = static_cast<TargetType>(Math::iround(v)); } else { @@ -2716,7 +2705,7 @@ if (UseRound) { assert(sizeof(RawPixel) < sizeof(int)); - *p = static_cast<RawPixel>(boost::math::iround(accumulator)); + *p = static_cast<RawPixel>(Math::iround(accumulator)); } else { @@ -2868,8 +2857,8 @@ static_cast<float>(target.GetWidth()) / cw, static_cast<float>(target.GetHeight()) / ch); - unsigned int sw = std::min(static_cast<unsigned int>(boost::math::iround(cw * r)), target.GetWidth()); - unsigned int sh = std::min(static_cast<unsigned int>(boost::math::iround(ch * r)), target.GetHeight()); + unsigned int sw = std::min(static_cast<unsigned int>(Math::iround(cw * r)), target.GetWidth()); + unsigned int sh = std::min(static_cast<unsigned int>(Math::iround(ch * r)), target.GetHeight()); Image resized(target.GetFormat(), sw, sh, false); @@ -2913,10 +2902,10 @@ static_cast<float>(height) / static_cast<float>(source.GetHeight())); unsigned int resizedWidth = static_cast<unsigned int>( - boost::math::iround(ratio * static_cast<float>(source.GetWidth()))); + Math::iround(ratio * static_cast<float>(source.GetWidth()))); unsigned int resizedHeight = static_cast<unsigned int>( - boost::math::iround(ratio * static_cast<float>(source.GetHeight()))); + Math::iround(ratio * static_cast<float>(source.GetHeight()))); std::unique_ptr<ImageAccessor> resized(FitSize(source, resizedWidth, resizedHeight));
--- a/OrthancFramework/Sources/JobsEngine/JobInfo.cpp Tue Jan 27 13:15:50 2026 +0100 +++ b/OrthancFramework/Sources/JobsEngine/JobInfo.cpp Tue Jan 27 16:07:48 2026 +0100 @@ -24,27 +24,11 @@ #include "../PrecompiledHeaders.h" -#ifdef __EMSCRIPTEN__ -/* -Avoid this error: - -.../boost/math/special_functions/round.hpp:118:12: warning: implicit conversion from 'std::__2::numeric_limits<long long>::type' (aka 'long long') to 'float' changes value from 9223372036854775807 to 9223372036854775808 [-Wimplicit-int-float-conversion] -.../boost/math/special_functions/round.hpp:125:11: note: in instantiation of function template specialization 'boost::math::llround<float, boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> >' requested here -.../orthanc/Core/JobsEngine/JobInfo.cpp:69:44: note: in instantiation of function template specialization 'boost::math::llround<float>' requested here - -.../boost/math/special_functions/round.hpp:86:12: warning: implicit conversion from 'std::__2::numeric_limits<int>::type' (aka 'int') to 'float' changes value from 2147483647 to 2147483648 [-Wimplicit-int-float-conversion] -.../boost/math/special_functions/round.hpp:93:11: note: in instantiation of function template specialization 'boost::math::iround<float, boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> >' requested here -.../orthanc/Core/JobsEngine/JobInfo.cpp:133:39: note: in instantiation of function template specialization 'boost::math::iround<float>' requested here -*/ -#pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion" -#endif - #include "JobInfo.h" +#include "../CompatibilityMath.h" #include "../OrthancException.h" -// This "include" is mandatory for Release builds using Linux Standard Base -#include <boost/math/special_functions/round.hpp> namespace Orthanc { @@ -79,7 +63,7 @@ if (progress > 0.01f && ms > 0.01f) { - long long remaining = boost::math::llround(ms / progress * (1.0f - progress)); + long long remaining = Math::llround(ms / progress * (1.0f - progress)); eta_ = timestamp_ + boost::posix_time::milliseconds(remaining); hasEta_ = true; } @@ -189,7 +173,7 @@ target["Timestamp"] = boost::posix_time::to_iso_string(timestamp_); target["CreationTime"] = boost::posix_time::to_iso_string(creationTime_); target["EffectiveRuntime"] = static_cast<double>(runtime_.total_milliseconds()) / 1000.0; - target["Progress"] = boost::math::iround(status_.GetProgress() * 100.0f); + target["Progress"] = Math::iround(status_.GetProgress() * 100.0f); if (status_.HasUserData()) {
--- a/OrthancFramework/Sources/MetricsRegistry.cpp Tue Jan 27 13:15:50 2026 +0100 +++ b/OrthancFramework/Sources/MetricsRegistry.cpp Tue Jan 27 16:07:48 2026 +0100 @@ -27,10 +27,10 @@ #include "ChunkedBuffer.h" #include "Compatibility.h" +#include "CompatibilityMath.h" #include "Logging.h" #include "OrthancException.h" -#include <boost/math/special_functions/round.hpp> namespace Orthanc { @@ -339,7 +339,7 @@ virtual void UpdateFloat(float value) ORTHANC_OVERRIDE { - value_.Update(boost::math::llround(value), GetPolicy()); + value_.Update(Orthanc::Math::llround(value), GetPolicy()); } virtual void UpdateInteger(int64_t value) ORTHANC_OVERRIDE
--- a/OrthancFramework/Sources/RestApi/RestApi.cpp Tue Jan 27 13:15:50 2026 +0100 +++ b/OrthancFramework/Sources/RestApi/RestApi.cpp Tue Jan 27 16:07:48 2026 +0100 @@ -25,12 +25,12 @@ #include "../PrecompiledHeaders.h" #include "RestApi.h" +#include "../CompatibilityMath.h" #include "../HttpServer/StringHttpOutput.h" #include "../Logging.h" #include "../OrthancException.h" #include <boost/algorithm/string/replace.hpp> -#include <boost/math/special_functions/round.hpp> #include <stdlib.h> // To define "_exit()" under Windows #include <stdio.h> @@ -356,7 +356,7 @@ LOG(WARNING) << "The documentation of the REST API contains " << GetSuccessPathsCount() << " paths over a total of " << GetTotalPathsCount() << " paths " - << "(coverage: " << static_cast<unsigned int>(boost::math::iround(coverage)) << "%)"; + << "(coverage: " << static_cast<unsigned int>(Math::iround(coverage)) << "%)"; } };
--- a/OrthancFramework/UnitTestsSources/FrameworkTests.cpp Tue Jan 27 13:15:50 2026 +0100 +++ b/OrthancFramework/UnitTestsSources/FrameworkTests.cpp Tue Jan 27 16:07:48 2026 +0100 @@ -35,6 +35,7 @@ #include <gtest/gtest.h> +#include "../Sources/CompatibilityMath.h" #include "../Sources/DicomFormat/DicomTag.h" #include "../Sources/HttpServer/HttpToolbox.h" #include "../Sources/Logging.h" @@ -51,6 +52,7 @@ #endif #include <ctype.h> +#include <limits> using namespace Orthanc; @@ -1855,3 +1857,46 @@ ASSERT_TRUE(b.GetContent().isString()); ASSERT_EQ("Hello", b.GetContent().asString()); } + + +TEST(Math, Rounding) +{ + ASSERT_EQ(42, Math::iround(42.005f)); + ASSERT_EQ(42, Math::iround(41.995f)); + ASSERT_EQ(-42, Math::iround(-42.005f)); + ASSERT_EQ(-42, Math::iround(-41.995f)); + + ASSERT_EQ(43, Math::iround(43.005)); + ASSERT_EQ(43, Math::iround(42.995)); + ASSERT_EQ(-43, Math::iround(-43.005)); + ASSERT_EQ(-43, Math::iround(-42.995)); + + ASSERT_EQ(44ll, Math::llround(44.005f)); + ASSERT_EQ(44ll, Math::llround(43.995f)); + ASSERT_EQ(-44ll, Math::llround(-44.005f)); + ASSERT_EQ(-44ll, Math::llround(-43.995f)); + + ASSERT_EQ(45ll, Math::llround(45.005)); + ASSERT_EQ(45ll, Math::llround(44.995)); + ASSERT_EQ(-45ll, Math::llround(-45.005)); + ASSERT_EQ(-45ll, Math::llround(-44.995)); + + long long a; + ASSERT_TRUE(Math::llround(a, 46.005f)); ASSERT_EQ(46ll, a); + ASSERT_TRUE(Math::llround(a, 45.995f)); ASSERT_EQ(46ll, a); + ASSERT_TRUE(Math::llround(a, -46.005f)); ASSERT_EQ(-46ll, a); + ASSERT_TRUE(Math::llround(a, -45.995f)); ASSERT_EQ(-46ll, a); + + ASSERT_FALSE(Math::llround(a, std::numeric_limits<float>::quiet_NaN())); + ASSERT_FALSE(Math::llround(a, std::numeric_limits<float>::infinity())); + ASSERT_FALSE(Math::llround(a, 1e20f /* float too large */)); + + ASSERT_TRUE(Math::llround(a, 47.005)); ASSERT_EQ(47ll, a); + ASSERT_TRUE(Math::llround(a, 46.995)); ASSERT_EQ(47ll, a); + ASSERT_TRUE(Math::llround(a, -47.005)); ASSERT_EQ(-47ll, a); + ASSERT_TRUE(Math::llround(a, -46.995)); ASSERT_EQ(-47ll, a); + + ASSERT_FALSE(Math::llround(a, std::numeric_limits<double>::quiet_NaN())); + ASSERT_FALSE(Math::llround(a, std::numeric_limits<double>::infinity())); + ASSERT_FALSE(Math::llround(a, 1e20 /* double too large */)); +}
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Tue Jan 27 13:15:50 2026 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Tue Jan 27 16:07:48 2026 +0100 @@ -69,7 +69,6 @@ #include "PluginsEnumerations.h" #include "PluginsJob.h" -#include <boost/math/special_functions/round.hpp> #include <boost/regex.hpp> #include <dcmtk/dcmdata/dcdicent.h> #include <dcmtk/dcmnet/dimse.h>
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue Jan 27 13:15:50 2026 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue Jan 27 16:07:48 2026 +0100 @@ -38,6 +38,7 @@ #include "../../../OrthancFramework/Sources/Logging.h" #include "../../../OrthancFramework/Sources/MultiThreading/Semaphore.h" #include "../../../OrthancFramework/Sources/SerializationToolbox.h" +#include "../../../OrthancFramework/Sources/CompatibilityMath.h" #include "../OrthancConfiguration.h" #include "../Search/DatabaseLookup.h" @@ -47,7 +48,6 @@ #include "../SliceOrdering.h" // This "include" is mandatory for Release builds using Linux Standard Base -#include <boost/math/special_functions/round.hpp> #include <boost/shared_ptr.hpp> #include "../../../OrthancFramework/Sources/FileStorage/StorageAccessor.h" @@ -1170,8 +1170,8 @@ ratio = static_cast<float>(argHeight) / static_cast<float>(decoded->GetHeight()); } - targetWidth = boost::math::iround(ratio * static_cast<float>(decoded->GetWidth())); - targetHeight = boost::math::iround(ratio * static_cast<float>(decoded->GetHeight())); + targetWidth = Math::iround(ratio * static_cast<float>(decoded->GetWidth())); + targetHeight = Math::iround(ratio * static_cast<float>(decoded->GetHeight())); } if (decoded->GetFormat() == PixelFormat_RGB24 || decoded->GetFormat() == PixelFormat_RGB48)
