Skip to content

Commit 396eef1

Browse files
committed
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 #285.
1 parent d534509 commit 396eef1

File tree

4 files changed

+131
-40
lines changed

4 files changed

+131
-40
lines changed

doc/release_history.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
<h2>1.83.0</h2>
4343
<ul>
44+
<li>Generic <code>path</code> comparison operators are now more restricted to avoid potential ambiguities when user's code contains a <code>using namespace boost::filesystem;</code> directive. (<a href="https://github.com/boostorg/filesystem/issues/285">#285</a>)</li>
4445
<li>On Windows, added another workaround for SMBv1 shares. Previously, creating a directory iterator over a directory in an SMBv1 share could fail with error code <code>ERROR_INVALID_LEVEL</code>. (<a href="https://github.com/boostorg/filesystem/issues/284">#284</a>)</li>
4546
</ul>
4647

include/boost/filesystem/path.hpp

Lines changed: 76 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,20 +1287,26 @@ BOOST_FORCEINLINE bool operator==(path const& lhs, path const& rhs)
12871287
return lhs.compare(rhs) == 0;
12881288
}
12891289

1290-
template< typename Source >
1290+
template< typename Path, typename Source >
12911291
BOOST_FORCEINLINE typename boost::enable_if_c<
1292-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1292+
boost::conjunction<
1293+
boost::is_same< Path, path >,
1294+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1295+
>::value,
12931296
bool
1294-
>::type operator==(path const& lhs, Source const& rhs)
1297+
>::type operator==(Path const& lhs, Source const& rhs)
12951298
{
12961299
return lhs.compare(rhs) == 0;
12971300
}
12981301

1299-
template< typename Source >
1302+
template< typename Source, typename Path >
13001303
BOOST_FORCEINLINE typename boost::enable_if_c<
1301-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1304+
boost::conjunction<
1305+
boost::is_same< Path, path >,
1306+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1307+
>::value,
13021308
bool
1303-
>::type operator==(Source const& lhs, path const& rhs)
1309+
>::type operator==(Source const& lhs, Path const& rhs)
13041310
{
13051311
return rhs.compare(lhs) == 0;
13061312
}
@@ -1310,20 +1316,26 @@ BOOST_FORCEINLINE bool operator!=(path const& lhs, path const& rhs)
13101316
return lhs.compare(rhs) != 0;
13111317
}
13121318

1313-
template< typename Source >
1319+
template< typename Path, typename Source >
13141320
BOOST_FORCEINLINE typename boost::enable_if_c<
1315-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1321+
boost::conjunction<
1322+
boost::is_same< Path, path >,
1323+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1324+
>::value,
13161325
bool
1317-
>::type operator!=(path const& lhs, Source const& rhs)
1326+
>::type operator!=(Path const& lhs, Source const& rhs)
13181327
{
13191328
return lhs.compare(rhs) != 0;
13201329
}
13211330

1322-
template< typename Source >
1331+
template< typename Source, typename Path >
13231332
BOOST_FORCEINLINE typename boost::enable_if_c<
1324-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1333+
boost::conjunction<
1334+
boost::is_same< Path, path >,
1335+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1336+
>::value,
13251337
bool
1326-
>::type operator!=(Source const& lhs, path const& rhs)
1338+
>::type operator!=(Source const& lhs, Path const& rhs)
13271339
{
13281340
return rhs.compare(lhs) != 0;
13291341
}
@@ -1333,20 +1345,26 @@ BOOST_FORCEINLINE bool operator<(path const& lhs, path const& rhs)
13331345
return lhs.compare(rhs) < 0;
13341346
}
13351347

1336-
template< typename Source >
1348+
template< typename Path, typename Source >
13371349
BOOST_FORCEINLINE typename boost::enable_if_c<
1338-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1350+
boost::conjunction<
1351+
boost::is_same< Path, path >,
1352+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1353+
>::value,
13391354
bool
1340-
>::type operator<(path const& lhs, Source const& rhs)
1355+
>::type operator<(Path const& lhs, Source const& rhs)
13411356
{
13421357
return lhs.compare(rhs) < 0;
13431358
}
13441359

