307 lines
11 KiB
Diff
307 lines
11 KiB
Diff
From 396eef1398a87463a234e7d313d637ea67df96b5 Mon Sep 17 00:00:00 2001
|
|
From: Andrey Semashev <andrey.semashev@gmail.com>
|
|
Date: Sun, 7 May 2023 20:58:27 +0300
|
|
Subject: [PATCH] Restrict generic path comparison operators to avoid
|
|
ambiguities with std lib.
|
|
|
|
Path comparison operators that accept arbitrary path source types now require
|
|
the other argument to be exactly path. This prevents the compiler from picking
|
|
those operators when the other argument is convertible to path. This can happen
|
|
even when neither of the arguments are actually paths, e.g. when the
|
|
comparison operators are brought into the current scope by a using directive.
|
|
|
|
Fixes https://github.com/boostorg/filesystem/issues/285.
|
|
---
|
|
diff --git a/boost/filesystem/path.hpp b/boost/filesystem/path.hpp
|
|
index 61278ef74..729e18812 100644
|
|
--- a/boost/filesystem/path.hpp
|
|
+++ b/boost/filesystem/path.hpp
|
|
@@ -1287,20 +1287,26 @@ BOOST_FORCEINLINE bool operator==(path const& lhs, path const& rhs)
|
|
return lhs.compare(rhs) == 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Path, typename Source >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator==(path const& lhs, Source const& rhs)
|
|
+>::type operator==(Path const& lhs, Source const& rhs)
|
|
{
|
|
return lhs.compare(rhs) == 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Source, typename Path >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator==(Source const& lhs, path const& rhs)
|
|
+>::type operator==(Source const& lhs, Path const& rhs)
|
|
{
|
|
return rhs.compare(lhs) == 0;
|
|
}
|
|
@@ -1310,20 +1316,26 @@ BOOST_FORCEINLINE bool operator!=(path const& lhs, path const& rhs)
|
|
return lhs.compare(rhs) != 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Path, typename Source >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator!=(path const& lhs, Source const& rhs)
|
|
+>::type operator!=(Path const& lhs, Source const& rhs)
|
|
{
|
|
return lhs.compare(rhs) != 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Source, typename Path >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator!=(Source const& lhs, path const& rhs)
|
|
+>::type operator!=(Source const& lhs, Path const& rhs)
|
|
{
|
|
return rhs.compare(lhs) != 0;
|
|
}
|
|
@@ -1333,20 +1345,26 @@ BOOST_FORCEINLINE bool operator<(path const& lhs, path const& rhs)
|
|
return lhs.compare(rhs) < 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Path, typename Source >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator<(path const& lhs, Source const& rhs)
|
|
+>::type operator<(Path const& lhs, Source const& rhs)
|
|
{
|
|
return lhs.compare(rhs) < 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Source, typename Path >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator<(Source const& lhs, path const& rhs)
|
|
+>::type operator<(Source const& lhs, Path const& rhs)
|
|
{
|
|
return rhs.compare(lhs) > 0;
|
|
}
|
|
@@ -1356,20 +1374,26 @@ BOOST_FORCEINLINE bool operator<=(path const& lhs, path const& rhs)
|
|
return lhs.compare(rhs) <= 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Path, typename Source >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator<=(path const& lhs, Source const& rhs)
|
|
+>::type operator<=(Path const& lhs, Source const& rhs)
|
|
{
|
|
return lhs.compare(rhs) <= 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Source, typename Path >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator<=(Source const& lhs, path const& rhs)
|
|
+>::type operator<=(Source const& lhs, Path const& rhs)
|
|
{
|
|
return rhs.compare(lhs) >= 0;
|
|
}
|
|
@@ -1379,20 +1403,26 @@ BOOST_FORCEINLINE bool operator>(path const& lhs, path const& rhs)
|
|
return lhs.compare(rhs) > 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Path, typename Source >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator>(path const& lhs, Source const& rhs)
|
|
+>::type operator>(Path const& lhs, Source const& rhs)
|
|
{
|
|
return lhs.compare(rhs) > 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Source, typename Path >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator>(Source const& lhs, path const& rhs)
|
|
+>::type operator>(Source const& lhs, Path const& rhs)
|
|
{
|
|
return rhs.compare(lhs) < 0;
|
|
}
|
|
@@ -1402,35 +1432,41 @@ BOOST_FORCEINLINE bool operator>=(path const& lhs, path const& rhs)
|
|
return lhs.compare(rhs) >= 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Path, typename Source >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator>=(path const& lhs, Source const& rhs)
|
|
+>::type operator>=(Path const& lhs, Source const& rhs)
|
|
{
|
|
return lhs.compare(rhs) >= 0;
|
|
}
|
|
|
|
-template< typename Source >
|
|
+template< typename Source, typename Path >
|
|
BOOST_FORCEINLINE typename boost::enable_if_c<
|
|
- detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
|
|
+ boost::conjunction<
|
|
+ boost::is_same< Path, path >,
|
|
+ detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
|
|
+ >::value,
|
|
bool
|
|
->::type operator>=(Source const& lhs, path const& rhs)
|
|
+>::type operator>=(Source const& lhs, Path const& rhs)
|
|
{
|
|
return rhs.compare(lhs) <= 0;
|
|
}
|
|
|
|
|
|
// Note: Declared as a template to delay binding to Boost.ContainerHash functions and make the dependency optional
|
|
-template< typename T >
|
|
+template< typename Path >
|
|
inline typename boost::enable_if_c<
|
|
- boost::is_same< T, path >::value,
|
|
+ boost::is_same< Path, path >::value,
|
|
std::size_t
|
|
->::type hash_value(T const& p) BOOST_NOEXCEPT
|
|
+>::type hash_value(Path const& p) BOOST_NOEXCEPT
|
|
{
|
|
#ifdef BOOST_WINDOWS_API
|
|
std::size_t seed = 0u;
|
|
- for (typename T::value_type const* it = p.c_str(); *it; ++it)
|
|
+ for (typename Path::value_type const* it = p.c_str(); *it; ++it)
|
|
hash_combine(seed, *it == L'/' ? L'\\' : *it);
|
|
return seed;
|
|
#else // BOOST_POSIX_API
|
|
diff --git a/libs/filesystem/test/Jamfile.v2 b/libs/filesystem/test/Jamfile.v2
|
|
index d1ffc2a6f..5d70b08ec 100644
|
|
--- a/libs/filesystem/test/Jamfile.v2
|
|
+++ b/libs/filesystem/test/Jamfile.v2
|
|
@@ -85,6 +85,7 @@ run operations_test.cpp : : : <link>static <define>BOOST_FILESYSTEM_VERSION=4 :
|
|
run operations_unit_test.cpp : $(HERE) : : <link>shared <define>BOOST_FILESYSTEM_VERSION=4 <test-info>always_show_run_output ;
|
|
run copy_test.cpp : : : <define>BOOST_FILESYSTEM_VERSION=4 ;
|
|
compile-fail cf_path_nullptr_test.cpp ;
|
|
+compile path_operator_ambiguity.cpp : <toolset>gcc:<warnings-as-errors>on ;
|
|
run path_test.cpp : : : <link>shared <define>BOOST_FILESYSTEM_VERSION=4 ;
|
|
run path_test.cpp : : : <link>static <define>BOOST_FILESYSTEM_VERSION=4 : path_test_static ;
|
|
run path_test.cpp : : : <link>shared <define>BOOST_FILESYSTEM_VERSION=3 : path_test_v3 ;
|
|
diff --git a/libs/filesystem/test/path_operator_ambiguity.cpp b/libs/filesystem/test/path_operator_ambiguity.cpp
|
|
new file mode 100644
|
|
index 000000000..5a43a63b6
|
|
--- /dev/null
|
|
+++ b/libs/filesystem/test/path_operator_ambiguity.cpp
|
|
@@ -0,0 +1,53 @@
|
|
+// Copyright Andrey Semashev 2023
|
|
+//
|
|
+// Use, modification, and distribution is subject to the Boost Software
|
|
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
|
+// http://www.boost.org/LICENSE_1_0.txt)
|
|
+//
|
|
+// See library home page at http://www.boost.org/libs/filesystem
|
|
+//
|
|
+// This test verifies that a using directive does not introduce operator
|
|
+// ambiguity with the standard library.
|
|
+// https://github.com/boostorg/filesystem/issues/285
|
|
+
|
|
+#include <string>
|
|
+#include <boost/filesystem.hpp>
|
|
+
|
|
+using namespace boost::filesystem;
|
|
+
|
|
+bool test_eq(char* arg)
|
|
+{
|
|
+ return std::string("abc") == arg;
|
|
+}
|
|
+
|
|
+bool test_ne(char* arg)
|
|
+{
|
|
+ return std::string("def") != arg;
|
|
+}
|
|
+
|
|
+bool test_lt(char* arg)
|
|
+{
|
|
+ return std::string("ghi") < arg;
|
|
+}
|
|
+
|
|
+bool test_gt(char* arg)
|
|
+{
|
|
+ return std::string("jkl") > arg;
|
|
+}
|
|
+
|
|
+bool test_le(char* arg)
|
|
+{
|
|
+ return std::string("mno") <= arg;
|
|
+}
|
|
+
|
|
+bool test_ge(char* arg)
|
|
+{
|
|
+ return std::string("pqr") >= arg;
|
|
+}
|
|
+
|
|
+int main(int, char* argv[])
|
|
+{
|
|
+ return test_eq(argv[0]) + test_ne(argv[0]) +
|
|
+ test_lt(argv[0]) + test_gt(argv[0]) +
|
|
+ test_le(argv[0]) + test_ge(argv[0]);
|
|
+}
|