diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ea413b761b..e0a710101da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +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) + - 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/CMakeLists.txt b/CMakeLists.txt index a7da030e906..e1967feaa81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,6 +287,42 @@ endif() find_package(Threads REQUIRED) +# 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") +else() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20") + set(CMAKE_REQUIRED_LIBRARIES "stdc++") +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) +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..fa56ed28eb9 --- /dev/null +++ b/include/util/format.hpp @@ -0,0 +1,33 @@ +#ifndef OSRM_FORMAT_HPP +#define OSRM_FORMAT_HPP + +// Compatibility layer for std::format and fmt::format + +#ifdef OSRM_HAS_STD_FORMAT + +#include +#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/nodejs/CMakeLists.txt b/src/nodejs/CMakeLists.txt index ab1c123b978..f153a0a8603 100644 --- a/src/nodejs/CMakeLists.txt +++ b/src/nodejs/CMakeLists.txt @@ -16,7 +16,7 @@ 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 diff --git a/src/server/connection.cpp b/src/server/connection.cpp index acaed7ab625..a68600bf595 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,9 @@ 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", + util::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..81ae6071f6a 100644 --- a/src/util/log.cpp +++ b/src/util/log.cpp @@ -1,8 +1,9 @@ #include "util/log.hpp" +#include "util/format.hpp" #include "util/isatty.hpp" #include +#include #include -#include #include #include #include @@ -75,12 +76,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 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); +#endif }; switch (level)