1 // Class filesystem::path -*- C++ -*- 2 3 // Copyright (C) 2014-2022 Free Software Foundation, Inc. 4 // 5 // This file is part of the GNU ISO C++ Library. This library is free 6 // software; you can redistribute it and/or modify it under the 7 // terms of the GNU General Public License as published by the 8 // Free Software Foundation; either version 3, or (at your option) 9 // any later version. 10 11 // This library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 16 // Under Section 7 of GPL version 3, you are granted additional 17 // permissions described in the GCC Runtime Library Exception, version 18 // 3.1, as published by the Free Software Foundation. 19 20 // You should have received a copy of the GNU General Public License and 21 // a copy of the GCC Runtime Library Exception along with this program; 22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 // <http://www.gnu.org/licenses/>. 24 25 /** @file include/bits/fs_path.h 26 * This is an internal header file, included by other library headers. 27 * Do not attempt to use it directly. @headername{filesystem} 28 */ 29 30 #ifndef _GLIBCXX_FS_PATH_H 31 #define _GLIBCXX_FS_PATH_H 1 32 33 #if __cplusplus >= 201703L 34 35 #include <type_traits> 36 #include <locale> 37 #include <iosfwd> 38 #include <iomanip> 39 #include <codecvt> 40 #include <string_view> 41 #include <system_error> 42 #include <bits/stl_algobase.h> 43 #include <bits/stl_pair.h> 44 #include <bits/locale_conv.h> 45 #include <ext/concurrence.h> 46 #include <bits/shared_ptr.h> 47 #include <bits/unique_ptr.h> 48 49 #if __cplusplus > 201703L 50 # include <compare> 51 #endif 52 53 #if defined(_WIN32) && !defined(__CYGWIN__) 54 # define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1 55 #endif 56 57 namespace std _GLIBCXX_VISIBILITY(default) 58 { 59 _GLIBCXX_BEGIN_NAMESPACE_VERSION 60 61 namespace filesystem 62 { 63 _GLIBCXX_BEGIN_NAMESPACE_CXX11 64 65 class path; 66 67 /// @cond undocumented 68 namespace __detail 69 { 70 /// @addtogroup filesystem 71 /// @{ 72 template<typename _CharT> 73 inline constexpr bool __is_encoded_char = false; 74 template<> 75 inline constexpr bool __is_encoded_char<char> = true; 76 #ifdef _GLIBCXX_USE_CHAR8_T 77 template<> 78 inline constexpr bool __is_encoded_char<char8_t> = true; 79 #endif 80 #if _GLIBCXX_USE_WCHAR_T 81 template<> 82 inline constexpr bool __is_encoded_char<wchar_t> = true; 83 #endif 84 template<> 85 inline constexpr bool __is_encoded_char<char16_t> = true; 86 template<> 87 inline constexpr bool __is_encoded_char<char32_t> = true; 88 89 #if __cpp_concepts >= 201907L 90 template<typename _Iter> 91 using __safe_iterator_traits = std::iterator_traits<_Iter>; 92 #else 93 template<typename _Iter> 94 struct __safe_iterator_traits : std::iterator_traits<_Iter> 95 { }; 96 97 // Protect against ill-formed iterator_traits specializations in C++17 98 template<> struct __safe_iterator_traits<void*> { }; 99 template<> struct __safe_iterator_traits<const void*> { }; 100 template<> struct __safe_iterator_traits<volatile void*> { }; 101 template<> struct __safe_iterator_traits<const volatile void*> { }; 102 #endif 103 104 template<typename _Iter_traits, typename = void> 105 struct __is_path_iter_src 106 : false_type 107 { }; 108 109 template<typename _Iter_traits> 110 struct __is_path_iter_src<_Iter_traits, 111 void_t<typename _Iter_traits::value_type>> 112 : bool_constant<__is_encoded_char<typename _Iter_traits::value_type>> 113 { }; 114 115 template<typename _Source> 116 inline constexpr bool __is_path_src 117 = __is_path_iter_src<iterator_traits<decay_t<_Source>>>::value; 118 119 template<> 120 inline constexpr bool __is_path_src<path> = false; 121 122 template<> 123 inline constexpr bool __is_path_src<volatile path> = false; 124 125 template<> 126 inline constexpr bool __is_path_src<void*> = false; 127 128 template<> 129 inline constexpr bool __is_path_src<const void*> = false; 130 131 template<> 132 inline constexpr bool __is_path_src<volatile void*> = false; 133 134 template<> 135 inline constexpr bool __is_path_src<const volatile void*> = false; 136 137 template<typename _CharT, typename _Traits, typename _Alloc> 138 inline constexpr bool 139 __is_path_src<basic_string<_CharT, _Traits, _Alloc>> 140 = __is_encoded_char<_CharT>; 141 142 template<typename _CharT, typename _Traits> 143 inline constexpr bool 144 __is_path_src<basic_string_view<_CharT, _Traits>> 145 = __is_encoded_char<_CharT>; 146 147 // SFINAE constraint for Source parameters as required by [fs.path.req]. 148 template<typename _Tp> 149 using _Path = enable_if_t<__is_path_src<_Tp>, path>; 150 151 // SFINAE constraint for InputIterator parameters as required by [fs.req]. 152 template<typename _Iter, typename _Tr = __safe_iterator_traits<_Iter>> 153 using _Path2 = enable_if_t<__is_path_iter_src<_Tr>::value, path>; 154 155 #if __cpp_lib_concepts 156 template<typename _Iter> 157 constexpr bool __is_contiguous = std::contiguous_iterator<_Iter>; 158 #else 159 template<typename _Iter> 160 constexpr bool __is_contiguous = false; 161 #endif 162 163 template<typename _Tp> 164 constexpr bool __is_contiguous<_Tp*> = true; 165 166 template<typename _Tp, typename _Seq> 167 constexpr bool 168 __is_contiguous<__gnu_cxx::__normal_iterator<_Tp*, _Seq>> = true; 169 170 #if !defined _GLIBCXX_FILESYSTEM_IS_WINDOWS && defined _GLIBCXX_USE_CHAR8_T 171 // For POSIX treat char8_t sequences as char without encoding conversions. 172 template<typename _EcharT> 173 using __unified_u8_t 174 = __conditional_t<is_same_v<_EcharT, char8_t>, char, _EcharT>; 175 #else 176 template<typename _EcharT> 177 using __unified_u8_t = _EcharT; 178 #endif 179 180 // The __effective_range overloads convert a Source parameter into 181 // either a basic_string_view<C> or basic_string<C> containing the 182 // effective range of the Source, as defined in [fs.path.req]. 183 184 template<typename _CharT, typename _Traits, typename _Alloc> 185 inline basic_string_view<_CharT> 186 __effective_range(const basic_string<_CharT, _Traits, _Alloc>& __source) 187 noexcept 188 { return __source; } 189 190 template<typename _CharT, typename _Traits> 191 inline basic_string_view<_CharT> 192 __effective_range(const basic_string_view<_CharT, _Traits>& __source) 193 noexcept 194 { return __source; } 195 196 // Return the effective range of an NTCTS. 197 template<typename _Source> 198 auto 199 __effective_range(const _Source& __source) 200 { 201 // Remove a level of normal/safe iterator indirection, or decay an array. 202 using _Iter = decltype(std::__niter_base(__source)); 203 using value_type = typename iterator_traits<_Iter>::value_type; 204 205 if constexpr (__is_contiguous<_Iter>) 206 return basic_string_view<value_type>{&*__source}; 207 else 208 { 209 // _Source is an input iterator that iterates over an NTCTS. 210 // Create a basic_string by reading until the null character. 211 basic_string<__unified_u8_t<value_type>> __str; 212 _Source __it = __source; 213 for (value_type __ch = *__it; __ch != value_type(); __ch = *++__it) 214 __str.push_back(__ch); 215 return __str; 216 } 217 } 218 219 // The value type of a Source parameter's effective range. 220 template<typename _Source> 221 struct __source_value_type_impl 222 { 223 using type 224 = typename __safe_iterator_traits<decay_t<_Source>>::value_type; 225 }; 226 227 template<typename _CharT, typename _Traits, typename _Alloc> 228 struct __source_value_type_impl<basic_string<_CharT, _Traits, _Alloc>> 229 { 230 using type = _CharT; 231 }; 232 233 template<typename _CharT, typename _Traits> 234 struct __source_value_type_impl<basic_string_view<_CharT, _Traits>> 235 { 236 using type = _CharT; 237 }; 238 239 // The value type of a Source parameter's effective range. 240 template<typename _Source> 241 using __source_value_t = typename __source_value_type_impl<_Source>::type; 242 243 // SFINAE helper to check that an effective range has value_type char, 244 // as required by path constructors taking a std::locale parameter. 245 // The type _Tp must have already been checked by _Path<Tp> or _Path2<_Tp>. 246 template<typename _Tp, typename _Val = __source_value_t<_Tp>> 247 using __value_type_is_char 248 = std::enable_if_t<std::is_same_v<_Val, char>, _Val>; 249 250 // As above, but also allows char8_t, as required by u8path 251 // C++20 [depr.fs.path.factory] 252 template<typename _Tp, typename _Val = __source_value_t<_Tp>> 253 using __value_type_is_char_or_char8_t 254 = std::enable_if_t<std::is_same_v<_Val, char> 255 #ifdef _GLIBCXX_USE_CHAR8_T 256 || std::is_same_v<_Val, char8_t> 257 #endif 258 , _Val>; 259 260 // Create a basic_string<C> or basic_string_view<C> from an iterator range. 261 template<typename _InputIterator> 262 inline auto 263 __string_from_range(_InputIterator __first, _InputIterator __last) 264 { 265 using _EcharT 266 = typename std::iterator_traits<_InputIterator>::value_type; 267 static_assert(__is_encoded_char<_EcharT>); // C++17 [fs.req]/3 268 269 if constexpr (__is_contiguous<_InputIterator>) 270 { 271 // For contiguous iterators we can just return a string view. 272 if (auto __len = __last - __first) [[__likely__]] 273 return basic_string_view<_EcharT>(&*__first, __len); 274 return basic_string_view<_EcharT>(); 275 } 276 else 277 { 278 // Conversion requires contiguous characters, so create a string. 279 return basic_string<__unified_u8_t<_EcharT>>(__first, __last); 280 } 281 } 282 283 /// @} group filesystem 284 } // namespace __detail 285 /// @endcond 286 287 /// @addtogroup filesystem 288 /// @{ 289 290 /// A filesystem path 291 /// @ingroup filesystem 292 class path 293 { 294 public: 295 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 296 using value_type = wchar_t; 297 static constexpr value_type preferred_separator = L'\\'; 298 #else 299 # ifdef _GLIBCXX_DOXYGEN 300 /// Windows uses wchar_t for path::value_type, POSIX uses char. 301 using value_type = __os_dependent__; 302 # else 303 using value_type = char; 304 # endif 305 static constexpr value_type preferred_separator = '/'; 306 #endif 307 using string_type = std::basic_string<value_type>; 308 309 /// path::format is ignored in this implementation 310 enum format : unsigned char { native_format, generic_format, auto_format }; 311 312 // constructors and destructor 313 314 path() noexcept { } 315 316 path(const path& __p) = default; 317 318 path(path&& __p) noexcept 319 : _M_pathname(std::move(__p._M_pathname)), 320 _M_cmpts(std::move(__p._M_cmpts)) 321 { __p.clear(); } 322 323 path(string_type&& __source, format = auto_format) 324 : _M_pathname(std::move(__source)) 325 { _M_split_cmpts(); } 326 327 template<typename _Source, 328 typename _Require = __detail::_Path<_Source>> 329 path(_Source const& __source, format = auto_format) 330 : _M_pathname(_S_convert(__detail::__effective_range(__source))) 331 { _M_split_cmpts(); } 332 333 template<typename _InputIterator, 334 typename _Require = __detail::_Path2<_InputIterator>> 335 path(_InputIterator __first, _InputIterator __last, format = auto_format) 336 : _M_pathname(_S_convert(__detail::__string_from_range(__first, __last))) 337 { _M_split_cmpts(); } 338 339 template<typename _Source, 340 typename _Require = __detail::_Path<_Source>, 341 typename _Require2 = __detail::__value_type_is_char<_Source>> 342 path(_Source const& __src, const locale& __loc, format = auto_format) 343 : _M_pathname(_S_convert_loc(__detail::__effective_range(__src), __loc)) 344 { _M_split_cmpts(); } 345 346 template<typename _InputIterator, 347 typename _Require = __detail::_Path2<_InputIterator>, 348 typename _Req2 = __detail::__value_type_is_char<_InputIterator>> 349 path(_InputIterator __first, _InputIterator __last, const locale& __loc, 350 format = auto_format) 351 : _M_pathname(_S_convert_loc(__first, __last, __loc)) 352 { _M_split_cmpts(); } 353 354 ~path() = default; 355 356 // assignments 357 358 path& operator=(const path&); 359 path& operator=(path&&) noexcept; 360 path& operator=(string_type&& __source); 361 path& assign(string_type&& __source); 362 363 template<typename _Source> 364 __detail::_Path<_Source>& 365 operator=(_Source const& __source) 366 { return *this = path(__source); } 367 368 template<typename _Source> 369 __detail::_Path<_Source>& 370 assign(_Source const& __source) 371 { return *this = path(__source); } 372 373 template<typename _InputIterator> 374 __detail::_Path2<_InputIterator>& 375 assign(_InputIterator __first, _InputIterator __last) 376 { return *this = path(__first, __last); } 377 378 // appends 379 380 path& operator/=(const path& __p); 381 382 template<typename _Source> 383 __detail::_Path<_Source>& 384 operator/=(_Source const& __source) 385 { 386 _M_append(_S_convert(__detail::__effective_range(__source))); 387 return *this; 388 } 389 390 template<typename _Source> 391 __detail::_Path<_Source>& 392 append(_Source const& __source) 393 { 394 _M_append(_S_convert(__detail::__effective_range(__source))); 395 return *this; 396 } 397 398 template<typename _InputIterator> 399 __detail::_Path2<_InputIterator>& 400 append(_InputIterator __first, _InputIterator __last) 401 { 402 _M_append(_S_convert(__detail::__string_from_range(__first, __last))); 403 return *this; 404 } 405 406 // concatenation 407 408 path& operator+=(const path& __x); 409 path& operator+=(const string_type& __x); 410 path& operator+=(const value_type* __x); 411 path& operator+=(value_type __x); 412 path& operator+=(basic_string_view<value_type> __x); 413 414 template<typename _Source> 415 __detail::_Path<_Source>& 416 operator+=(_Source const& __x) { return concat(__x); } 417 418 template<typename _CharT> 419 __detail::_Path2<_CharT*>& 420 operator+=(_CharT __x); 421 422 template<typename _Source> 423 __detail::_Path<_Source>& 424 concat(_Source const& __x) 425 { 426 _M_concat(_S_convert(__detail::__effective_range(__x))); 427 return *this; 428 } 429 430 template<typename _InputIterator> 431 __detail::_Path2<_InputIterator>& 432 concat(_InputIterator __first, _InputIterator __last) 433 { 434 _M_concat(_S_convert(__detail::__string_from_range(__first, __last))); 435 return *this; 436 } 437 438 // modifiers 439 440 void clear() noexcept { _M_pathname.clear(); _M_split_cmpts(); } 441 442 path& make_preferred(); 443 path& remove_filename(); 444 path& replace_filename(const path& __replacement); 445 path& replace_extension(const path& __replacement = path()); 446 447 void swap(path& __rhs) noexcept; 448 449 // native format observers 450 451 const string_type& native() const noexcept { return _M_pathname; } 452 const value_type* c_str() const noexcept { return _M_pathname.c_str(); } 453 operator string_type() const { return _M_pathname; } 454 455 template<typename _CharT, typename _Traits = std::char_traits<_CharT>, 456 typename _Allocator = std::allocator<_CharT>> 457 std::basic_string<_CharT, _Traits, _Allocator> 458 string(const _Allocator& __a = _Allocator()) const; 459 460 std::string string() const; 461 #if _GLIBCXX_USE_WCHAR_T 462 std::wstring wstring() const; 463 #endif 464 #ifdef _GLIBCXX_USE_CHAR8_T 465 __attribute__((__abi_tag__("__u8"))) 466 std::u8string u8string() const; 467 #else 468 std::string u8string() const; 469 #endif // _GLIBCXX_USE_CHAR8_T 470 std::u16string u16string() const; 471 std::u32string u32string() const; 472 473 // generic format observers 474 template<typename _CharT, typename _Traits = std::char_traits<_CharT>, 475 typename _Allocator = std::allocator<_CharT>> 476 std::basic_string<_CharT, _Traits, _Allocator> 477 generic_string(const _Allocator& __a = _Allocator()) const; 478 479 std::string generic_string() const; 480 #if _GLIBCXX_USE_WCHAR_T 481 std::wstring generic_wstring() const; 482 #endif 483 #ifdef _GLIBCXX_USE_CHAR8_T 484 __attribute__((__abi_tag__("__u8"))) 485 std::u8string generic_u8string() const; 486 #else 487 std::string generic_u8string() const; 488 #endif // _GLIBCXX_USE_CHAR8_T 489 std::u16string generic_u16string() const; 490 std::u32string generic_u32string() const; 491 492 // compare 493 494 int compare(const path& __p) const noexcept; 495 int compare(const string_type& __s) const noexcept; 496 int compare(const value_type* __s) const noexcept; 497 int compare(basic_string_view<value_type> __s) const noexcept; 498 499 // decomposition 500 501 path root_name() const; 502 path root_directory() const; 503 path root_path() const; 504 path relative_path() const; 505 path parent_path() const; 506 path filename() const; 507 path stem() const; 508 path extension() const; 509 510 // query 511 512 [[nodiscard]] bool empty() const noexcept { return _M_pathname.empty(); } 513 bool has_root_name() const noexcept; 514 bool has_root_directory() const noexcept; 515 bool has_root_path() const noexcept; 516 bool has_relative_path() const noexcept; 517 bool has_parent_path() const noexcept; 518 bool has_filename() const noexcept; 519 bool has_stem() const noexcept; 520 bool has_extension() const noexcept; 521 bool is_absolute() const noexcept; 522 bool is_relative() const noexcept { return !is_absolute(); } 523 524 // generation 525 path lexically_normal() const; 526 path lexically_relative(const path& base) const; 527 path lexically_proximate(const path& base) const; 528 529 // iterators 530 class iterator; 531 using const_iterator = iterator; 532 533 iterator begin() const noexcept; 534 iterator end() const noexcept; 535 536 /// Write a path to a stream 537 template<typename _CharT, typename _Traits> 538 friend std::basic_ostream<_CharT, _Traits>& 539 operator<<(std::basic_ostream<_CharT, _Traits>& __os, const path& __p) 540 { 541 __os << std::quoted(__p.string<_CharT, _Traits>()); 542 return __os; 543 } 544 545 /// Read a path from a stream 546 template<typename _CharT, typename _Traits> 547 friend std::basic_istream<_CharT, _Traits>& 548 operator>>(std::basic_istream<_CharT, _Traits>& __is, path& __p) 549 { 550 std::basic_string<_CharT, _Traits> __tmp; 551 if (__is >> std::quoted(__tmp)) 552 __p = std::move(__tmp); 553 return __is; 554 } 555 556 // non-member operators 557 558 /// Compare paths 559 friend bool operator==(const path& __lhs, const path& __rhs) noexcept 560 { return path::_S_compare(__lhs, __rhs) == 0; } 561 562 #if __cpp_lib_three_way_comparison 563 /// Compare paths 564 friend strong_ordering 565 operator<=>(const path& __lhs, const path& __rhs) noexcept 566 { return path::_S_compare(__lhs, __rhs) <=> 0; } 567 #else 568 /// Compare paths 569 friend bool operator!=(const path& __lhs, const path& __rhs) noexcept 570 { return !(__lhs == __rhs); } 571 572 /// Compare paths 573 friend bool operator<(const path& __lhs, const path& __rhs) noexcept 574 { return __lhs.compare(__rhs) < 0; } 575 576 /// Compare paths 577 friend bool operator<=(const path& __lhs, const path& __rhs) noexcept 578 { return !(__rhs < __lhs); } 579 580 /// Compare paths 581 friend bool operator>(const path& __lhs, const path& __rhs) noexcept 582 { return __rhs < __lhs; } 583 584 /// Compare paths 585 friend bool operator>=(const path& __lhs, const path& __rhs) noexcept 586 { return !(__lhs < __rhs); } 587 #endif 588 589 /// Append one path to another 590 friend path operator/(const path& __lhs, const path& __rhs) 591 { 592 path __result(__lhs); 593 __result /= __rhs; 594 return __result; 595 } 596 597 private: 598 enum class _Type : unsigned char { 599 _Multi = 0, _Root_name, _Root_dir, _Filename 600 }; 601 602 path(basic_string_view<value_type> __str, _Type __type); 603 604 enum class _Split { _Stem, _Extension }; 605 606 void _M_append(basic_string_view<value_type>); 607 void _M_concat(basic_string_view<value_type>); 608 609 pair<const string_type*, size_t> _M_find_extension() const noexcept; 610 611 // path::_S_convert creates a basic_string<value_type> or 612 // basic_string_view<value_type> from a basic_string<C> or 613 // basic_string_view<C>, for an encoded character type C, 614 // performing the conversions required by [fs.path.type.cvt]. 615 template<typename _Tp> 616 static auto 617 _S_convert(_Tp __str) 618 noexcept(is_same_v<typename _Tp::value_type, value_type>) 619 { 620 if constexpr (is_same_v<typename _Tp::value_type, value_type>) 621 return __str; // No conversion needed. 622 #if !defined _GLIBCXX_FILESYSTEM_IS_WINDOWS && defined _GLIBCXX_USE_CHAR8_T 623 else if constexpr (is_same_v<_Tp, std::u8string>) 624 // Calling _S_convert<char8_t> will return a u8string_view that 625 // refers to __str and would dangle after this function returns. 626 // Return a string_type instead, to avoid dangling. 627 return string_type(_S_convert(__str.data(), 628 __str.data() + __str.size())); 629 #endif 630 else 631 return _S_convert(__str.data(), __str.data() + __str.size()); 632 } 633 634 template<typename _EcharT> 635 static auto 636 _S_convert(const _EcharT* __first, const _EcharT* __last); 637 638 // _S_convert_loc converts a range of char to string_type, using the 639 // supplied locale for encoding conversions. 640 641 static string_type 642 _S_convert_loc(const char* __first, const char* __last, 643 const std::locale& __loc); 644 645 template<typename _Iter> 646 static string_type 647 _S_convert_loc(_Iter __first, _Iter __last, const std::locale& __loc) 648 { 649 const auto __s = __detail::__string_from_range(__first, __last); 650 return _S_convert_loc(__s.data(), __s.data() + __s.size(), __loc); 651 } 652 653 template<typename _Tp> 654 static string_type 655 _S_convert_loc(const _Tp& __s, const std::locale& __loc) 656 { 657 return _S_convert_loc(__s.data(), __s.data() + __s.size(), __loc); 658 } 659 660 template<typename _CharT, typename _Traits, typename _Allocator> 661 static basic_string<_CharT, _Traits, _Allocator> 662 _S_str_convert(basic_string_view<value_type>, const _Allocator&); 663 664 // Returns lhs.compare(rhs), but defined after path::iterator is complete. 665 __attribute__((__always_inline__)) 666 static int 667 _S_compare(const path& __lhs, const path& __rhs) noexcept; 668 669 void _M_split_cmpts(); 670 671 _Type _M_type() const noexcept { return _M_cmpts.type(); } 672 673 string_type _M_pathname; 674 675 struct _Cmpt; 676 677 struct _List 678 { 679 using value_type = _Cmpt; 680 using iterator = value_type*; 681 using const_iterator = const value_type*; 682 683 _List(); 684 _List(const _List&); 685 _List(_List&&) = default; 686 _List& operator=(const _List&); 687 _List& operator=(_List&&) = default; 688 ~_List() = default; 689 690 _Type type() const noexcept 691 { return _Type(reinterpret_cast<uintptr_t>(_M_impl.get()) & 0x3); } 692 693 void type(_Type) noexcept; 694 695 int size() const noexcept; // zero unless type() == _Type::_Multi 696 bool empty() const noexcept; // true unless type() == _Type::_Multi 697 void clear(); 698 void swap(_List& __l) noexcept { _M_impl.swap(__l._M_impl); } 699 int capacity() const noexcept; 700 void reserve(int, bool); ///< @pre type() == _Type::_Multi 701 702 // All the member functions below here have a precondition !empty() 703 // (and they should only be called from within the library). 704 705 iterator begin() noexcept; 706 iterator end() noexcept; 707 const_iterator begin() const noexcept; 708 const_iterator end() const noexcept; 709 710 value_type& front() noexcept; 711 value_type& back() noexcept; 712 const value_type& front() const noexcept; 713 const value_type& back() const noexcept; 714 715 void pop_back(); 716 void _M_erase_from(const_iterator __pos); // erases [__pos,end()) 717 718 struct _Impl; 719 struct _Impl_deleter 720 { 721 void operator()(_Impl*) const noexcept; 722 }; 723 unique_ptr<_Impl, _Impl_deleter> _M_impl; 724 }; 725 _List _M_cmpts; 726 727 struct _Parser; 728 729 template<typename _EcharT> struct _Codecvt; 730 }; 731 732 /// @{ 733 /// @relates std::filesystem::path 734 735 #if __cpp_concepts >= 201907L 736 // Workaround for PR libstdc++/106201 737 inline void 738 swap(same_as<path> auto& __lhs, same_as<path> auto& __rhs) noexcept 739 { __lhs.swap(__rhs); } 740 #else 741 inline void swap(path& __lhs, path& __rhs) noexcept { __lhs.swap(__rhs); } 742 #endif 743 744 size_t hash_value(const path& __p) noexcept; 745 746 /// @} 747 748 /// Exception type thrown by the Filesystem library 749 class filesystem_error : public std::system_error 750 { 751 public: 752 filesystem_error(const string& __what_arg, error_code __ec); 753 754 filesystem_error(const string& __what_arg, const path& __p1, 755 error_code __ec); 756 757 filesystem_error(const string& __what_arg, const path& __p1, 758 const path& __p2, error_code __ec); 759 760 filesystem_error(const filesystem_error&) = default; 761 filesystem_error& operator=(const filesystem_error&) = default; 762 763 // No move constructor or assignment operator. 764 // Copy rvalues instead, so that _M_impl is not left empty. 765 766 ~filesystem_error(); 767 768 const path& path1() const noexcept; 769 const path& path2() const noexcept; 770 const char* what() const noexcept; 771 772 private: 773 struct _Impl; 774 std::__shared_ptr<const _Impl> _M_impl; 775 }; 776 777 /// @cond undocumented 778 namespace __detail 779 { 780 [[noreturn]] inline void 781 __throw_conversion_error() 782 { 783 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 784 "Cannot convert character sequence", 785 std::make_error_code(errc::illegal_byte_sequence))); 786 } 787 788 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 789 template<typename _Tp> 790 inline std::wstring 791 __wstr_from_utf8(const _Tp& __str) 792 { 793 static_assert(std::is_same_v<typename _Tp::value_type, char>); 794 std::wstring __wstr; 795 // XXX This assumes native wide encoding is UTF-16. 796 std::codecvt_utf8_utf16<wchar_t> __wcvt; 797 const auto __p = __str.data(); 798 if (!__str_codecvt_in_all(__p, __p + __str.size(), __wstr, __wcvt)) 799 __detail::__throw_conversion_error(); 800 return __wstr; 801 } 802 #endif 803 804 } // namespace __detail 805 /// @endcond 806 807 808 /** Create a path from a UTF-8-encoded sequence of char 809 * 810 * @relates std::filesystem::path 811 */ 812 template<typename _InputIterator, 813 typename _Require = __detail::_Path2<_InputIterator>, 814 typename _CharT 815 = __detail::__value_type_is_char_or_char8_t<_InputIterator>> 816 inline path 817 u8path(_InputIterator __first, _InputIterator __last) 818 { 819 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 820 if constexpr (is_same_v<_CharT, char>) 821 return path{ __detail::__wstr_from_utf8( 822 __detail::__string_from_range(__first, __last)) }; 823 else 824 return path{ __first, __last }; // constructor handles char8_t 825 #else 826 // This assumes native normal encoding is UTF-8. 827 return path{ __first, __last }; 828 #endif 829 } 830 831 /** Create a path from a UTF-8-encoded sequence of char 832 * 833 * @relates std::filesystem::path 834 */ 835 template<typename _Source, 836 typename _Require = __detail::_Path<_Source>, 837 typename _CharT = __detail::__value_type_is_char_or_char8_t<_Source>> 838 inline path 839 u8path(const _Source& __source) 840 { 841 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 842 if constexpr (is_same_v<_CharT, char>) 843 return path{ __detail::__wstr_from_utf8( 844 __detail::__effective_range(__source)) }; 845 else 846 return path{ __source }; // constructor handles char8_t 847 #else 848 // This assumes native normal encoding is UTF-8. 849 return path{ __source }; 850 #endif 851 } 852 853 /// @cond undocumented 854 855 struct path::_Cmpt : path 856 { 857 _Cmpt(basic_string_view<value_type> __s, _Type __t, size_t __pos); 858 859 _Cmpt() : _M_pos(-1) { } 860 861 size_t _M_pos; 862 }; 863 864 // path::_Codecvt<C> Performs conversions between C and path::string_type. 865 // The native encoding of char strings is the OS-dependent current 866 // encoding for pathnames. FIXME: We assume this is UTF-8 everywhere, 867 // but should use a Windows API to query it. 868 869 // Converts between native pathname encoding and char16_t or char32_t. 870 template<typename _EcharT> 871 struct path::_Codecvt 872 // Need derived class here because std::codecvt has protected destructor. 873 : std::codecvt<_EcharT, char, mbstate_t> 874 { }; 875 876 // Converts between native pathname encoding and native wide encoding. 877 // The native encoding for wide strings is the execution wide-character 878 // set encoding. FIXME: We assume that this is either UTF-32 or UTF-16 879 // (depending on the width of wchar_t). That matches GCC's default, 880 // but can be changed with -fwide-exec-charset. 881 // We need a custom codecvt converting the native pathname encoding 882 // to/from the native wide encoding. 883 template<> 884 struct path::_Codecvt<wchar_t> 885 : __conditional_t<sizeof(wchar_t) == sizeof(char32_t), 886 std::codecvt_utf8<wchar_t>, // UTF-8 <-> UTF-32 887 std::codecvt_utf8_utf16<wchar_t>> // UTF-8 <-> UTF-16 888 { }; 889 890 template<typename _EcharT> 891 auto 892 path::_S_convert(const _EcharT* __f, const _EcharT* __l) 893 { 894 static_assert(__detail::__is_encoded_char<_EcharT>); 895 896 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 897 # define _GLIBCXX_CONV_FROM_UTF8(S) __detail::__wstr_from_utf8(S) 898 #else 899 # define _GLIBCXX_CONV_FROM_UTF8(S) S 900 #endif 901 902 if constexpr (is_same_v<_EcharT, value_type>) 903 return basic_string_view<value_type>(__f, __l - __f); 904 #ifdef _GLIBCXX_USE_CHAR8_T 905 else if constexpr (is_same_v<_EcharT, char8_t>) 906 { 907 string_view __str(reinterpret_cast<const char*>(__f), __l - __f); 908 return _GLIBCXX_CONV_FROM_UTF8(__str); 909 } 910 #endif 911 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 912 else if constexpr (is_same_v<_EcharT, char>) 913 { 914 std::wstring __wstr; 915 path::_Codecvt<wchar_t> __cvt; 916 if (__str_codecvt_in_all(__f, __l, __wstr, __cvt)) 917 return __wstr; 918 } 919 #endif 920 else 921 { 922 path::_Codecvt<_EcharT> __cvt; 923 std::string __str; 924 if (__str_codecvt_out_all(__f, __l, __str, __cvt)) 925 return _GLIBCXX_CONV_FROM_UTF8(__str); 926 } 927 __detail::__throw_conversion_error(); 928 } 929 #undef _GLIBCXX_CONV_FROM_UTF8 930 931 /// @endcond 932 933 /// An iterator for the components of a path 934 class path::iterator 935 { 936 public: 937 using difference_type = std::ptrdiff_t; 938 using value_type = path; 939 using reference = const path&; 940 using pointer = const path*; 941 using iterator_category = std::bidirectional_iterator_tag; 942 943 iterator() noexcept : _M_path(nullptr), _M_cur(), _M_at_end() { } 944 945 iterator(const iterator&) = default; 946 iterator& operator=(const iterator&) = default; 947 948 reference operator*() const noexcept; 949 pointer operator->() const noexcept { return std::__addressof(**this); } 950 951 iterator& operator++() noexcept; 952 953 iterator operator++(int) noexcept 954 { auto __tmp = *this; ++*this; return __tmp; } 955 956 iterator& operator--() noexcept; 957 958 iterator operator--(int) noexcept 959 { auto __tmp = *this; --*this; return __tmp; } 960 961 friend bool 962 operator==(const iterator& __lhs, const iterator& __rhs) noexcept 963 { return __lhs._M_equals(__rhs); } 964 965 friend bool 966 operator!=(const iterator& __lhs, const iterator& __rhs) noexcept 967 { return !__lhs._M_equals(__rhs); } 968 969 private: 970 friend class path; 971 972 bool 973 _M_is_multi() const noexcept 974 { return _M_path->_M_type() == _Type::_Multi; } 975 976 friend difference_type 977 __path_iter_distance(const iterator& __first, const iterator& __last) 978 noexcept 979 { 980 __glibcxx_assert(__first._M_path != nullptr); 981 __glibcxx_assert(__first._M_path == __last._M_path); 982 if (__first._M_is_multi()) 983 return std::distance(__first._M_cur, __last._M_cur); 984 else if (__first._M_at_end == __last._M_at_end) 985 return 0; 986 else 987 return __first._M_at_end ? -1 : 1; 988 } 989 990 friend void 991 __path_iter_advance(iterator& __i, difference_type __n) noexcept 992 { 993 if (__n == 1) 994 ++__i; 995 else if (__n == -1) 996 --__i; 997 else if (__n != 0) 998 { 999 __glibcxx_assert(__i._M_path != nullptr); 1000 __glibcxx_assert(__i._M_is_multi()); 1001 // __glibcxx_assert(__i._M_path->_M_cmpts.end() - __i._M_cur >= __n); 1002 __i._M_cur += __n; 1003 } 1004 } 1005 1006 iterator(const path* __path, path::_List::const_iterator __iter) noexcept 1007 : _M_path(__path), _M_cur(__iter), _M_at_end() 1008 { } 1009 1010 iterator(const path* __path, bool __at_end) noexcept 1011 : _M_path(__path), _M_cur(), _M_at_end(__at_end) 1012 { } 1013 1014 bool _M_equals(iterator) const noexcept; 1015 1016 const path* _M_path; 1017 path::_List::const_iterator _M_cur; 1018 bool _M_at_end; // only used when type != _Multi 1019 }; 1020 1021 1022 inline path& 1023 path::operator=(path&& __p) noexcept 1024 { 1025 if (&__p == this) [[__unlikely__]] 1026 return *this; 1027 1028 _M_pathname = std::move(__p._M_pathname); 1029 _M_cmpts = std::move(__p._M_cmpts); 1030 __p.clear(); 1031 return *this; 1032 } 1033 1034 inline path& 1035 path::operator=(string_type&& __source) 1036 { return *this = path(std::move(__source)); } 1037 1038 inline path& 1039 path::assign(string_type&& __source) 1040 { return *this = path(std::move(__source)); } 1041 1042 inline path& 1043 path::operator+=(const string_type& __x) 1044 { 1045 _M_concat(__x); 1046 return *this; 1047 } 1048 1049 inline path& 1050 path::operator+=(const value_type* __x) 1051 { 1052 _M_concat(__x); 1053 return *this; 1054 } 1055 1056 inline path& 1057 path::operator+=(value_type __x) 1058 { 1059 _M_concat(basic_string_view<value_type>(&__x, 1)); 1060 return *this; 1061 } 1062 1063 inline path& 1064 path::operator+=(basic_string_view<value_type> __x) 1065 { 1066 _M_concat(__x); 1067 return *this; 1068 } 1069 1070 template<typename _CharT> 1071 inline __detail::_Path2<_CharT*>& 1072 path::operator+=(const _CharT __x) 1073 { 1074 _M_concat(_S_convert(&__x, &__x + 1)); 1075 return *this; 1076 } 1077 1078 inline path& 1079 path::make_preferred() 1080 { 1081 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1082 auto __pos = _M_pathname.find(L'/'); 1083 while (__pos != _M_pathname.npos) 1084 { 1085 _M_pathname[__pos] = preferred_separator; 1086 __pos = _M_pathname.find(L'/', __pos); 1087 } 1088 #endif 1089 return *this; 1090 } 1091 1092 inline void path::swap(path& __rhs) noexcept 1093 { 1094 _M_pathname.swap(__rhs._M_pathname); 1095 _M_cmpts.swap(__rhs._M_cmpts); 1096 } 1097 1098 /// @cond undocumented 1099 template<typename _CharT, typename _Traits, typename _Allocator> 1100 std::basic_string<_CharT, _Traits, _Allocator> 1101 path::_S_str_convert(basic_string_view<value_type> __str, 1102 const _Allocator& __a) 1103 { 1104 static_assert(!is_same_v<_CharT, value_type>); 1105 1106 using _WString = basic_string<_CharT, _Traits, _Allocator>; 1107 1108 if (__str.size() == 0) 1109 return _WString(__a); 1110 1111 #ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1112 string_view __u8str = __str; 1113 #else 1114 // First convert native string from UTF-16 to to UTF-8. 1115 // XXX This assumes that the execution wide-character set is UTF-16. 1116 std::codecvt_utf8_utf16<value_type> __cvt; 1117 1118 using _CharAlloc = __alloc_rebind<_Allocator, char>; 1119 using _String = basic_string<char, char_traits<char>, _CharAlloc>; 1120 _String __u8str{_CharAlloc{__a}}; 1121 const value_type* __wfirst = __str.data(); 1122 const value_type* __wlast = __wfirst + __str.size(); 1123 if (!__str_codecvt_out_all(__wfirst, __wlast, __u8str, __cvt)) 1124 __detail::__throw_conversion_error(); 1125 if constexpr (is_same_v<_CharT, char>) 1126 return __u8str; // XXX assumes native ordinary encoding is UTF-8. 1127 else 1128 #endif 1129 { 1130 const char* __first = __u8str.data(); 1131 const char* __last = __first + __u8str.size(); 1132 1133 // Convert UTF-8 string to requested format. 1134 #ifdef _GLIBCXX_USE_CHAR8_T 1135 if constexpr (is_same_v<_CharT, char8_t>) 1136 return _WString(__first, __last, __a); 1137 else 1138 #endif 1139 { 1140 // Convert UTF-8 to wide string. 1141 _WString __wstr(__a); 1142 path::_Codecvt<_CharT> __cvt; 1143 if (__str_codecvt_in_all(__first, __last, __wstr, __cvt)) 1144 return __wstr; 1145 } 1146 } 1147 __detail::__throw_conversion_error(); 1148 } 1149 /// @endcond 1150 1151 template<typename _CharT, typename _Traits, typename _Allocator> 1152 inline basic_string<_CharT, _Traits, _Allocator> 1153 path::string(const _Allocator& __a) const 1154 { 1155 if constexpr (is_same_v<_CharT, value_type>) 1156 return { _M_pathname.c_str(), _M_pathname.length(), __a }; 1157 else 1158 return _S_str_convert<_CharT, _Traits>(_M_pathname, __a); 1159 } 1160 1161 inline std::string 1162 path::string() const { return string<char>(); } 1163 1164 #if _GLIBCXX_USE_WCHAR_T 1165 inline std::wstring 1166 path::wstring() const { return string<wchar_t>(); } 1167 #endif 1168 1169 #ifdef _GLIBCXX_USE_CHAR8_T 1170 inline std::u8string 1171 path::u8string() const { return string<char8_t>(); } 1172 #else 1173 inline std::string 1174 path::u8string() const 1175 { 1176 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1177 std::string __str; 1178 // convert from native wide encoding (assumed to be UTF-16) to UTF-8 1179 std::codecvt_utf8_utf16<value_type> __cvt; 1180 const value_type* __first = _M_pathname.data(); 1181 const value_type* __last = __first + _M_pathname.size(); 1182 if (__str_codecvt_out_all(__first, __last, __str, __cvt)) 1183 return __str; 1184 __detail::__throw_conversion_error(); 1185 #else 1186 return _M_pathname; 1187 #endif 1188 } 1189 #endif // _GLIBCXX_USE_CHAR8_T 1190 1191 inline std::u16string 1192 path::u16string() const { return string<char16_t>(); } 1193 1194 inline std::u32string 1195 path::u32string() const { return string<char32_t>(); } 1196 1197 template<typename _CharT, typename _Traits, typename _Allocator> 1198 inline std::basic_string<_CharT, _Traits, _Allocator> 1199 path::generic_string(const _Allocator& __a) const 1200 { 1201 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1202 const value_type __slash = L'/'; 1203 #else 1204 const value_type __slash = '/'; 1205 #endif 1206 using _Alloc2 = typename allocator_traits<_Allocator>::template 1207 rebind_alloc<value_type>; 1208 basic_string<value_type, char_traits<value_type>, _Alloc2> __str(__a); 1209 1210 if (_M_type() == _Type::_Root_dir) 1211 __str.assign(1, __slash); 1212 else 1213 { 1214 __str.reserve(_M_pathname.size()); 1215 bool __add_slash = false; 1216 for (auto& __elem : *this) 1217 { 1218 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1219 if (__elem._M_type() == _Type::_Root_dir) 1220 { 1221 __str += __slash; 1222 continue; 1223 } 1224 #endif 1225 if (__add_slash) 1226 __str += __slash; 1227 __str += basic_string_view<value_type>(__elem._M_pathname); 1228 __add_slash = __elem._M_type() == _Type::_Filename; 1229 } 1230 } 1231 1232 if constexpr (is_same_v<_CharT, value_type>) 1233 return __str; 1234 else 1235 return _S_str_convert<_CharT, _Traits>(__str, __a); 1236 } 1237 1238 inline std::string 1239 path::generic_string() const 1240 { return generic_string<char>(); } 1241 1242 #if _GLIBCXX_USE_WCHAR_T 1243 inline std::wstring 1244 path::generic_wstring() const 1245 { return generic_string<wchar_t>(); } 1246 #endif 1247 1248 #ifdef _GLIBCXX_USE_CHAR8_T 1249 inline std::u8string 1250 path::generic_u8string() const 1251 { return generic_string<char8_t>(); } 1252 #else 1253 inline std::string 1254 path::generic_u8string() const 1255 { return generic_string(); } 1256 #endif 1257 1258 inline std::u16string 1259 path::generic_u16string() const 1260 { return generic_string<char16_t>(); } 1261 1262 inline std::u32string 1263 path::generic_u32string() const 1264 { return generic_string<char32_t>(); } 1265 1266 inline int 1267 path::compare(const string_type& __s) const noexcept 1268 { return compare(basic_string_view<value_type>(__s)); } 1269 1270 inline int 1271 path::compare(const value_type* __s) const noexcept 1272 { return compare(basic_string_view<value_type>(__s)); } 1273 1274 inline path 1275 path::filename() const 1276 { 1277 if (empty()) 1278 return {}; 1279 else if (_M_type() == _Type::_Filename) 1280 return *this; 1281 else if (_M_type() == _Type::_Multi) 1282 { 1283 if (_M_pathname.back() == preferred_separator) 1284 return {}; 1285 auto __last = --end(); 1286 if (__last->_M_type() == _Type::_Filename) 1287 return *__last; 1288 } 1289 return {}; 1290 } 1291 1292 inline path 1293 path::stem() const 1294 { 1295 auto ext = _M_find_extension(); 1296 if (ext.first && ext.second != 0) 1297 return path{ext.first->substr(0, ext.second)}; 1298 return {}; 1299 } 1300 1301 inline path 1302 path::extension() const 1303 { 1304 auto ext = _M_find_extension(); 1305 if (ext.first && ext.second != string_type::npos) 1306 return path{ext.first->substr(ext.second)}; 1307 return {}; 1308 } 1309 1310 inline bool 1311 path::has_stem() const noexcept 1312 { 1313 auto ext = _M_find_extension(); 1314 return ext.first && ext.second != 0; 1315 } 1316 1317 inline bool 1318 path::has_extension() const noexcept 1319 { 1320 auto ext = _M_find_extension(); 1321 return ext.first && ext.second != string_type::npos; 1322 } 1323 1324 inline bool 1325 path::is_absolute() const noexcept 1326 { 1327 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS 1328 return has_root_name() && has_root_directory(); 1329 #else 1330 return has_root_directory(); 1331 #endif 1332 } 1333 1334 inline path::iterator 1335 path::begin() const noexcept 1336 { 1337 if (_M_type() == _Type::_Multi) 1338 return iterator(this, _M_cmpts.begin()); 1339 return iterator(this, empty()); 1340 } 1341 1342 inline path::iterator 1343 path::end() const noexcept 1344 { 1345 if (_M_type() == _Type::_Multi) 1346 return iterator(this, _M_cmpts.end()); 1347 return iterator(this, true); 1348 } 1349 1350 inline path::iterator& 1351 path::iterator::operator++() noexcept 1352 { 1353 __glibcxx_assert(_M_path != nullptr); 1354 if (_M_is_multi()) 1355 { 1356 __glibcxx_assert(_M_cur != _M_path->_M_cmpts.end()); 1357 ++_M_cur; 1358 } 1359 else 1360 { 1361 __glibcxx_assert(!_M_at_end); 1362 _M_at_end = true; 1363 } 1364 return *this; 1365 } 1366 1367 inline path::iterator& 1368 path::iterator::operator--() noexcept 1369 { 1370 __glibcxx_assert(_M_path != nullptr); 1371 if (_M_is_multi()) 1372 { 1373 __glibcxx_assert(_M_cur != _M_path->_M_cmpts.begin()); 1374 --_M_cur; 1375 } 1376 else 1377 { 1378 __glibcxx_assert(_M_at_end); 1379 _M_at_end = false; 1380 } 1381 return *this; 1382 } 1383 1384 inline path::iterator::reference 1385 path::iterator::operator*() const noexcept 1386 { 1387 __glibcxx_assert(_M_path != nullptr); 1388 if (_M_is_multi()) 1389 { 1390 __glibcxx_assert(_M_cur != _M_path->_M_cmpts.end()); 1391 return *_M_cur; 1392 } 1393 return *_M_path; 1394 } 1395 1396 inline bool 1397 path::iterator::_M_equals(iterator __rhs) const noexcept 1398 { 1399 if (_M_path != __rhs._M_path) 1400 return false; 1401 if (_M_path == nullptr) 1402 return true; 1403 if (_M_is_multi()) 1404 return _M_cur == __rhs._M_cur; 1405 return _M_at_end == __rhs._M_at_end; 1406 } 1407 1408 // Define this now that path and path::iterator are complete. 1409 // It needs to consider the string_view(Range&&) constructor during 1410 // overload resolution, which depends on whether range<path> is satisfied, 1411 // which depends on whether path::iterator is complete. 1412 inline int 1413 path::_S_compare(const path& __lhs, const path& __rhs) noexcept 1414 { return __lhs.compare(__rhs); } 1415 1416 /// @} group filesystem 1417 _GLIBCXX_END_NAMESPACE_CXX11 1418 } // namespace filesystem 1419 1420 /// @cond undocumented 1421 1422 inline ptrdiff_t 1423 distance(filesystem::path::iterator __first, filesystem::path::iterator __last) 1424 noexcept 1425 { return __path_iter_distance(__first, __last); } 1426 1427 template<typename _Distance> 1428 inline void 1429 advance(filesystem::path::iterator& __i, _Distance __n) noexcept 1430 { __path_iter_advance(__i, static_cast<ptrdiff_t>(__n)); } 1431 1432 extern template class __shared_ptr<const filesystem::filesystem_error::_Impl>; 1433 1434 /// @endcond 1435 1436 // _GLIBCXX_RESOLVE_LIB_DEFECTS 1437 // 3657. std::hash<std::filesystem::path> is not enabled 1438 template<> 1439 struct hash<filesystem::path> 1440 { 1441 size_t 1442 operator()(const filesystem::path& __p) const noexcept 1443 { return filesystem::hash_value(__p); } 1444 }; 1445 1446 _GLIBCXX_END_NAMESPACE_VERSION 1447 } // namespace std 1448 1449 #endif // C++17 1450 1451 #endif // _GLIBCXX_FS_PATH_H 1452