Skip to content

Commit 145f6de

Browse files
authored
Add std::format compatibility layer and fix node_osrm C++17/C++20 ABI mismatch (#7261)
1 parent 51f5bdc commit 145f6de

File tree

8 files changed

+96
-18
lines changed

8 files changed

+96
-18
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
- Build:
44
- FIXED: Reduce MSVC compiler warnings by suppressing informational warnings while preserving bug-indicating warnings [#7253](https://github.com/Project-OSRM/osrm-backend/issues/7253)
55
- Misc:
6+
- CHANGED: Add std::format compatibility layer with fallback to fmt::format [#7261](https://github.com/Project-OSRM/osrm-backend/pull/7261)
7+
- 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)
68
- CHANGED: Update fmt library to version 11.2.0 [#7238](https://github.com/Project-OSRM/osrm-backend/issues/7238)
79
- CHANGED: Upgrade protozero from v1.7.1 to v1.8.1 [#7239](https://github.com/Project-OSRM/osrm-backend/pull/7239)
810
- 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)

CMakeLists.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,42 @@ endif()
287287

288288
find_package(Threads REQUIRED)
289289

290+
# Check for C++20 <format> with full chrono support that compiles, links and runs
291+
# This is necessary because some environments have the header but incomplete
292+
# implementation (e.g., Alpine GCC 14 missing chrono formatters, or Clang
293+
# with libstdc++ from older GCC lacking std::format symbols at link time)
294+
include(CheckCXXSourceRuns)
295+
296+
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
297+
set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} /std:c++20")
298+
else()
299+
set(CMAKE_REQUIRED_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20")
300+
set(CMAKE_REQUIRED_LIBRARIES "stdc++")
301+
endif()
302+
303+
check_cxx_source_runs("
304+
#include <chrono>
305+
#include <format>
306+
#include <numbers>
307+
#include <string>
308+
int main() {
309+
// Test basic formatting
310+
std::string s1 = std::format(\"{:.10g}\", std::numbers::pi);
311+
// Test chrono formatting
312+
auto now = std::chrono::system_clock::now();
313+
auto secs = std::chrono::floor<std::chrono::seconds>(now);
314+
std::string s2 = std::format(\"{:%FT%T}\", secs);
315+
return (s1.empty() || s2.empty()) ? 1 : 0;
316+
}
317+
" OSRM_HAS_STD_FORMAT)
318+
319+
unset(CMAKE_REQUIRED_FLAGS)
320+
unset(CMAKE_REQUIRED_LIBRARIES)
321+
322+
if(OSRM_HAS_STD_FORMAT)
323+
add_definitions(-DOSRM_HAS_STD_FORMAT)
324+
endif()
325+
290326
# Third-party libraries
291327
set(RAPIDJSON_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include")
292328
include_directories(SYSTEM ${RAPIDJSON_INCLUDE_DIR})

include/util/format.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#ifndef OSRM_FORMAT_HPP
2+
#define OSRM_FORMAT_HPP
3+
4+
// Compatibility layer for std::format and fmt::format
5+
6+
#ifdef OSRM_HAS_STD_FORMAT
7+
8+
#include <cmath>
9+
#include <format>
10+
#include <string>
11+
12+
namespace osrm::util::compat
13+
{
14+
// Use C++20 std::format when available
15+
using std::format;
16+
using std::to_string;
17+
} // namespace osrm::util::compat
18+
19+
#else // Fallback to fmt library
20+
21+
#include <fmt/chrono.h>
22+
#include <fmt/format.h>
23+
24+
namespace osrm::util::compat
25+
{
26+
// Use fmt library for backward compatibility
27+
using fmt::format;
28+
using fmt::to_string;
29+
} // namespace osrm::util::compat
30+
31+
#endif // OSRM_HAS_STD_FORMAT
32+
33+
#endif // OSRM_FORMAT_HPP

include/util/json_renderer.hpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
#include <boost/assert.hpp>
1717

18-
#include <fmt/compile.h>
18+
#include "util/format.hpp"
1919

2020
namespace osrm::util::json
2121
{
@@ -50,12 +50,8 @@ template <typename Out> struct Renderer
5050
{
5151
// we don't want to print NaN or Infinity
5252
BOOST_ASSERT(std::isfinite(number.value));
53-
// `fmt::memory_buffer` stores first 500 bytes in the object itself(i.e. on stack in this
54-
// case) and then grows using heap if needed
55-
fmt::memory_buffer buffer;
56-
fmt::format_to(std::back_inserter(buffer), FMT_COMPILE("{:.10g}"), number.value);
57-
58-
write(buffer.data(), buffer.size());
53+
std::string formatted = compat::format("{:.10g}", number.value);
54+
write(formatted.data(), formatted.size());
5955
}
6056

6157
void operator()(const Object &object)

scripts/update_dependencies.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ PROTOZERO_TAG=v1.8.1
2727
VTZERO_PATH="mapbox/vtzero"
2828
VTZERO_TAG=v1.1.0
2929

30+
# Note: fmt is kept for backward compatibility with compilers lacking std::format support
31+
# (e.g., Clang with older libstdc++). Will be removed once GCC 13+ becomes minimum requirement.
3032
FMT_PATH="fmtlib/fmt"
3133
FMT_TAG=11.2.0
3234

src/nodejs/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ message(STATUS "Configuring node_osrm bindings for NodeJs ${NODEJS_VERSION}")
1616

1717

1818
add_nodejs_module(node_osrm node_osrm.cpp)
19-
set_target_properties(node_osrm PROPERTIES CXX_STANDARD 17)
19+
set_target_properties(node_osrm PROPERTIES CXX_STANDARD 20)
2020
# TODO: we disable clang-tidy for this target, because it causes errors in third-party NodeJs related headers
2121
set_target_properties(node_osrm PROPERTIES CXX_CLANG_TIDY "")
2222
# TODO: we turn off some warnings for this target, because it causes errors in third-party NodeJs related headers

src/server/connection.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
#include "server/request_handler.hpp"
33
#include "server/request_parser.hpp"
44

5+
#include "util/format.hpp"
6+
57
#include <boost/algorithm/string/predicate.hpp>
68
#include <boost/bind.hpp>
79
#include <boost/iostreams/filter/gzip.hpp>
810
#include <boost/iostreams/filtering_stream.hpp>
911

10-
#include <fmt/format.h>
1112
#include <vector>
1213

1314
namespace osrm::server
@@ -91,9 +92,9 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t
9192
{
9293
keep_alive = true;
9394
current_reply.headers.emplace_back("Connection", "keep-alive");
94-
current_reply.headers.emplace_back("Keep-Alive",
95-
"timeout=" + fmt::to_string(keepalive_timeout) +
96-
", max=" + fmt::to_string(processed_requests));
95+
current_reply.headers.emplace_back(
96+
"Keep-Alive",
97+
util::compat::format("timeout={}, max={}", keepalive_timeout, processed_requests));
9798
}
9899

99100
// compress the result w/ gzip/deflate if requested

src/util/log.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#include "util/log.hpp"
2+
#include "util/format.hpp"
23
#include "util/isatty.hpp"
34
#include <boost/algorithm/string/predicate.hpp>
5+
#include <chrono>
46
#include <cstdio>
5-
#include <fmt/chrono.h>
67
#include <iostream>
78
#include <mutex>
89
#include <string>
@@ -75,12 +76,19 @@ void Log::Init()
7576

7677
auto format = [is_terminal](const char *level, const char *color)
7778
{
79+
#ifdef OSRM_HAS_STD_FORMAT
80+
const auto now = std::chrono::system_clock::now();
81+
const auto timestamp =
82+
compat::format("{:%FT%T}", std::chrono::floor<std::chrono::seconds>(now));
83+
return compat::format("{}[{}] [{}] ", is_terminal ? color : "", timestamp, level);
84+
#else
7885
const auto timestamp = std::chrono::system_clock::now();
79-
return fmt::format("{}[{:%FT%H:%M:}{:%S}] [{}] ",
80-
is_terminal ? color : "",
81-
timestamp,
82-
timestamp.time_since_epoch(),
83-
level);
86+
return compat::format("{}[{:%FT%H:%M:}{:%S}] [{}] ",
87+
is_terminal ? color : "",
88+
timestamp,
89+
timestamp.time_since_epoch(),
90+
level);
91+
#endif
8492
};
8593

8694
switch (level)

0 commit comments

Comments
 (0)