1345-
template< typename Source >
1360+
template< typename Source, typename Path >
13461361
BOOST_FORCEINLINE typename boost::enable_if_c<
1347-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1362+
boost::conjunction<
1363+
boost::is_same< Path, path >,
1364+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1365+
>::value,
13481366
bool
1349-
>::type operator<(Source const& lhs, path const& rhs)
1367+
>::type operator<(Source const& lhs, Path const& rhs)
13501368
{
13511369
return rhs.compare(lhs) > 0;
13521370
}
@@ -1356,20 +1374,26 @@ BOOST_FORCEINLINE bool operator<=(path const& lhs, path const& rhs)
13561374
return lhs.compare(rhs) <= 0;
13571375
}
13581376

1359-
template< typename Source >
1377+
template< typename Path, typename Source >
13601378
BOOST_FORCEINLINE typename boost::enable_if_c<
1361-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1379+
boost::conjunction<
1380+
boost::is_same< Path, path >,
1381+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1382+
>::value,
13621383
bool
1363-
>::type operator<=(path const& lhs, Source const& rhs)
1384+
>::type operator<=(Path const& lhs, Source const& rhs)
13641385
{
13651386
return lhs.compare(rhs) <= 0;
13661387
}
13671388

1368-
template< typename Source >
1389+
template< typename Source, typename Path >
13691390
BOOST_FORCEINLINE typename boost::enable_if_c<
1370-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1391+
boost::conjunction<
1392+
boost::is_same< Path, path >,
1393+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1394+
>::value,
13711395
bool
1372-
>::type operator<=(Source const& lhs, path const& rhs)
1396+
>::type operator<=(Source const& lhs, Path const& rhs)
13731397
{
13741398
return rhs.compare(lhs) >= 0;
13751399
}
@@ -1379,20 +1403,26 @@ BOOST_FORCEINLINE bool operator>(path const& lhs, path const& rhs)
13791403
return lhs.compare(rhs) > 0;
13801404
}
13811405

1382-
template< typename Source >
1406+
template< typename Path, typename Source >
13831407
BOOST_FORCEINLINE typename boost::enable_if_c<
1384-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1408+
boost::conjunction<
1409+
boost::is_same< Path, path >,
1410+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1411+
>::value,
13851412
bool
1386-
>::type operator>(path const& lhs, Source const& rhs)
1413+
>::type operator>(Path const& lhs, Source const& rhs)
13871414
{
13881415
return lhs.compare(rhs) > 0;
13891416
}
13901417

1391-
template< typename Source >
1418+
template< typename Source, typename Path >
13921419
BOOST_FORCEINLINE typename boost::enable_if_c<
1393-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1420+
boost::conjunction<
1421+
boost::is_same< Path, path >,
1422+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1423+
>::value,
13941424
bool
1395-
>::type operator>(Source const& lhs, path const& rhs)
1425+
>::type operator>(Source const& lhs, Path const& rhs)
13961426
{
13971427
return rhs.compare(lhs) < 0;
13981428
}
@@ -1402,35 +1432,41 @@ BOOST_FORCEINLINE bool operator>=(path const& lhs, path const& rhs)
14021432
return lhs.compare(rhs) >= 0;
14031433
}
14041434

1405-
template< typename Source >
1435+
template< typename Path, typename Source >
14061436
BOOST_FORCEINLINE typename boost::enable_if_c<
1407-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1437+
boost::conjunction<
1438+
boost::is_same< Path, path >,
1439+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1440+
>::value,
14081441
bool
1409-
>::type operator>=(path const& lhs, Source const& rhs)
1442+
>::type operator>=(Path const& lhs, Source const& rhs)
14101443
{
14111444
return lhs.compare(rhs) >= 0;
14121445
}
14131446

