g++ breaking change in std::filesystem::last_write_time

喜欢而已 提交于 2021-02-08 17:38:07

问题


With recent OS upgrade I noticed that small part of my code stopped compiling - it seems the reason is switching from g++-8 to g++-9.

This code is compiling alright on g++ 8.3.0 (verified this using gcc:8.3 image from Dockerhub)

#include <filesystem>
#include <chrono>

int main() {
    namespace fs = std::filesystem;
    using namespace std::chrono_literals;
    std::filesystem::last_write_time("test", std::chrono::system_clock::now() - 5min);
}

On g++ 9.1.0 it fails with:

test.cpp: In function 'int main()':
test.cpp:8:69: error: no matching function for call to 'last_write_time(const char [5], std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >)'
    8 |  fs::last_write_time("test", std::chrono::system_clock::now() - 5min);
      |                                                                     ^
In file included from /usr/local/include/c++/9.1.0/filesystem:39,
                 from test.cpp:1:
/usr/local/include/c++/9.1.0/bits/fs_ops.h:243:19: note: candidate: 'std::filesystem::file_time_type std::filesystem::last_write_time(const std::filesystem::__cxx11::path&)'
  243 |   file_time_type  last_write_time(const path& __p);
      |                   ^~~~~~~~~~~~~~~
/usr/local/include/c++/9.1.0/bits/fs_ops.h:243:19: note:   candidate expects 1 argument, 2 provided
/usr/local/include/c++/9.1.0/bits/fs_ops.h:244:19: note: candidate: 'std::filesystem::file_time_type std::filesystem::last_write_time(const std::filesystem::__cxx11::path&, std::error_code&)'
  244 |   file_time_type  last_write_time(const path& __p, error_code& __ec) noexcept;
      |                   ^~~~~~~~~~~~~~~
In file included from /usr/local/include/c++/9.1.0/filesystem:36,
                 from test.cpp:1:
/usr/local/include/c++/9.1.0/bits/fs_fwd.h:362:47: note:   no known conversion for argument 2 from 'std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >' to 'std::error_code&'
  362 |   file_time_type last_write_time(const path&, error_code&) noexcept;
      |                                               ^~~~~~~~~~~
In file included from /usr/local/include/c++/9.1.0/filesystem:39,
                 from test.cpp:1:
/usr/local/include/c++/9.1.0/bits/fs_ops.h:245:8: note: candidate: 'void std::filesystem::last_write_time(const std::filesystem::__cxx11::path&, std::filesystem::file_time_type)'
  245 |   void last_write_time(const path& __p, file_time_type __new_time);
      |        ^~~~~~~~~~~~~~~
/usr/local/include/c++/9.1.0/bits/fs_ops.h:245:56: note:   no known conversion for argument 2 from 'time_point<std::chrono::_V2::system_clock,[...]>' to 'time_point<std::filesystem::__file_clock,[...]>'
  245 |   void last_write_time(const path& __p, file_time_type __new_time);
      |                                         ~~~~~~~~~~~~~~~^~~~~~~~~~
/usr/local/include/c++/9.1.0/bits/fs_ops.h:246:8: note: candidate: 'void std::filesystem::last_write_time(const std::filesystem::__cxx11::path&, std::filesystem::file_time_type, std::error_code&)'
  246 |   void last_write_time(const path& __p, file_time_type __new_time,
      |        ^~~~~~~~~~~~~~~
/usr/local/include/c++/9.1.0/bits/fs_ops.h:246:8: note:   candidate expects 3 arguments, 2 provided

shell returned 1

Press ENTER or type command to continue

Compile command: g++ -std=c++17 test.cpp -lstdc++-fs (even though linking stdc++-fs is unnecessary on since g++9)

My question is - what is the idiomatic use of this function the way I intended? Namely changing the last write time of a file to five minutes ago.

I understand I used this in somehow non-idiomatic way if a breaking change was introduced.


回答1:


As pointed out by @LightnessRacesinOrbit in their answer the std::filesystem::file_time_type that last_write_time takes uses an unspecified time_point type. This means it is completely legal for this to break moving from one compiler to another or even versions of the same compiler.

What you can do though is get the clock that the implementation uses, and use that yourself. std::chrono::time_point was built to take the clock type that creates it as a template parameter, and it surfaces a public clock type that represents that. So to portably get the clock, and call now on it you can use

std::filesystem::last_write_time("test", std::filesystem::file_time_type::clock::now() - 5min);
//                                       ^ give me whatever clock you use and call now on it



回答2:


This is touched on by cppreference.com's example.

Your giving a std::chrono::time_point<std::chrono::system_clock> to std::file_time_type was never portable; it just happened to work on your previous toolchain. That wasn't really your fault: you were unavoidably relying on implementation details.

C++20 has introduced a portable alternative, so you'd do:

std::filesystem::last_write_time("test", std::chrono::file_clock::now() - 5min);
//                                                    ^^^^^^^^^^

… i.e. use file_clock, rather than just hoping that the alternative is convertible to whatever implementation-defined clock is used by your toolchain.

Whether that's supported in GCC 9 I couldn't say for sure, though it doesn't look like it. If there's a workaround, I'm not aware of one (which is why the C++20 change was made).

This problem affects other people on other toolchains, too.



来源:https://stackoverflow.com/questions/56708267/g-breaking-change-in-stdfilesystemlast-write-time

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!