Bug #14431
openCompilation (still) fails with C++17 filesystem and Clang in C++20 mode (or higher)
0%
Description
First reported as #14352, but the chosen solution does not actually solve the problem, only a rounding issue mentioned as part of the report.
I suggested a reduction of lines 71–83 in src/web/FileUtils.C to:
#ifdef WT_FILESYSTEM_IMPL_STD
auto ftime = Wt::cpp17::filesystem::last_write_time(file);
auto systime = decltype(ftime)::clock::to_sys(ftime);
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(systime);
#else // !WT_FILESYSTEM_IMPL_STD
return std::chrono::system_clock::from_time_t(Wt::cpp17::filesystem::last_write_time(file));
#endif // WT_FILESYSTEM_IMPL_STD
In my suggestion I pointed out that time_point_cast is a C++11 feature, but I missed that file_clock::to_sys is a C++20 feature, so I acknowledge the need for the WT_FILESYSTEM_IMPL_STD_CLOCK_17 macro to stay.
The relevant block of code is now lines 71–81 and looks like this:
#ifdef WT_FILESYSTEM_IMPL_STD
#ifndef WT_FILESYSTEM_IMPL_STD_CLOCK_17
return std::chrono::clock_cast<std::chrono::system_clock>(Wt::cpp17::filesystem::last_write_time(file));
#else // WT_FILESYSTEM_IMPL_STD_CLOCK_17
LOG_DEBUG("When using cpp17 or lower with std::filesystem, the result of this function is an approximation. Use boost::filesystem, instead of std::filesystem (see WT_CPP17_FILESYSTEM_IMPLEMENTATION) if this is a problem for your application.");
auto ftime = Wt::cpp17::filesystem::last_write_time(file);
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(ftime - Wt::cpp17::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now());
#endif //WT_FILESYSTEM_IMPL_STD_CLOCK_17
#else // !WT_FILESYSTEM_IMPL_STD
return std::chrono::system_clock::from_time_t(Wt::cpp17::filesystem::last_write_time(file));
#endif // WT_FILESYSTEM_IMPL_STD
The problem is that Clang/libc++ lacks clock_cast (even in the recently released LLVM/Clang 22). I think that clock_cast represents an improvement per se, but since it does not work with Clang, the problem is not at all gone. Therefore I would like to suggest a slightly different solution this time:
#ifdef WT_FILESYSTEM_IMPL_STD
#ifndef WT_FILESYSTEM_IMPL_STD_CLOCK_17
#if __cpp_lib_chrono >= 201803L
return std::chrono::clock_cast<std::chrono::system_clock>(Wt::cpp17::filesystem::last_write_time(file));
#else
auto ftime = Wt::cpp17::filesystem::last_write_time(file);
auto systime = decltype(ftime)::clock::to_sys(ftime);
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(systime);
#endif
#else // WT_FILESYSTEM_IMPL_STD_CLOCK_17
LOG_DEBUG("When using cpp17 or lower with std::filesystem, the result of this function is an approximation. Use boost::filesystem, instead of std::filesystem (see WT_CPP17_FILESYSTEM_IMPLEMENTATION) if this is a problem for your application.");
auto ftime = Wt::cpp17::filesystem::last_write_time(file);
return std::chrono::time_point_cast<std::chrono::system_clock::duration>(ftime - Wt::cpp17::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now());
#endif //WT_FILESYSTEM_IMPL_STD_CLOCK_17
#else // !WT_FILESYSTEM_IMPL_STD
return std::chrono::system_clock::from_time_t(Wt::cpp17::filesystem::last_write_time(file));
#endif // WT_FILESYSTEM_IMPL_STD
That is, inside the #ifndef WT_FILESYSTEM_IMPL_STD_CLOCK_17 block, use clock_cast if it is available (using the feature test macro __cpp_lib_chrono) and fall back on time_point_cast with the code modification I suggested if it is not.
The __cpp_lib_chrono macro will have the value 201803L or higher if and only if the standard library implements P0355R7 (that is where clock_cast came in) in full. As mentioned, libc++ does not do that, and thus we can see in libcxx/include/version (line 60) of libc++ on the main branch of the llvm-project repo that __cpp_lib_chrono is defined to 201611L.
Updated by Romain Mardulyn 5 days ago
- Status changed from New to InProgress
- Assignee set to Romain Mardulyn
- Target version set to 4.13.0
Updated by Romain Mardulyn 5 days ago
- Status changed from InProgress to Review
- Assignee deleted (
Romain Mardulyn)
Updated by Romain Mardulyn 5 days ago
Hi Erik,
The test for __cpp_lib_chrono will not work as GCC’s libstdc++ does also not fully implement P0355R7 yet. I however think that checking for _LIBCPP_VERSION will work.
Updated by Erik Solem 5 days ago
Hi,
Thanks for the reply. According to this table on cppreference libstdc++ implements it in full as of GCC 14. Thus, we can see that the macro is defined to 201907L in GCC 14 and 15 (see this line on master). It is correct that GCC 13 did not fully implement R0355R7, so in GCC 13 this macro is defined to 201611L. This means that with my suggestion GCC 13 users would not be able to benefit from clock_cast even though libstdc++ in GCC 13 probably has it, but what it does provide is a safe and portable way to check for this feature and have a guarantee that it is available when this macro is >= 201803L (i.e., there will be false negatives, but no false positives, so nothing breaks).
Checking for _LIBCPP_VERSION will probably work just fine and I guess it will work even when compiling with Clang against GCC's libstdc++. The drawback is just that it is a specialized check for libc++ and that when a newer libc++ version comes along, with clock_cast and the rest of R0355R7 implemented, the Wt code must be updated.