1414-
template< typename Source >
1447+
template< typename Source, typename Path >
14151448
BOOST_FORCEINLINE typename boost::enable_if_c<
1416-
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >::value,
1449+
boost::conjunction<
1450+
boost::is_same< Path, path >,
1451+
detail::path_traits::is_convertible_to_path_source< typename boost::remove_cv< Source >::type >
1452+
>::value,
14171453
bool
1418-
>::type operator>=(Source const& lhs, path const& rhs)
1454+
>::type operator>=(Source const& lhs, Path const& rhs)
14191455
{
14201456
return rhs.compare(lhs) <= 0;
14211457
}
14221458

14231459

14241460
// Note: Declared as a template to delay binding to Boost.ContainerHash functions and make the dependency optional
1425-
template< typename T >
1461+
template< typename Path >
14261462
inline typename boost::enable_if_c<
1427-
boost::is_same< T, path >::value,
1463+
boost::is_same< Path, path >::value,
14281464
std::size_t
1429-
>::type hash_value(T const& p) BOOST_NOEXCEPT
1465+
>::type hash_value(Path const& p) BOOST_NOEXCEPT
14301466
{
14311467
#ifdef BOOST_WINDOWS_API
14321468
std::size_t seed = 0u;
1433-
for (typename T::value_type const* it = p.c_str(); *it; ++it)
1469+
for (typename Path::value_type const* it = p.c_str(); *it; ++it)
14341470
hash_combine(seed, *it == L'/' ? L'\\' : *it);
14351471
return seed;
14361472
#else // BOOST_POSIX_API

test/Jamfile.v2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ run operations_test.cpp : : : <link>static <define>BOOST_FILESYSTEM_VERSION=4 :
8585
run operations_unit_test.cpp : $(HERE) : : <link>shared <define>BOOST_FILESYSTEM_VERSION=4 <test-info>always_show_run_output ;
8686
run copy_test.cpp : : : <define>BOOST_FILESYSTEM_VERSION=4 ;
8787
compile-fail cf_path_nullptr_test.cpp ;
88+
compile path_operator_ambiguity.cpp : <toolset>gcc:<warnings-as-errors>on ;
8889
run path_test.cpp : : : <link>shared <define>BOOST_FILESYSTEM_VERSION=4 ;
8990
run path_test.cpp : : : <link>static <define>BOOST_FILESYSTEM_VERSION=4 : path_test_static ;
9091
run path_test.cpp : : : <link>shared <define>BOOST_FILESYSTEM_VERSION=3 : path_test_v3 ;

test/path_operator_ambiguity.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright Andrey Semashev 2023
2+
//
3+
// Use, modification, and distribution is subject to the Boost Software
4+
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
5+
// http://www.boost.org/LICENSE_1_0.txt)
6+
//
7+
// See library home page at http://www.boost.org/libs/filesystem
8+
//
9+
// This test verifies that a using directive does not introduce operator
10+
// ambiguity with the standard library.
11+
// https://github.com/boostorg/filesystem/issues/285
12+
13+
#include <string>
14+
#include <boost/filesystem.hpp>
15+
16+
using namespace boost::filesystem;
17+
18+
bool test_eq(char* arg)
19+
{
20+
return std::string("abc") == arg;
21+
}
22+
23+
bool test_ne(char* arg)
24+
{
25+
return std::string("def") != arg;
26+
}
27+
28+
bool test_lt(char* arg)
29+
{
30+
return std::string("ghi") < arg;
31+
}
32+
33+
bool test_gt(char* arg)
34+
{
35+
return std::string("jkl") > arg;
36+
}
37+
38+
bool test_le(char* arg)
39+
{
40+
return std::string("mno") <= arg;
41+
}
42+
43+
bool test_ge(char* arg)
44+
{
45+
return std::string("pqr") >= arg;
46+
}
47+
48+
int main(int, char* argv[])
49+
{
50+
return test_eq(argv[0]) + test_ne(argv[0]) +
51+
test_lt(argv[0]) + test_gt(argv[0]) +
52+
test_le(argv[0]) + test_ge(argv[0]);
53+
}

0 commit comments

Comments
 (0)