Skip to content

Commit 4761e74

Browse files
authored
[libc++] Implement LWG3430 disallow implicit conversion of the source arguments to std::filesystem::path when constructing std::basic_*fstream (#85079)
Implement [LWG3430](https://wg21.link/LWG3430). --------- Signed-off-by: yronglin <[email protected]>
1 parent 233c030 commit 4761e74

File tree

7 files changed

+125
-15
lines changed

7 files changed

+125
-15
lines changed

libcxx/docs/ReleaseNotes/19.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ Deprecations and Removals
9292
libatomic is not available. If you are one such user, please reach out to the libc++ developers so we can collaborate
9393
on a path for supporting atomics properly on freestanding platforms.
9494

95+
- LWG3430 disallow implicit conversion of the source arguments to ``std::filesystem::path`` when
96+
constructing ``std::basic_*fstream``. This effectively removes the possibility to directly construct
97+
a ``std::basic_*fstream`` from a ``std::basic_string_view``, a input-iterator or a C-string, instead
98+
you can construct a temporary ``std::basic_string``. This change has been applied to C++17 and later.
99+
95100

96101
Upcoming Deprecations and Removals
97102
----------------------------------

libcxx/docs/Status/Cxx23Issues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
`2818 <https://wg21.link/LWG2818>`__,"``::std::`` everywhere rule needs tweaking","June 2021","|Nothing To Do|",""
6565
`2997 <https://wg21.link/LWG2997>`__,"LWG 491 and the specification of ``{forward_,}list::unique``","June 2021","",""
6666
`3410 <https://wg21.link/LWG3410>`__,"``lexicographical_compare_three_way`` is overspecified","June 2021","|Complete|","17.0","|spaceship|"
67-
`3430 <https://wg21.link/LWG3430>`__,"``std::fstream`` & co. should be constructible from string_view","June 2021","",""
67+
`3430 <https://wg21.link/LWG3430>`__,"``std::fstream`` & co. should be constructible from string_view","June 2021","|Complete|","19.0",""
6868
`3462 <https://wg21.link/LWG3462>`__,"§[formatter.requirements]: Formatter requirements forbid use of ``fc.arg()``","June 2021","|Nothing To Do|","","|format|"
6969
`3481 <https://wg21.link/LWG3481>`__,"``viewable_range`` mishandles lvalue move-only views","June 2021","Superseded by `P2415R2 <https://wg21.link/P2415R2>`__","","|ranges|"
7070
`3506 <https://wg21.link/LWG3506>`__,"Missing allocator-extended constructors for ``priority_queue``","June 2021","|Complete|","14.0"

libcxx/include/fstream

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ public:
7878
basic_ifstream();
7979
explicit basic_ifstream(const char* s, ios_base::openmode mode = ios_base::in);
8080
explicit basic_ifstream(const string& s, ios_base::openmode mode = ios_base::in);
81-
explicit basic_ifstream(const filesystem::path& p,
82-
ios_base::openmode mode = ios_base::in); // C++17
81+
template<class T>
82+
explicit basic_ifstream(const T& s, ios_base::openmode mode = ios_base::in); // Since C++17
8383
basic_ifstream(basic_ifstream&& rhs);
8484
8585
basic_ifstream& operator=(basic_ifstream&& rhs);
@@ -117,8 +117,8 @@ public:
117117
basic_ofstream();
118118
explicit basic_ofstream(const char* s, ios_base::openmode mode = ios_base::out);
119119
explicit basic_ofstream(const string& s, ios_base::openmode mode = ios_base::out);
120-
explicit basic_ofstream(const filesystem::path& p,
121-
ios_base::openmode mode = ios_base::out); // C++17
120+
template<class T>
121+
explicit basic_ofstream(const T& s, ios_base::openmode mode = ios_base::out); // Since C++17
122122
basic_ofstream(basic_ofstream&& rhs);
123123
124124
basic_ofstream& operator=(basic_ofstream&& rhs);
@@ -158,8 +158,8 @@ public:
158158
basic_fstream();
159159
explicit basic_fstream(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out);
160160
explicit basic_fstream(const string& s, ios_base::openmode mode = ios_base::in|ios_base::out);
161-
explicit basic_fstream(const filesystem::path& p,
162-
ios_base::openmode mode = ios_base::in|ios_base::out); C++17
161+
template<class T>
162+
explicit basic_fstream(const T& s, ios_base::openmode mode = ios_base::in | ios_base::out); // Since C++17
163163
basic_fstream(basic_fstream&& rhs);
164164
165165
basic_fstream& operator=(basic_fstream&& rhs);
@@ -192,6 +192,8 @@ typedef basic_fstream<wchar_t> wfstream;
192192
#include <__config>
193193
#include <__fwd/fstream.h>
194194
#include <__locale>
195+
#include <__type_traits/enable_if.h>
196+
#include <__type_traits/is_same.h>
195197
#include <__utility/move.h>
196198
#include <__utility/swap.h>
197199
#include <__utility/unreachable.h>
@@ -1101,8 +1103,9 @@ public:
11011103
# endif
11021104
_LIBCPP_HIDE_FROM_ABI explicit basic_ifstream(const string& __s, ios_base::openmode __mode = ios_base::in);
11031105
# if _LIBCPP_STD_VER >= 17
1106+
template <class _Tp, class = enable_if_t<is_same_v<_Tp, filesystem::path>>>
11041107
_LIBCPP_AVAILABILITY_FILESYSTEM_LIBRARY _LIBCPP_HIDE_FROM_ABI explicit basic_ifstream(
1105-
const filesystem::path& __p, ios_base::openmode __mode = ios_base::in)
1108+
const _Tp& __p, ios_base::openmode __mode = ios_base::in)
11061109
: basic_ifstream(__p.c_str(), __mode) {}
11071110
# endif // _LIBCPP_STD_VER >= 17
11081111
_LIBCPP_HIDE_FROM_ABI basic_ifstream(basic_ifstream&& __rhs);
@@ -1255,8 +1258,9 @@ public:
12551258
_LIBCPP_HIDE_FROM_ABI explicit basic_ofstream(const string& __s, ios_base::openmode __mode = ios_base::out);
12561259

12571260
# if _LIBCPP_STD_VER >= 17
1261+
template <class _Tp, class = enable_if_t<is_same_v<_Tp, filesystem::path>>>
12581262
_LIBCPP_AVAILABILITY_FILESYSTEM_LIBRARY _LIBCPP_HIDE_FROM_ABI explicit basic_ofstream(
1259-
const filesystem::path& __p, ios_base::openmode __mode = ios_base::out)
1263+
const _Tp& __p, ios_base::openmode __mode = ios_base::out)
12601264
: basic_ofstream(__p.c_str(), __mode) {}
12611265
# endif // _LIBCPP_STD_VER >= 17
12621266

