Commit 00e087fd by Derek Mauro Committed by Copybara-Service

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.

This re-lands 3bd86026 with a fix
for iOS 13 unavailability of std::filesystem::path.

Roll-forward of 524ebb7e.

Closes #655
Closes #1560

PiperOrigin-RevId: 583365100
Change-Id: Id49735c49d123e0cd6a620a2b5b5a12d94129f94
parent 8197f8f1
......@@ -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) {
#ifndef ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE
GTEST_SKIP() << "std::filesystem::path is unavailable on this platform";
#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));
......
......@@ -19,6 +19,11 @@
#ifndef ABSL_HASH_INTERNAL_HASH_H_
#define ABSL_HASH_INTERNAL_HASH_H_
#ifdef __APPLE__
#include <Availability.h>
#include <TargetConditionals.h>
#endif
#include <algorithm>
#include <array>
#include <bitset>
......@@ -56,6 +61,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 +587,28 @@ 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 && \
(!defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || \
__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 130000)
#define ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE 1
// 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 // ABSL_INTERNAL_STD_FILESYSTEM_PATH_HASH_AVAILABLE
// -----------------------------------------------------------------------------
// AbslHashValue for Sequence Containers
// -----------------------------------------------------------------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment