diff options
author | 2023-11-15 18:02:49 -0800 | |
---|---|---|
committer | 2023-11-15 18:03:30 -0800 | |
commit | 3bd86026c93da5a40006fd53403dff9d5f5e30e3 (patch) | |
tree | 2bb708baea92a1ba2d90e0fcdb388b759aca451e | |
parent | 20f92b10d5ae1dc03592b4b6caafec7b0e161252 (diff) |
Provide AbslHashValue for std::filesystem::path in C++17
This is somewhat tricky to implement because path equality is not
straightforward. See
https://github.com/abseil/abseil-cpp/pull/1560#issuecomment-1799983471
for discussion.
Closes #655
Closes #1560
PiperOrigin-RevId: 582863821
Change-Id: I03517a7f2003614c027c786abbfb91b6571ab662
-rw-r--r-- | absl/hash/hash_test.cc | 41 | ||||
-rw-r--r-- | absl/hash/internal/hash.h | 22 |
2 files changed, 63 insertions, 0 deletions
diff --git a/absl/hash/hash_test.cc b/absl/hash/hash_test.cc index 111f375b..71874061 100644 --- a/absl/hash/hash_test.cc +++ b/absl/hash/hash_test.cc @@ -48,6 +48,10 @@ #include "absl/types/optional.h" #include "absl/types/variant.h" +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L +#include <filesystem> // NOLINT +#endif + #ifdef ABSL_HAVE_STD_STRING_VIEW #include <string_view> #endif @@ -480,6 +484,43 @@ TEST(HashValueTest, U32StringView) { #endif } +TEST(HashValueTest, StdFilesystemPath) { +#if ABSL_INTERNAL_CPLUSPLUS_LANG < 201703L + GTEST_SKIP() << "std::filesystem::path requires C++17"; +#else + EXPECT_TRUE((is_hashable<std::filesystem::path>::value)); + + // clang-format off + const auto kTestCases = std::make_tuple( + std::filesystem::path(), + std::filesystem::path("/"), +#ifndef __GLIBCXX__ + // libstdc++ has a known issue normalizing "//". + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106452 + std::filesystem::path("//"), +#endif + std::filesystem::path("/a/b"), + std::filesystem::path("/a//b"), + std::filesystem::path("a/b"), + std::filesystem::path("a/b/"), + std::filesystem::path("a//b"), + std::filesystem::path("a//b/"), + std::filesystem::path("c:/"), + std::filesystem::path("c:\\"), + std::filesystem::path("c:\\/"), + std::filesystem::path("c:\\//"), + std::filesystem::path("c://"), + std::filesystem::path("c://\\"), + std::filesystem::path("/e/p"), + std::filesystem::path("/s/../e/p"), + std::filesystem::path("e/p"), + std::filesystem::path("s/../e/p")); + // clang-format on + + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(kTestCases)); +#endif +} + TEST(HashValueTest, StdArray) { EXPECT_TRUE((is_hashable<std::array<int, 3>>::value)); diff --git a/absl/hash/internal/hash.h b/absl/hash/internal/hash.h index e97cb315..003f3dde 100644 --- a/absl/hash/internal/hash.h +++ b/absl/hash/internal/hash.h @@ -56,6 +56,10 @@ #include "absl/types/variant.h" #include "absl/utility/utility.h" +#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L +#include <filesystem> // NOLINT +#endif + #ifdef ABSL_HAVE_STD_STRING_VIEW #include <string_view> #endif @@ -578,6 +582,24 @@ H AbslHashValue(H hash_state, std::basic_string_view<Char> str) { #endif // ABSL_HAVE_STD_STRING_VIEW +#if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L + +// Support std::filesystem::path. The SFINAE is required because some string +// types are implicitly convertible to std::filesystem::path. +template <typename Path, typename H, + typename = absl::enable_if_t< + std::is_same_v<Path, std::filesystem::path>>> +H AbslHashValue(H hash_state, const Path& path) { + // This is implemented by deferring to the standard library to compute the + // hash. The standard library requires that for two paths, `p1 == p2`, then + // `hash_value(p1) == hash_value(p2)`. `AbslHashValue` has the same + // requirement. Since `operator==` does platform specific matching, deferring + // to the standard library is the simplest approach. + return H::combine(std::move(hash_state), std::filesystem::hash_value(path)); +} + +#endif // defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L + // ----------------------------------------------------------------------------- // AbslHashValue for Sequence Containers // ----------------------------------------------------------------------------- |