@@ -1414,8 +1418,9 @@ public:
14141418
ios_base::openmode __mode = ios_base::in | ios_base::out);
14151419

14161420
# if _LIBCPP_STD_VER >= 17
1421+
template <class _Tp, class = enable_if_t<is_same_v<_Tp, filesystem::path>>>
14171422
_LIBCPP_AVAILABILITY_FILESYSTEM_LIBRARY _LIBCPP_HIDE_FROM_ABI explicit basic_fstream(
1418-
const filesystem::path& __p, ios_base::openmode __mode = ios_base::in | ios_base::out)
1423+
const _Tp& __p, ios_base::openmode __mode = ios_base::in | ios_base::out)
14191424
: basic_fstream(__p.c_str(), __mode) {}
14201425
# endif // _LIBCPP_STD_VER >= 17
14211426

libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/path.pass.cpp

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,50 @@
1515
// plate <class charT, class traits = char_traits<charT> >
1616
// class basic_fstream
1717

18-
// explicit basic_fstream(const filesystem::path& s,
19-
// ios_base::openmode mode = ios_base::in|ios_base::out);
18+
// template<class T>
19+
// explicit basic_fstream(const T& s, ios_base::openmode mode = ios_base::in); // Since C++17
20+
// Constraints: is_same_v<T, filesystem::path> is true
2021

2122
#include <fstream>
2223
#include <filesystem>
2324
#include <cassert>
25+
#include <type_traits>
26+
2427
#include "test_macros.h"
28+
#include "test_iterators.h"
2529
#include "platform_support.h"
2630

2731
namespace fs = std::filesystem;
2832

33+
template <class CharT>
34+
constexpr bool test_non_convert_to_path() {
35+
// String types
36+
static_assert(!std::is_constructible_v<std::fstream, std::basic_string_view<CharT>>);
37+
static_assert(!std::is_constructible_v<std::fstream, const std::basic_string_view<CharT>>);
38+
39+
// Char* pointers
40+
if constexpr (!std::is_same_v<CharT, char>)
41+
static_assert(!std::is_constructible_v<std::fstream, const CharT*>);
42+
43+
// Iterators
44+
static_assert(!std::is_convertible_v<std::fstream, cpp17_input_iterator<const CharT*>>);
45+
46+
return true;
47+
}
48+
49+
static_assert(test_non_convert_to_path<char>());
50+
51+
#if !defined(TEST_HAS_NO_WIDE_CHARACTERS) && !defined(TEST_HAS_OPEN_WITH_WCHAR)
52+
static_assert(test_non_convert_to_path<wchar_t>());
53+
#endif // !TEST_HAS_NO_WIDE_CHARACTERS && !TEST_HAS_OPEN_WITH_WCHAR
54+
55+
#ifndef TEST_HAS_NO_CHAR8_T
56+
static_assert(test_non_convert_to_path<char8_t>());
57+
#endif // TEST_HAS_NO_CHAR8_T
58+
59+
static_assert(test_non_convert_to_path<char16_t>());
60+
static_assert(test_non_convert_to_path<char32_t>());
61+
2962
int main(int, char**) {
3063
fs::path p = get_temp_file_name();
3164
{

libcxx/test/std/input.output/file.streams/fstreams/ifstream.cons/path.pass.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,49 @@
1717
// template <class charT, class traits = char_traits<charT> >
1818
// class basic_ifstream
1919

20-
// explicit basic_ifstream(const filesystem::path& s,
21-
// ios_base::openmode mode = ios_base::in);
20+
// template<class T>
21+
// explicit basic_ifstream(const T& s, ios_base::openmode mode = ios_base::in); // Since C++17
22+
// Constraints: is_same_v<T, filesystem::path> is true
2223

2324
#include <cassert>
2425
#include <filesystem>
2526
#include <fstream>
2627
#include <type_traits>
2728

2829
#include "test_macros.h"
30+
#include "test_iterators.h"
2931

3032
namespace fs = std::filesystem;
3133

34+
template <class CharT>
35+
constexpr bool test_non_convert_to_path() {
36+
// String types
37+
static_assert(!std::is_constructible_v<std::ifstream, std::basic_string_view<CharT>>);
38+
static_assert(!std::is_constructible_v<std::ifstream, const std::basic_string_view<CharT>>);
39+
40+
// Char* pointers
41+
if constexpr (!std::is_same_v<CharT, char>)
42+
static_assert(!std::is_constructible_v<std::ifstream, const CharT*>);
43+
44+
// Iterators
45+
static_assert(!std::is_convertible_v<std::ifstream, cpp17_input_iterator<const CharT*>>);
46+
47+
return true;
48+
}
49+
50+
static_assert(test_non_convert_to_path<char>());
51+
52+
#if !defined(TEST_HAS_NO_WIDE_CHARACTERS) && !defined(TEST_HAS_OPEN_WITH_WCHAR)
53+
static_assert(test_non_convert_to_path<wchar_t>());
54+
#endif // !TEST_HAS_NO_WIDE_CHARACTERS && !TEST_HAS_OPEN_WITH_WCHAR
55+
56+
#ifndef TEST_HAS_NO_CHAR8_T
57+
static_assert(test_non_convert_to_path<char8_t>());
58+
#endif // TEST_HAS_NO_CHAR8_T
59+
60+
static_assert(test_non_convert_to_path<char16_t>());
61+
static_assert(test_non_convert_to_path<char32_t>());
62+
3263
int main(int, char**) {
3364
{
3465
fs::path p;

libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/path.pass.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
// plate <class charT, class traits = char_traits<charT> >
1616
// class basic_ofstream
1717

18-
// explicit basic_ofstream(const filesystem::path& s, ios_base::openmode mode = ios_base::out);
18+
// template<class T>
19+
// explicit basic_ifstream(const T& s, ios_base::openmode mode = ios_base::in); // Since C++17
20+
// Constraints: is_same_v<T, filesystem::path> is true
1921

2022
#include <cassert>
2123
#include <filesystem>
@@ -24,9 +26,39 @@
2426

2527
#include "platform_support.h"
2628
#include "test_macros.h"
29+
#include "test_iterators.h"
2730

2831
namespace fs = std::filesystem;
2932

33+
template <class CharT>
34+
constexpr bool test_non_convert_to_path() {
35+
// String types
36+
static_assert(!std::is_constructible_v<std::ofstream, std::basic_string_view<CharT>>);
37+
static_assert(!std::is_constructible_v<std::ofstream, const std::basic_string_view<CharT>>);
38+
39+
// Char* pointers
40+
if constexpr (!std::is_same_v<CharT, char>)
41+
static_assert(!std::is_constructible_v<std::ofstream, const CharT*>);
42+
43+
// Iterators
44+
static_assert(!std::is_convertible_v<std::ofstream, cpp17_input_iterator<const CharT*>>);
45+
46+
return true;
47+
}
48+
49+
static_assert(test_non_convert_to_path<char>());
50+
51+
#if !defined(TEST_HAS_NO_WIDE_CHARACTERS) && !defined(TEST_HAS_OPEN_WITH_WCHAR)
52+
static_assert(test_non_convert_to_path<wchar_t>());
53+
#endif // !TEST_HAS_NO_WIDE_CHARACTERS && !TEST_HAS_OPEN_WITH_WCHAR
54+
55+
#ifndef TEST_HAS_NO_CHAR8_T
56+
static_assert(test_non_convert_to_path<char8_t>());
57+
#endif // TEST_HAS_NO_CHAR8_T
58+
59+
static_assert(test_non_convert_to_path<char16_t>());
60+
static_assert(test_non_convert_to_path<char32_t>());
61+
3062
int main(int, char**) {
3163
fs::path p = get_temp_file_name();
3264
{

libcxx/test/support/test_macros.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ inline Tp const& DoNotOptimize(Tp const& value) {
385385
# define TEST_HAS_NO_UNICODE
386386
#endif
387387

388+
#if defined(_LIBCPP_HAS_OPEN_WITH_WCHAR)
389+
# define TEST_HAS_OPEN_WITH_WCHAR
390+
#endif
391+
388392
#if defined(_LIBCPP_HAS_NO_INT128) || defined(_MSVC_STL_VERSION)
389393
# define TEST_HAS_NO_INT128
390394
#endif

0 commit comments

Comments
 (0)