From afae2b9d13d0971feec38e47f934a5a1b5f61b2a Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 11:25:32 +0200 Subject: [PATCH 01/19] Add std::format compatibility layer with fallback to fmt::format for older compilers --- CHANGELOG.md | 1 + CMakeLists.txt | 10 ++++++++++ include/util/format.hpp | 31 +++++++++++++++++++++++++++++++ include/util/json_renderer.hpp | 10 +++------- scripts/update_dependencies.sh | 2 ++ src/server/connection.cpp | 8 ++++---- src/util/log.cpp | 12 ++++++------ 7 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 include/util/format.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ea413b761b..6e810fafb8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - FIXED: Reduce MSVC compiler warnings by suppressing informational warnings while preserving bug-indicating warnings [#7253](https://github.com/Project-OSRM/osrm-backend/issues/7253) - Misc: - CHANGED: Update fmt library to version 11.2.0 [#7238](https://github.com/Project-OSRM/osrm-backend/issues/7238) + - CHANGED: Add std::format compatibility layer, using std::format when available and falling back to fmt::format for older compilers [#7251](https://github.com/Project-OSRM/osrm-backend/pull/7251) - CHANGED: Upgrade protozero from v1.7.1 to v1.8.1 [#7239](https://github.com/Project-OSRM/osrm-backend/pull/7239) - CHANGED: Replace `std::is_trivial` with `std::is_trivially_default_constructible && std::is_trivially_copyable` [#7245](https://github.com/Project-OSRM/osrm-backend/issues/7245) - ADDED: Add husky pre-commit hook for compiling and linting staged JS files [#7228](https://github.com/Project-OSRM/osrm-backend/issues/7228) diff --git a/CMakeLists.txt b/CMakeLists.txt index a7da030e906..01eebb2a9f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,6 +287,16 @@ endif() find_package(Threads REQUIRED) +# Check for C++20 header support +include(CheckIncludeFileCXX) +check_include_file_cxx("format" OSRM_HAS_STD_FORMAT) +if(OSRM_HAS_STD_FORMAT) + add_definitions(-DOSRM_HAS_STD_FORMAT) + message(STATUS "Using std::format (C++20)") +else() + message(STATUS "Using fmt::format (fallback)") +endif() + # Third-party libraries set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include") include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR}) diff --git a/include/util/format.hpp b/include/util/format.hpp new file mode 100644 index 00000000000..6f8cc5b1679 --- /dev/null +++ b/include/util/format.hpp @@ -0,0 +1,31 @@ +#ifndef OSRM_FORMAT_HPP +#define OSRM_FORMAT_HPP + +// Compatibility layer for std::format and fmt::format + +#ifdef OSRM_HAS_STD_FORMAT +#include +#include + +namespace osrm::util::compat +{ +// Use C++20 std::format when available +using std::format; +using std::to_string; +} // namespace osrm::util::compat + +#else // Fallback to fmt library + +#include +#include + +namespace osrm::util::compat +{ +// Use fmt library for backward compatibility +using fmt::format; +using fmt::to_string; +} // namespace osrm::util::compat + +#endif // OSRM_HAS_STD_FORMAT + +#endif // OSRM_FORMAT_HPP diff --git a/include/util/json_renderer.hpp b/include/util/json_renderer.hpp index 2a6b9a90ff6..45d6521cdd2 100644 --- a/include/util/json_renderer.hpp +++ b/include/util/json_renderer.hpp @@ -15,7 +15,7 @@ #include -#include +#include "util/format.hpp" namespace osrm::util::json { @@ -50,12 +50,8 @@ template struct Renderer { // we don't want to print NaN or Infinity BOOST_ASSERT(std::isfinite(number.value)); - // `fmt::memory_buffer` stores first 500 bytes in the object itself(i.e. on stack in this - // case) and then grows using heap if needed - fmt::memory_buffer buffer; - fmt::format_to(std::back_inserter(buffer), FMT_COMPILE("{:.10g}"), number.value); - - write(buffer.data(), buffer.size()); + std::string formatted = compat::format("{:.10g}", number.value); + write(formatted.data(), formatted.size()); } void operator()(const Object &object) diff --git a/scripts/update_dependencies.sh b/scripts/update_dependencies.sh index efadf875716..0ef21ce8f50 100755 --- a/scripts/update_dependencies.sh +++ b/scripts/update_dependencies.sh @@ -27,6 +27,8 @@ PROTOZERO_TAG=v1.8.1 VTZERO_PATH="mapbox/vtzero" VTZERO_TAG=v1.1.0 +# Note: fmt is kept for backward compatibility with compilers lacking std::format support +# (e.g., Clang with older libstdc++). Will be removed once GCC 13+ becomes minimum requirement. FMT_PATH="fmtlib/fmt" FMT_TAG=11.2.0 diff --git a/src/server/connection.cpp b/src/server/connection.cpp index acaed7ab625..9dff9d22072 100644 --- a/src/server/connection.cpp +++ b/src/server/connection.cpp @@ -2,12 +2,13 @@ #include "server/request_handler.hpp" #include "server/request_parser.hpp" +#include "util/format.hpp" + #include #include #include #include -#include #include namespace osrm::server @@ -91,9 +92,8 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t { keep_alive = true; current_reply.headers.emplace_back("Connection", "keep-alive"); - current_reply.headers.emplace_back("Keep-Alive", - "timeout=" + fmt::to_string(keepalive_timeout) + - ", max=" + fmt::to_string(processed_requests)); + current_reply.headers.emplace_back( + "Keep-Alive", compat::format("timeout={}, max={}", keepalive_timeout, processed_requests)); } // compress the result w/ gzip/deflate if requested diff --git a/src/util/log.cpp b/src/util/log.cpp index aee8594b7b9..6c05797b77d 100644 --- a/src/util/log.cpp +++ b/src/util/log.cpp @@ -1,8 +1,8 @@ #include "util/log.hpp" #include "util/isatty.hpp" +#include "util/format.hpp" #include #include -#include #include #include #include @@ -76,11 +76,11 @@ void Log::Init() auto format = [is_terminal](const char *level, const char *color) { const auto timestamp = std::chrono::system_clock::now(); - return fmt::format("{}[{:%FT%H:%M:}{:%S}] [{}] ", - is_terminal ? color : "", - timestamp, - timestamp.time_since_epoch(), - level); + return compat::format("{}[{:%FT%H:%M:}{:%S}] [{}] ", + is_terminal ? color : "", + timestamp, + timestamp.time_since_epoch(), + level); }; switch (level) From bf272dc7c5d517acfb46a5a2a552006da6ee6927 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 11:38:28 +0200 Subject: [PATCH 02/19] Link the PR --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e810fafb8f..11f7c3ccba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,8 @@ - Build: - FIXED: Reduce MSVC compiler warnings by suppressing informational warnings while preserving bug-indicating warnings [#7253](https://github.com/Project-OSRM/osrm-backend/issues/7253) - Misc: + - CHANGED: Add std::format compatibility layer with fallback to fmt::format [#7261](https://github.com/Project-OSRM/osrm-backend/pull/7261) - CHANGED: Update fmt library to version 11.2.0 [#7238](https://github.com/Project-OSRM/osrm-backend/issues/7238) - - CHANGED: Add std::format compatibility layer, using std::format when available and falling back to fmt::format for older compilers [#7251](https://github.com/Project-OSRM/osrm-backend/pull/7251) - CHANGED: Upgrade protozero from v1.7.1 to v1.8.1 [#7239](https://github.com/Project-OSRM/osrm-backend/pull/7239) - CHANGED: Replace `std::is_trivial` with `std::is_trivially_default_constructible && std::is_trivially_copyable` [#7245](https://github.com/Project-OSRM/osrm-backend/issues/7245) - ADDED: Add husky pre-commit hook for compiling and linting staged JS files [#7228](https://github.com/Project-OSRM/osrm-backend/issues/7228) From fc9979249f646d708de0274fb208a8475ba0545c Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 13:01:04 +0200 Subject: [PATCH 03/19] Fix the formatting --- include/util/format.hpp | 2 +- src/server/connection.cpp | 3 ++- src/util/log.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/util/format.hpp b/include/util/format.hpp index 6f8cc5b1679..8ccd2fe2a66 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -16,8 +16,8 @@ using std::to_string; #else // Fallback to fmt library -#include #include +#include namespace osrm::util::compat { diff --git a/src/server/connection.cpp b/src/server/connection.cpp index 9dff9d22072..1ba74c86c6b 100644 --- a/src/server/connection.cpp +++ b/src/server/connection.cpp @@ -93,7 +93,8 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t keep_alive = true; current_reply.headers.emplace_back("Connection", "keep-alive"); current_reply.headers.emplace_back( - "Keep-Alive", compat::format("timeout={}, max={}", keepalive_timeout, processed_requests)); + "Keep-Alive", + compat::format("timeout={}, max={}", keepalive_timeout, processed_requests)); } // compress the result w/ gzip/deflate if requested diff --git a/src/util/log.cpp b/src/util/log.cpp index 6c05797b77d..46ab0454617 100644 --- a/src/util/log.cpp +++ b/src/util/log.cpp @@ -1,6 +1,6 @@ #include "util/log.hpp" -#include "util/isatty.hpp" #include "util/format.hpp" +#include "util/isatty.hpp" #include #include #include From 92dcce91cf5b02e33feb5bb06c92c3e1916cbda8 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 13:13:52 +0200 Subject: [PATCH 04/19] Use full name --- src/server/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/connection.cpp b/src/server/connection.cpp index 1ba74c86c6b..a68600bf595 100644 --- a/src/server/connection.cpp +++ b/src/server/connection.cpp @@ -94,7 +94,7 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t current_reply.headers.emplace_back("Connection", "keep-alive"); current_reply.headers.emplace_back( "Keep-Alive", - compat::format("timeout={}, max={}", keepalive_timeout, processed_requests)); + util::compat::format("timeout={}, max={}", keepalive_timeout, processed_requests)); } // compress the result w/ gzip/deflate if requested From a61328d1155d85fe455a19823d0941e0e473e7fa Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 13:23:45 +0200 Subject: [PATCH 05/19] Fix timestamp format --- src/util/log.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/util/log.cpp b/src/util/log.cpp index 46ab0454617..c3fbcaf8480 100644 --- a/src/util/log.cpp +++ b/src/util/log.cpp @@ -75,12 +75,19 @@ void Log::Init() auto format = [is_terminal](const char *level, const char *color) { +#ifdef OSRM_HAS_STD_FORMAT + const auto now = std::chrono::system_clock::now(); + const auto timestamp = + compat::format("{:%FT%T}", std::chrono::floor(now)); + return compat::format("{}[{}] [{}] ", is_terminal ? color : "", timestamp, level); +#else const auto timestamp = std::chrono::system_clock::now(); return compat::format("{}[{:%FT%H:%M:}{:%S}] [{}] ", is_terminal ? color : "", timestamp, timestamp.time_since_epoch(), level); +#endif }; switch (level) From fb8afb8885b8dd1267a84b77aa548c9d66bd7b76 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 14:03:25 +0200 Subject: [PATCH 06/19] Fix Alpine build by testing std::format chrono support --- CMakeLists.txt | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01eebb2a9f0..45471b1c2e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,9 +287,29 @@ endif() find_package(Threads REQUIRED) -# Check for C++20 header support -include(CheckIncludeFileCXX) -check_include_file_cxx("format" OSRM_HAS_STD_FORMAT) +# Check for C++20 header with full chrono formatting support +# This is necessary because some environments have header but +# incomplete implementation (e.g., Alpine GCC 14 missing chrono formatters) +include(CheckCXXSourceCompiles) + +set(CMAKE_REQUIRED_FLAGS "-std=c++20") +check_cxx_source_compiles(" + #include + #include + #include + #include + int main() { + // Test basic formatting + std::string s1 = std::format(\"{:.10g}\", std::numbers::pi); + // Test chrono formatting + auto now = std::chrono::system_clock::now(); + auto secs = std::chrono::floor(now); + std::string s2 = std::format(\"{:%FT%T}\", secs); + return (s1.empty() || s2.empty()) ? 1 : 0; + } +" OSRM_HAS_STD_FORMAT) +unset(CMAKE_REQUIRED_FLAGS) + if(OSRM_HAS_STD_FORMAT) add_definitions(-DOSRM_HAS_STD_FORMAT) message(STATUS "Using std::format (C++20)") From a40b323174fd51c97eea9673e09d41c1d82168d7 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 14:12:34 +0200 Subject: [PATCH 07/19] Match the build flags --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 45471b1c2e1..35c153fcc20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,10 +289,11 @@ find_package(Threads REQUIRED) # Check for C++20 header with full chrono formatting support # This is necessary because some environments have header but -# incomplete implementation (e.g., Alpine GCC 14 missing chrono formatters) +# incomplete implementation (e.g., Alpine GCC 14 missing chrono formatters, +# or Clang with libstdc++ from older GCC) include(CheckCXXSourceCompiles) -set(CMAKE_REQUIRED_FLAGS "-std=c++20") +set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") check_cxx_source_compiles(" #include #include From 076ef8d61f72f5b1daa1a3f2f3dde534ad476a85 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 16:12:22 +0200 Subject: [PATCH 08/19] Add missing include --- src/util/log.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/log.cpp b/src/util/log.cpp index c3fbcaf8480..81ae6071f6a 100644 --- a/src/util/log.cpp +++ b/src/util/log.cpp @@ -2,6 +2,7 @@ #include "util/format.hpp" #include "util/isatty.hpp" #include +#include #include #include #include From ccd97848de03f88cb15fbdcc3350106ae8356f29 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 16:45:35 +0200 Subject: [PATCH 09/19] Add CMAKE_REQUIRED_LIBRARIES to the check --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 35c153fcc20..dd142500f45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -294,6 +294,7 @@ find_package(Threads REQUIRED) include(CheckCXXSourceCompiles) set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") +set(CMAKE_REQUIRED_LIBRARIES "stdc++") check_cxx_source_compiles(" #include #include @@ -310,6 +311,7 @@ check_cxx_source_compiles(" } " OSRM_HAS_STD_FORMAT) unset(CMAKE_REQUIRED_FLAGS) +unset(CMAKE_REQUIRED_LIBRARIES) if(OSRM_HAS_STD_FORMAT) add_definitions(-DOSRM_HAS_STD_FORMAT) From 46fff878161824b3a422349110619473b9f2e85a Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Wed, 15 Oct 2025 16:54:15 +0200 Subject: [PATCH 10/19] For MSVC use /std:c++20 --- CMakeLists.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd142500f45..8be1afb8bbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -293,8 +293,12 @@ find_package(Threads REQUIRED) # or Clang with libstdc++ from older GCC) include(CheckCXXSourceCompiles) -set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") -set(CMAKE_REQUIRED_LIBRARIES "stdc++") +if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20") +else() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") + set(CMAKE_REQUIRED_LIBRARIES "stdc++") +endif() check_cxx_source_compiles(" #include #include From 9d3b301521aff6b679c63a97ff56cb64a8db3806 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Thu, 16 Oct 2025 00:19:35 +0200 Subject: [PATCH 11/19] Make the CMake test more robust by verifying linking too --- CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8be1afb8bbb..3dddf41b156 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,11 +287,11 @@ endif() find_package(Threads REQUIRED) -# Check for C++20 header with full chrono formatting support -# This is necessary because some environments have header but -# incomplete implementation (e.g., Alpine GCC 14 missing chrono formatters, -# or Clang with libstdc++ from older GCC) -include(CheckCXXSourceCompiles) +# Check for C++20 with full chrono support that compiles, links and runs +# This is necessary because some environments have the header but incomplete +# implementation (e.g., Alpine GCC 14 missing chrono formatters, or Clang +# with libstdc++ from older GCC lacking std::format symbols at link time) +include(CheckCXXSourceRuns) if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20") @@ -299,7 +299,7 @@ else() set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") set(CMAKE_REQUIRED_LIBRARIES "stdc++") endif() -check_cxx_source_compiles(" +check_cxx_source_runs(" #include #include #include From 79bb649bd62650dbc588d4c9abef3be97db73e22 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Thu, 16 Oct 2025 00:28:34 +0200 Subject: [PATCH 12/19] Hardcode the versions --- CMakeLists.txt | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dddf41b156..0754ab51f48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,35 +287,23 @@ endif() find_package(Threads REQUIRED) -# Check for C++20 with full chrono support that compiles, links and runs +# Check for C++20 with full chrono support # This is necessary because some environments have the header but incomplete # implementation (e.g., Alpine GCC 14 missing chrono formatters, or Clang # with libstdc++ from older GCC lacking std::format symbols at link time) -include(CheckCXXSourceRuns) - -if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20") -else() - set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") - set(CMAKE_REQUIRED_LIBRARIES "stdc++") +# Clang with libstdc++ often has the header but not the implementation until Clang 19+ +set(OSRM_HAS_STD_FORMAT FALSE) + +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13) + # GCC 13+ has full std::format support + set(OSRM_HAS_STD_FORMAT TRUE) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19) + # Clang 19+ with libc++ has full std::format support + set(OSRM_HAS_STD_FORMAT TRUE) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND MSVC_VERSION GREATER_EQUAL 1929) + # VS 2019 16.10+ has full std::format support + set(OSRM_HAS_STD_FORMAT TRUE) endif() -check_cxx_source_runs(" - #include - #include - #include - #include - int main() { - // Test basic formatting - std::string s1 = std::format(\"{:.10g}\", std::numbers::pi); - // Test chrono formatting - auto now = std::chrono::system_clock::now(); - auto secs = std::chrono::floor(now); - std::string s2 = std::format(\"{:%FT%T}\", secs); - return (s1.empty() || s2.empty()) ? 1 : 0; - } -" OSRM_HAS_STD_FORMAT) -unset(CMAKE_REQUIRED_FLAGS) -unset(CMAKE_REQUIRED_LIBRARIES) if(OSRM_HAS_STD_FORMAT) add_definitions(-DOSRM_HAS_STD_FORMAT) From 8dbb2589dec1d2f3e551b0db2593d4f48efa2631 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Sat, 18 Oct 2025 19:21:56 +0200 Subject: [PATCH 13/19] Revert to linking a test program, but add more logs --- CMakeLists.txt | 56 +++++++++++++++++++++++++++++++---------- include/util/format.hpp | 15 +++++++++++ 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0754ab51f48..6b34b3adb59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,29 +287,59 @@ endif() find_package(Threads REQUIRED) -# Check for C++20 with full chrono support +# Check for C++20 with full chrono support that compiles, links and runs # This is necessary because some environments have the header but incomplete # implementation (e.g., Alpine GCC 14 missing chrono formatters, or Clang # with libstdc++ from older GCC lacking std::format symbols at link time) -# Clang with libstdc++ often has the header but not the implementation until Clang 19+ -set(OSRM_HAS_STD_FORMAT FALSE) - -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 13) - # GCC 13+ has full std::format support - set(OSRM_HAS_STD_FORMAT TRUE) -elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19) - # Clang 19+ with libc++ has full std::format support - set(OSRM_HAS_STD_FORMAT TRUE) -elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND MSVC_VERSION GREATER_EQUAL 1929) - # VS 2019 16.10+ has full std::format support - set(OSRM_HAS_STD_FORMAT TRUE) + +message(STATUS "=== Checking for std::format support ===") +message(STATUS "Compiler ID: ${CMAKE_CXX_COMPILER_ID}") +message(STATUS "Compiler version: ${CMAKE_CXX_COMPILER_VERSION}") +if(MSVC) + message(STATUS "MSVC_VERSION: ${MSVC_VERSION}") +endif() + +include(CheckCXXSourceRuns) + +if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20") + message(STATUS "Test flags: ${CMAKE_REQUIRED_FLAGS}") +else() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") + set(CMAKE_REQUIRED_LIBRARIES "stdc++") + message(STATUS "Test flags: ${CMAKE_REQUIRED_FLAGS}") + message(STATUS "Test libraries: ${CMAKE_REQUIRED_LIBRARIES}") endif() +message(STATUS "Running std::format test program...") +check_cxx_source_runs(" + #include + #include + #include + #include + int main() { + // Test basic formatting + std::string s1 = std::format(\"{:.10g}\", std::numbers::pi); + // Test chrono formatting + auto now = std::chrono::system_clock::now(); + auto secs = std::chrono::floor(now); + std::string s2 = std::format(\"{:%FT%T}\", secs); + return (s1.empty() || s2.empty()) ? 1 : 0; + } +" OSRM_HAS_STD_FORMAT) + +unset(CMAKE_REQUIRED_FLAGS) +unset(CMAKE_REQUIRED_LIBRARIES) + if(OSRM_HAS_STD_FORMAT) add_definitions(-DOSRM_HAS_STD_FORMAT) + message(STATUS "=== std::format test: PASSED ===") message(STATUS "Using std::format (C++20)") else() + message(STATUS "=== std::format test: FAILED ===") message(STATUS "Using fmt::format (fallback)") + message(STATUS "Reason: std::format is not available or incomplete") + message(STATUS "This is normal for older compilers or incomplete standard library implementations") endif() # Third-party libraries diff --git a/include/util/format.hpp b/include/util/format.hpp index 8ccd2fe2a66..58576d43f56 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -4,6 +4,14 @@ // Compatibility layer for std::format and fmt::format #ifdef OSRM_HAS_STD_FORMAT + +// Compile-time diagnostic: using std::format +#if defined(__GNUC__) || defined(__clang__) +#pragma message("OSRM: Using std::format from C++20 standard library") +#elif defined(_MSC_VER) +#pragma message("OSRM: Using std::format from C++20 standard library") +#endif + #include #include @@ -16,6 +24,13 @@ using std::to_string; #else // Fallback to fmt library +// Compile-time diagnostic: using fmt::format +#if defined(__GNUC__) || defined(__clang__) +#pragma message("OSRM: Using fmt::format as fallback (std::format not available)") +#elif defined(_MSC_VER) +#pragma message("OSRM: Using fmt::format as fallback (std::format not available)") +#endif + #include #include From 1839b456efa3b2ae4df1e1adf14366a304eadd2d Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Sat, 18 Oct 2025 19:39:46 +0200 Subject: [PATCH 14/19] Include Boost headers in test to match real build env --- CMakeLists.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b34b3adb59..823907a66bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -311,12 +311,21 @@ else() message(STATUS "Test libraries: ${CMAKE_REQUIRED_LIBRARIES}") endif() -message(STATUS "Running std::format test program...") +# Include Boost headers in test to match real build environment +set(CMAKE_REQUIRED_INCLUDES ${Boost_INCLUDE_DIRS}) +message(STATUS "Test includes: ${CMAKE_REQUIRED_INCLUDES}") + +message(STATUS "Running std::format test program (with Boost headers to match real build)...") check_cxx_source_runs(" + // Include Boost headers first to test for conflicts (matches real build) + #include + + // Now test std::format #include #include #include #include + int main() { // Test basic formatting std::string s1 = std::format(\"{:.10g}\", std::numbers::pi); @@ -330,6 +339,7 @@ check_cxx_source_runs(" unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) +unset(CMAKE_REQUIRED_INCLUDES) if(OSRM_HAS_STD_FORMAT) add_definitions(-DOSRM_HAS_STD_FORMAT) From d93e6e3aca11f4065c1a234db0df1d55e51ff839 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Sat, 18 Oct 2025 19:52:28 +0200 Subject: [PATCH 15/19] Disable std::format for node_osrm --- CMakeLists.txt | 12 +----------- src/nodejs/CMakeLists.txt | 8 ++++++++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 823907a66bd..6b34b3adb59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -311,21 +311,12 @@ else() message(STATUS "Test libraries: ${CMAKE_REQUIRED_LIBRARIES}") endif() -# Include Boost headers in test to match real build environment -set(CMAKE_REQUIRED_INCLUDES ${Boost_INCLUDE_DIRS}) -message(STATUS "Test includes: ${CMAKE_REQUIRED_INCLUDES}") - -message(STATUS "Running std::format test program (with Boost headers to match real build)...") +message(STATUS "Running std::format test program...") check_cxx_source_runs(" - // Include Boost headers first to test for conflicts (matches real build) - #include - - // Now test std::format #include #include #include #include - int main() { // Test basic formatting std::string s1 = std::format(\"{:.10g}\", std::numbers::pi); @@ -339,7 +330,6 @@ check_cxx_source_runs(" unset(CMAKE_REQUIRED_FLAGS) unset(CMAKE_REQUIRED_LIBRARIES) -unset(CMAKE_REQUIRED_INCLUDES) if(OSRM_HAS_STD_FORMAT) add_definitions(-DOSRM_HAS_STD_FORMAT) diff --git a/src/nodejs/CMakeLists.txt b/src/nodejs/CMakeLists.txt index ab1c123b978..d759a4eb975 100644 --- a/src/nodejs/CMakeLists.txt +++ b/src/nodejs/CMakeLists.txt @@ -23,6 +23,14 @@ set_target_properties(node_osrm PROPERTIES CXX_CLANG_TIDY "") target_no_warning(node_osrm suggest-destructor-override) target_no_warning(node_osrm suggest-override) +# WORKAROUND: Node.js bindings have issues with std::format in some environments +# (Clang with libstdc++, even when std::format technically exists). Force fallback to fmt::format. +# See: https://github.com/Project-OSRM/osrm-backend/pull/7261 +if(OSRM_HAS_STD_FORMAT) + message(STATUS "Disabling std::format for node_osrm (using fmt::format fallback)") + target_compile_options(node_osrm PRIVATE -UOSRM_HAS_STD_FORMAT) +endif() + # https://github.com/cjntaylor/node-cmake/issues/53#issuecomment-842357457 execute_process(COMMAND node -p "require('node-addon-api').include" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE NODE_ADDON_API_DIR) string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) From b1eed7731be65f16017cd24e811ba6b76d901baf Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Sat, 18 Oct 2025 20:08:27 +0200 Subject: [PATCH 16/19] Include cmath --- include/util/format.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/util/format.hpp b/include/util/format.hpp index 58576d43f56..1e76c6cb3e7 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -12,6 +12,7 @@ #pragma message("OSRM: Using std::format from C++20 standard library") #endif +#include // Ensure std::isfinite and other math functions are available #include #include From 53f9ca659d0e956a511200a496af79de2edd3402 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Sat, 18 Oct 2025 20:29:02 +0200 Subject: [PATCH 17/19] Fix format issue --- include/util/format.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/util/format.hpp b/include/util/format.hpp index 1e76c6cb3e7..02bf5520300 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -12,7 +12,7 @@ #pragma message("OSRM: Using std::format from C++20 standard library") #endif -#include // Ensure std::isfinite and other math functions are available +#include #include #include From 7a8ff503deef2d69173a324e70b1018fbef317f2 Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Sat, 18 Oct 2025 20:53:39 +0200 Subject: [PATCH 18/19] Remove debug logs --- CMakeLists.txt | 19 ------------------- include/util/format.hpp | 14 -------------- src/nodejs/CMakeLists.txt | 1 - 3 files changed, 34 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b34b3adb59..e1967feaa81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -291,27 +291,15 @@ find_package(Threads REQUIRED) # This is necessary because some environments have the header but incomplete # implementation (e.g., Alpine GCC 14 missing chrono formatters, or Clang # with libstdc++ from older GCC lacking std::format symbols at link time) - -message(STATUS "=== Checking for std::format support ===") -message(STATUS "Compiler ID: ${CMAKE_CXX_COMPILER_ID}") -message(STATUS "Compiler version: ${CMAKE_CXX_COMPILER_VERSION}") -if(MSVC) - message(STATUS "MSVC_VERSION: ${MSVC_VERSION}") -endif() - include(CheckCXXSourceRuns) if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20") - message(STATUS "Test flags: ${CMAKE_REQUIRED_FLAGS}") else() set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") set(CMAKE_REQUIRED_LIBRARIES "stdc++") - message(STATUS "Test flags: ${CMAKE_REQUIRED_FLAGS}") - message(STATUS "Test libraries: ${CMAKE_REQUIRED_LIBRARIES}") endif() -message(STATUS "Running std::format test program...") check_cxx_source_runs(" #include #include @@ -333,13 +321,6 @@ unset(CMAKE_REQUIRED_LIBRARIES) if(OSRM_HAS_STD_FORMAT) add_definitions(-DOSRM_HAS_STD_FORMAT) - message(STATUS "=== std::format test: PASSED ===") - message(STATUS "Using std::format (C++20)") -else() - message(STATUS "=== std::format test: FAILED ===") - message(STATUS "Using fmt::format (fallback)") - message(STATUS "Reason: std::format is not available or incomplete") - message(STATUS "This is normal for older compilers or incomplete standard library implementations") endif() # Third-party libraries diff --git a/include/util/format.hpp b/include/util/format.hpp index 02bf5520300..fa56ed28eb9 100644 --- a/include/util/format.hpp +++ b/include/util/format.hpp @@ -5,13 +5,6 @@ #ifdef OSRM_HAS_STD_FORMAT -// Compile-time diagnostic: using std::format -#if defined(__GNUC__) || defined(__clang__) -#pragma message("OSRM: Using std::format from C++20 standard library") -#elif defined(_MSC_VER) -#pragma message("OSRM: Using std::format from C++20 standard library") -#endif - #include #include #include @@ -25,13 +18,6 @@ using std::to_string; #else // Fallback to fmt library -// Compile-time diagnostic: using fmt::format -#if defined(__GNUC__) || defined(__clang__) -#pragma message("OSRM: Using fmt::format as fallback (std::format not available)") -#elif defined(_MSC_VER) -#pragma message("OSRM: Using fmt::format as fallback (std::format not available)") -#endif - #include #include diff --git a/src/nodejs/CMakeLists.txt b/src/nodejs/CMakeLists.txt index d759a4eb975..5166ef3c1cc 100644 --- a/src/nodejs/CMakeLists.txt +++ b/src/nodejs/CMakeLists.txt @@ -27,7 +27,6 @@ target_no_warning(node_osrm suggest-override) # (Clang with libstdc++, even when std::format technically exists). Force fallback to fmt::format. # See: https://github.com/Project-OSRM/osrm-backend/pull/7261 if(OSRM_HAS_STD_FORMAT) - message(STATUS "Disabling std::format for node_osrm (using fmt::format fallback)") target_compile_options(node_osrm PRIVATE -UOSRM_HAS_STD_FORMAT) endif() From d7041f2e9722ca7480a0b039bbb967fd3687e9db Mon Sep 17 00:00:00 2001 From: Alexander Farber Date: Sat, 18 Oct 2025 21:56:57 +0200 Subject: [PATCH 19/19] Update node_osrm to C++ 20 --- CHANGELOG.md | 1 + src/nodejs/CMakeLists.txt | 9 +-------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11f7c3ccba5..e0a710101da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - FIXED: Reduce MSVC compiler warnings by suppressing informational warnings while preserving bug-indicating warnings [#7253](https://github.com/Project-OSRM/osrm-backend/issues/7253) - Misc: - CHANGED: Add std::format compatibility layer with fallback to fmt::format [#7261](https://github.com/Project-OSRM/osrm-backend/pull/7261) + - FIXED: Update node_osrm to C++20 to fix ABI mismatch with libosrm (was overlooked in #6877) [#7261](https://github.com/Project-OSRM/osrm-backend/pull/7261) - CHANGED: Update fmt library to version 11.2.0 [#7238](https://github.com/Project-OSRM/osrm-backend/issues/7238) - CHANGED: Upgrade protozero from v1.7.1 to v1.8.1 [#7239](https://github.com/Project-OSRM/osrm-backend/pull/7239) - CHANGED: Replace `std::is_trivial` with `std::is_trivially_default_constructible && std::is_trivially_copyable` [#7245](https://github.com/Project-OSRM/osrm-backend/issues/7245) diff --git a/src/nodejs/CMakeLists.txt b/src/nodejs/CMakeLists.txt index 5166ef3c1cc..f153a0a8603 100644 --- a/src/nodejs/CMakeLists.txt +++ b/src/nodejs/CMakeLists.txt @@ -16,20 +16,13 @@ message(STATUS "Configuring node_osrm bindings for NodeJs ${NODEJS_VERSION}") add_nodejs_module(node_osrm node_osrm.cpp) -set_target_properties(node_osrm PROPERTIES CXX_STANDARD 17) +set_target_properties(node_osrm PROPERTIES CXX_STANDARD 20) # TODO: we disable clang-tidy for this target, because it causes errors in third-party NodeJs related headers set_target_properties(node_osrm PROPERTIES CXX_CLANG_TIDY "") # TODO: we turn off some warnings for this target, because it causes errors in third-party NodeJs related headers target_no_warning(node_osrm suggest-destructor-override) target_no_warning(node_osrm suggest-override) -# WORKAROUND: Node.js bindings have issues with std::format in some environments -# (Clang with libstdc++, even when std::format technically exists). Force fallback to fmt::format. -# See: https://github.com/Project-OSRM/osrm-backend/pull/7261 -if(OSRM_HAS_STD_FORMAT) - target_compile_options(node_osrm PRIVATE -UOSRM_HAS_STD_FORMAT) -endif() - # https://github.com/cjntaylor/node-cmake/issues/53#issuecomment-842357457 execute_process(COMMAND node -p "require('node-addon-api').include" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE NODE_ADDON_API_DIR) string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})