public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [committed 0/3] libstdc++: Refactor filesystem::path string conversions
@ 2020-05-23  8:40 Jonathan Wakely
  2020-05-23  8:42 ` [committed 1/3] libstdc++: Simplify filesystem::path SFINAE constraints Jonathan Wakely
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Jonathan Wakely @ 2020-05-23  8:40 UTC (permalink / raw)
  To: libstdc++, gcc-patches

This is a series of three patches to simplify the logic used to
construct a std::filesystem::path from strings/ranges of arbitrary
characters, and fix one bug.

The simpler logic also avoids some unnecessary string constructions
when a string view could be used instead. Previously a string view was
only used for strings and pairs of pointers. Now contiguous iterators
will be detected (using the concept in C++20, or by handling the
__normal_iterator wrapper in C++17).

The bug fix should be backported, the rest would be safe but doesn't
need to be.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [committed 1/3] libstdc++: Simplify filesystem::path SFINAE constraints
  2020-05-23  8:40 [committed 0/3] libstdc++: Refactor filesystem::path string conversions Jonathan Wakely
@ 2020-05-23  8:42 ` Jonathan Wakely
  2020-05-23  8:43 ` [committed 2/3] libstdc++: Remove incorrect static specifiers Jonathan Wakely
  2020-05-23  8:44 ` [committed 3/3] libstdc++: Refactor filesystem::path string conversions Jonathan Wakely
  2 siblings, 0 replies; 5+ messages in thread
From: Jonathan Wakely @ 2020-05-23  8:42 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1809 bytes --]

This replaces the filesystem::__detail::_Path SFINAE helper with two
separate helpers, _Path and _Path2. This avoids having one helper which
tries to check two different sets of requirements.

The _Path helper now uses variable templates instead of a set of
overloaded functions to detect specializations of basic_string or
basic_string_view.

The __not_<is_void<remove_pointer_t<_Tp1>> check is not necessary in
C++20 because iterator_traits<void*> is now empty. For C++17 replace
that check with a __safe_iterator_traits helper with partial
specializations for void pointers.

Finally, the __is_encoded_char check no longer uses remove_const_t,
which means that iterators with a const value_type will no longer be
accepted as arguments for path creation. Such iterators resulted in
undefined behaviour anyway, so it's still conforming to reject them in
the constraint checks.

         * include/bits/fs_path.h (filesystem::__detail::__is_encoded_char):
         Replace alias template with variable template. Don't remove const.
         (filesystem::__detail::__is_path_src): Replace overloaded function
         template with variable template and specializations.
         (filesystem::__detail::__is_path_iter_src): Replace alias template
         with class template.
         (filesystem::__detail::_Path): Use __is_path_src. Remove support for
         iterator pairs.
         (filesystem::__detail::_Path2): New alias template for checking
         InputIterator requirements.
         (filesystem::__detail::__constructible_from): Remove.
         (filesystem::path): Replace _Path<Iter, Iter> with _Path2<Iter>.
         * testsuite/27_io/filesystem/path/construct/80762.cc: Check with two
         constructor arguments of void and void* types.

Tested powerpc64le-linux, committed to master.


[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 10405 bytes --]

commit 988b853f9c829742907ae22ac66de56facfc7bc5
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sat May 23 07:28:40 2020 +0100

    libstdc++: Simplify filesystem::path SFINAE constraints
    
    This replaces the filesystem::__detail::_Path SFINAE helper with two
    separate helpers, _Path and _Path2. This avoids having one helper which
    tries to check two different sets of requirements.
    
    The _Path helper now uses variable templates instead of a set of
    overloaded functions to detect specializations of basic_string or
    basic_string_view.
    
    The __not_<is_void<remove_pointer_t<_Tp1>> check is not necessary in
    C++20 because iterator_traits<void*> is now empty. For C++17 replace
    that check with a __safe_iterator_traits helper with partial
    specializations for void pointers.
    
    Finally, the __is_encoded_char check no longer uses remove_const_t,
    which means that iterators with a const value_type will no longer be
    accepted as arguments for path creation. Such iterators resulted in
    undefined behaviour anyway, so it's still conforming to reject them in
    the constraint checks.
    
            * include/bits/fs_path.h (filesystem::__detail::__is_encoded_char):
            Replace alias template with variable template. Don't remove const.
            (filesystem::__detail::__is_path_src): Replace overloaded function
            template with variable template and specializations.
            (filesystem::__detail::__is_path_iter_src): Replace alias template
            with class template.
            (filesystem::__detail::_Path): Use __is_path_src. Remove support for
            iterator pairs.
            (filesystem::__detail::_Path2): New alias template for checking
            InputIterator requirements.
            (filesystem::__detail::__constructible_from): Remove.
            (filesystem::path): Replace _Path<Iter, Iter> with _Path2<Iter>.
            * testsuite/27_io/filesystem/path/construct/80762.cc: Check with two
            constructor arguments of void and void* types.

diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h
index ee6ab15cc4c..5a998284a99 100644
--- a/libstdc++-v3/include/bits/fs_path.h
+++ b/libstdc++-v3/include/bits/fs_path.h
@@ -73,58 +73,87 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
 namespace __detail
 {
   template<typename _CharT>
-    using __is_encoded_char = __is_one_of<remove_const_t<_CharT>,
-	  char,
+    inline constexpr bool __is_encoded_char = false;
+  template<>
+    inline constexpr bool __is_encoded_char<char> = true;
 #ifdef _GLIBCXX_USE_CHAR8_T
-	  char8_t,
+  template<>
+    inline constexpr bool __is_encoded_char<char8_t> = true;
 #endif
 #if _GLIBCXX_USE_WCHAR_T
-	  wchar_t,
+  template<>
+    inline constexpr bool __is_encoded_char<wchar_t> = true;
 #endif
-	  char16_t, char32_t>;
-
-  template<typename _Iter,
-	   typename _Iter_traits = std::iterator_traits<_Iter>>
-    using __is_path_iter_src
-      = __and_<__is_encoded_char<typename _Iter_traits::value_type>,
-	       std::is_base_of<std::input_iterator_tag,
-			       typename _Iter_traits::iterator_category>>;
+  template<>
+    inline constexpr bool __is_encoded_char<char16_t> = true;
+  template<>
+    inline constexpr bool __is_encoded_char<char32_t> = true;
 
+#if __cpp_concepts >= 201907L
   template<typename _Iter>
-    static __is_path_iter_src<_Iter>
-    __is_path_src(_Iter, int);
-
-  template<typename _CharT, typename _Traits, typename _Alloc>
-    static __is_encoded_char<_CharT>
-    __is_path_src(const basic_string<_CharT, _Traits, _Alloc>&, int);
-
-  template<typename _CharT, typename _Traits>
-    static __is_encoded_char<_CharT>
-    __is_path_src(const basic_string_view<_CharT, _Traits>&, int);
-
-  template<typename _Unknown>
-    static std::false_type
-    __is_path_src(const _Unknown&, ...);
-
-  template<typename _Tp1, typename _Tp2>
-    struct __constructible_from;
-
+    using __safe_iterator_traits = std::iterator_traits<_Iter>;
+#else
   template<typename _Iter>
-    struct __constructible_from<_Iter, _Iter>
-    : __is_path_iter_src<_Iter>
+    struct __safe_iterator_traits : std::iterator_traits<_Iter>
+    { };
+
+  // Protect against ill-formed iterator_traits specializations in C++17
+  template<> struct __safe_iterator_traits<void*> { };
+  template<> struct __safe_iterator_traits<const void*> { };
+  template<> struct __safe_iterator_traits<volatile void*> { };
+  template<> struct __safe_iterator_traits<const volatile void*> { };
+#endif
+
+  template<typename _Iter_traits, typename = void>
+    struct __is_path_iter_src
+    : false_type
+    { };
+
+  template<typename _Iter_traits>
+    struct __is_path_iter_src<_Iter_traits,
+			      void_t<typename _Iter_traits::value_type>>
+    : bool_constant<__is_encoded_char<typename _Iter_traits::value_type>>
     { };
 
   template<typename _Source>
-    struct __constructible_from<_Source, void>
-    : decltype(__is_path_src(std::declval<_Source>(), 0))
-    { };
+    inline constexpr bool __is_path_src
+      = __is_path_iter_src<iterator_traits<decay_t<_Source>>>::value;
 
-  template<typename _Tp1, typename _Tp2 = void>
-    using _Path = typename
-      std::enable_if<__and_<__not_<is_same<remove_cv_t<_Tp1>, path>>,
-			    __not_<is_void<remove_pointer_t<_Tp1>>>,
-			    __constructible_from<_Tp1, _Tp2>>::value,
-		     path>::type;
+  template<>
+    inline constexpr bool __is_path_src<path> = false;
+
+  template<>
+    inline constexpr bool __is_path_src<volatile path> = false;
+
+  template<>
+    inline constexpr bool __is_path_src<void*> = false;
+
+  template<>
+    inline constexpr bool __is_path_src<const void*> = false;
+
+  template<>
+    inline constexpr bool __is_path_src<volatile void*> = false;
+
+  template<>
+    inline constexpr bool __is_path_src<const volatile void*> = false;
+
+  template<typename _CharT, typename _Traits, typename _Alloc>
+    inline constexpr bool
+      __is_path_src<basic_string<_CharT, _Traits, _Alloc>>
+	= __is_encoded_char<_CharT>;
+
+  template<typename _CharT, typename _Traits>
+    inline constexpr bool
+      __is_path_src<basic_string_view<_CharT, _Traits>>
+	= __is_encoded_char<_CharT>;
+
+  // SFINAE constraint for Source parameters as required by [fs.path.req].
+  template<typename _Tp>
+    using _Path = enable_if_t<__is_path_src<_Tp>, path>;
+
+  // SFINAE constraint for InputIterator parameters as required by [fs.req].
+  template<typename _Iter, typename _Tr = __safe_iterator_traits<_Iter>>
+    using _Path2 = enable_if_t<__is_path_iter_src<_Tr>::value, path>;
 
   template<typename _Source>
     static _Source
@@ -227,7 +256,7 @@ namespace __detail
       { _M_split_cmpts(); }
 
     template<typename _InputIterator,
-	     typename _Require = __detail::_Path<_InputIterator, _InputIterator>>
+	     typename _Require = __detail::_Path2<_InputIterator>>
       path(_InputIterator __first, _InputIterator __last, format = auto_format)
       : _M_pathname(_S_convert(__first, __last))
       { _M_split_cmpts(); }
@@ -241,8 +270,8 @@ namespace __detail
       { _M_split_cmpts(); }
 
     template<typename _InputIterator,
-	     typename _Require = __detail::_Path<_InputIterator, _InputIterator>,
-	     typename _Require2 = __detail::__value_type_is_char<_InputIterator>>
+	     typename _Require = __detail::_Path2<_InputIterator>,
+	     typename _Req2 = __detail::__value_type_is_char<_InputIterator>>
       path(_InputIterator __first, _InputIterator __last, const locale& __loc,
 	   format = auto_format)
       : _M_pathname(_S_convert_loc(__first, __last, __loc))
@@ -268,7 +297,7 @@ namespace __detail
       { return *this = path(__source); }
 
     template<typename _InputIterator>
-      __detail::_Path<_InputIterator, _InputIterator>&
+      __detail::_Path2<_InputIterator>&
       assign(_InputIterator __first, _InputIterator __last)
       { return *this = path(__first, __last); }
 
@@ -295,7 +324,7 @@ namespace __detail
       }
 
     template<typename _InputIterator>
-      __detail::_Path<_InputIterator, _InputIterator>&
+      __detail::_Path2<_InputIterator>&
       append(_InputIterator __first, _InputIterator __last)
       {
 	_M_append(_S_convert(__first, __last));
@@ -315,7 +344,7 @@ namespace __detail
       operator+=(_Source const& __x) { return concat(__x); }
 
     template<typename _CharT>
-      __detail::_Path<_CharT*, _CharT*>&
+      __detail::_Path2<_CharT*>&
       operator+=(_CharT __x);
 
     template<typename _Source>
@@ -328,7 +357,7 @@ namespace __detail
       }
 
     template<typename _InputIterator>
-      __detail::_Path<_InputIterator, _InputIterator>&
+      __detail::_Path2<_InputIterator>&
       concat(_InputIterator __first, _InputIterator __last)
       {
 	_M_concat(_S_convert(__first, __last));
@@ -695,7 +724,7 @@ namespace __detail
    * @relates std::filesystem::path
    */
   template<typename _InputIterator,
-	   typename _Require = __detail::_Path<_InputIterator, _InputIterator>,
+	   typename _Require = __detail::_Path2<_InputIterator>,
 	   typename _CharT
 	     = __detail::__value_type_is_char_or_char8_t<_InputIterator>>
     inline path
@@ -1000,7 +1029,7 @@ namespace __detail
   }
 
   template<typename _CharT>
-    inline __detail::_Path<_CharT*, _CharT*>&
+    inline __detail::_Path2<_CharT*>&
     path::operator+=(_CharT __x)
     {
       auto* __addr = std::__addressof(__x);
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/construct/80762.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/construct/80762.cc
index bfc1e125e0c..2f37b663f26 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/path/construct/80762.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/path/construct/80762.cc
@@ -37,3 +37,9 @@ static_assert( !std::is_constructible_v<path, const volatile void*> );
 static_assert( !std::is_constructible_v<path, void*&> );
 static_assert( !std::is_constructible_v<path, void* const&> );
 static_assert( !std::is_constructible_v<path, const void* const&> );
+
+static_assert( !std::is_constructible_v<path, void, void> );
+static_assert( !std::is_constructible_v<path, void*, void*> );
+static_assert( !std::is_constructible_v<path, const void*, void*> );
+static_assert( !std::is_constructible_v<path, volatile void*, void*> );
+static_assert( !std::is_constructible_v<path, const volatile void*, void*> );

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [committed 2/3] libstdc++: Remove incorrect static specifiers
  2020-05-23  8:40 [committed 0/3] libstdc++: Refactor filesystem::path string conversions Jonathan Wakely
  2020-05-23  8:42 ` [committed 1/3] libstdc++: Simplify filesystem::path SFINAE constraints Jonathan Wakely
@ 2020-05-23  8:43 ` Jonathan Wakely
  2020-05-23  8:44 ` [committed 3/3] libstdc++: Refactor filesystem::path string conversions Jonathan Wakely
  2 siblings, 0 replies; 5+ messages in thread
From: Jonathan Wakely @ 2020-05-23  8:43 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 639 bytes --]

These functions were originally static members of the path class, but
the 'static' specifiers were not removed when they were moved to
namespace scope. This causes ODR violations when the functions are
called from functions defined in the header, which is incompatible with
Nathan's modules branch.  Change them to 'inline' instead.

         * include/bits/fs_path.h (__detail::_S_range_begin)
         (__detail::_S_range_end): Remove unintentional static specifiers.
         * include/experimental/bits/fs_path.h (__detail::_S_range_begin)
         (__detail::_S_range_end): Likewise.

Tested powerpc64le-linux, committed to master.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 3810 bytes --]

commit 00c8f2a5e3a21d93a03182cacbae4badc02a37f1
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sat May 23 09:00:16 2020 +0100

    libstdc++: Remove incorrect static specifiers
    
    These functions were originally static members of the path class, but
    the 'static' specifiers were not removed when they were moved to
    namespace scope. This causes ODR violations when the functions are
    called from functions defined in the header, which is incompatible with
    Nathan's modules branch.  Change them to 'inline' instead.
    
            * include/bits/fs_path.h (__detail::_S_range_begin)
            (__detail::_S_range_end): Remove unintentional static specifiers.
            * include/experimental/bits/fs_path.h (__detail::_S_range_begin)
            (__detail::_S_range_end): Likewise.

diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h
index 5a998284a99..818b5918927 100644
--- a/libstdc++-v3/include/bits/fs_path.h
+++ b/libstdc++-v3/include/bits/fs_path.h
@@ -156,32 +156,32 @@ namespace __detail
     using _Path2 = enable_if_t<__is_path_iter_src<_Tr>::value, path>;
 
   template<typename _Source>
-    static _Source
+    _Source
     _S_range_begin(_Source __begin) { return __begin; }
 
   struct __null_terminated { };
 
   template<typename _Source>
-    static __null_terminated
+    __null_terminated
     _S_range_end(_Source) { return {}; }
 
   template<typename _CharT, typename _Traits, typename _Alloc>
-    static const _CharT*
+    inline const _CharT*
     _S_range_begin(const basic_string<_CharT, _Traits, _Alloc>& __str)
     { return __str.data(); }
 
   template<typename _CharT, typename _Traits, typename _Alloc>
-    static const _CharT*
+    inline const _CharT*
     _S_range_end(const basic_string<_CharT, _Traits, _Alloc>& __str)
     { return __str.data() + __str.size(); }
 
   template<typename _CharT, typename _Traits>
-    static const _CharT*
+    inline const _CharT*
     _S_range_begin(const basic_string_view<_CharT, _Traits>& __str)
     { return __str.data(); }
 
   template<typename _CharT, typename _Traits>
-    static const _CharT*
+    inline const _CharT*
     _S_range_end(const basic_string_view<_CharT, _Traits>& __str)
     { return __str.data() + __str.size(); }
 
diff --git a/libstdc++-v3/include/experimental/bits/fs_path.h b/libstdc++-v3/include/experimental/bits/fs_path.h
index d7234c08a00..69b823a3466 100644
--- a/libstdc++-v3/include/experimental/bits/fs_path.h
+++ b/libstdc++-v3/include/experimental/bits/fs_path.h
@@ -137,33 +137,33 @@ namespace __detail
 		     path>::type;
 
   template<typename _Source>
-    static _Source
+    inline _Source
     _S_range_begin(_Source __begin) { return __begin; }
 
   struct __null_terminated { };
 
   template<typename _Source>
-    static __null_terminated
+    inline __null_terminated
     _S_range_end(_Source) { return {}; }
 
   template<typename _CharT, typename _Traits, typename _Alloc>
-    static const _CharT*
+    inline const _CharT*
     _S_range_begin(const basic_string<_CharT, _Traits, _Alloc>& __str)
     { return __str.data(); }
 
   template<typename _CharT, typename _Traits, typename _Alloc>
-    static const _CharT*
+    inline const _CharT*
     _S_range_end(const basic_string<_CharT, _Traits, _Alloc>& __str)
     { return __str.data() + __str.size(); }
 
 #if __cplusplus >= 201402L
   template<typename _CharT, typename _Traits>
-    static const _CharT*
+    inline const _CharT*
     _S_range_begin(const basic_string_view<_CharT, _Traits>& __str)
     { return __str.data(); }
 
   template<typename _CharT, typename _Traits>
-    static const _CharT*
+    inline const _CharT*
     _S_range_end(const basic_string_view<_CharT, _Traits>& __str)
     { return __str.data() + __str.size(); }
 #endif

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [committed 3/3] libstdc++: Refactor filesystem::path string conversions
  2020-05-23  8:40 [committed 0/3] libstdc++: Refactor filesystem::path string conversions Jonathan Wakely
  2020-05-23  8:42 ` [committed 1/3] libstdc++: Simplify filesystem::path SFINAE constraints Jonathan Wakely
  2020-05-23  8:43 ` [committed 2/3] libstdc++: Remove incorrect static specifiers Jonathan Wakely
@ 2020-05-23  8:44 ` Jonathan Wakely
  2020-06-01 23:14   ` Jonathan Wakely
  2 siblings, 1 reply; 5+ messages in thread
From: Jonathan Wakely @ 2020-05-23  8:44 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2197 bytes --]

This simplifies the logic of converting Source arguments and pairs of
InputIterator arguments into the native string format. For any input
that is a contiguous range of path::value_type (or char8_t for POSIX)
a string view can be created and the conversion can be done directly,
with no intermediate allocation. Previously some cases created a
basic_string unnecessarily, for example construction from a pair of
path::string_type::iterators, or a pair of non-const value_type*
pointers.

         * include/bits/fs_path.h (__detail::_S_range_begin)
         (__detail::_S_range_end, path::_S_string_from_iter): Replace with
         overloaded function template __detail::__effective_range.
         (__detail::__effective_range): New overloaded function template to
         create a basic_string or basic_string_view for an effective range.
         (__detail::__value_type_is_char): Use __detail::__effective_range.
         Do not use remove_const on value type.
         (__detail::__value_type_is_char_or_char8_t): Likewise.
         (path::path(const Source&, format))
         (path::path(const Source&, const locale&))
         (path::operator/=(const Source&), path::append(const Source&))
         (path::concat(const Source&)): Use __detail::__effective_range.
         (path::_S_to_string(InputIterator, InputIterator)): New function
         template to create a string view if possible, or string otherwise.
         (path::_S_convert): Add overloads that convert a string returned
         by __detail::__effective_range. Use if-constexpr to inline conversion
         logic from all overloads of _Cvt::_S_convert.
         (path::_S_convert_loc): Add overload that converts a string. Use
         _S_to_string to avoid allocation when possible.
         (path::_Cvt): Remove.
         (path::operator+=(CharT)): Remove indirection through path::concat.
         * include/experimental/bits/fs_path.h (path::_S_convert_loc): Add
         overload for non-const pointers, to avoid constructing a std::string.
         * src/c++17/fs_path.cc (path::_S_convert_loc): Replace conditional
         compilation with call to _S_convert.


Tested powerpc64le-linux, committed to master.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 20476 bytes --]

commit 584d52b088f9fcf78704b504c3f1f07e17c1cded
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sat May 23 09:00:32 2020 +0100

    libstdc++: Refactor filesystem::path string conversions
    
    This simplifies the logic of converting Source arguments and pairs of
    InputIterator arguments into the native string format. For any input
    that is a contiguous range of path::value_type (or char8_t for POSIX)
    a string view can be created and the conversion can be done directly,
    with no intermediate allocation. Previously some cases created a
    basic_string unnecessarily, for example construction from a pair of
    path::string_type::iterators, or a pair of non-const value_type*
    pointers.
    
            * include/bits/fs_path.h (__detail::_S_range_begin)
            (__detail::_S_range_end, path::_S_string_from_iter): Replace with
            overloaded function template __detail::__effective_range.
            (__detail::__effective_range): New overloaded function template to
            create a basic_string or basic_string_view for an effective range.
            (__detail::__value_type_is_char): Use __detail::__effective_range.
            Do not use remove_const on value type.
            (__detail::__value_type_is_char_or_char8_t): Likewise.
            (path::path(const Source&, format))
            (path::path(const Source&, const locale&))
            (path::operator/=(const Source&), path::append(const Source&))
            (path::concat(const Source&)): Use __detail::__effective_range.
            (path::_S_to_string(InputIterator, InputIterator)): New function
            template to create a string view if possible, or string otherwise.
            (path::_S_convert): Add overloads that convert a string returned
            by __detail::__effective_range. Use if-constexpr to inline conversion
            logic from all overloads of _Cvt::_S_convert.
            (path::_S_convert_loc): Add overload that converts a string. Use
            _S_to_string to avoid allocation when possible.
            (path::_Cvt): Remove.
            (path::operator+=(CharT)): Remove indirection through path::concat.
            * include/experimental/bits/fs_path.h (path::_S_convert_loc): Add
            overload for non-const pointers, to avoid constructing a std::string.
            * src/c++17/fs_path.cc (path::_S_convert_loc): Replace conditional
            compilation with call to _S_convert.

diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h
index 818b5918927..2d2766ec62e 100644
--- a/libstdc++-v3/include/bits/fs_path.h
+++ b/libstdc++-v3/include/bits/fs_path.h
@@ -155,56 +155,61 @@ namespace __detail
   template<typename _Iter, typename _Tr = __safe_iterator_traits<_Iter>>
     using _Path2 = enable_if_t<__is_path_iter_src<_Tr>::value, path>;
 
-  template<typename _Source>
-    _Source
-    _S_range_begin(_Source __begin) { return __begin; }
-
-  struct __null_terminated { };
-
-  template<typename _Source>
-    __null_terminated
-    _S_range_end(_Source) { return {}; }
+  // The __effective_range overloads convert a Source parameter into
+  // either a basic_string_view or basic_string containing the
+  // effective range of the Source, as defined in [fs.path.req].
 
   template<typename _CharT, typename _Traits, typename _Alloc>
-    inline const _CharT*
-    _S_range_begin(const basic_string<_CharT, _Traits, _Alloc>& __str)
-    { return __str.data(); }
-
-  template<typename _CharT, typename _Traits, typename _Alloc>
-    inline const _CharT*
-    _S_range_end(const basic_string<_CharT, _Traits, _Alloc>& __str)
-    { return __str.data() + __str.size(); }
+    inline basic_string_view<_CharT, _Traits>
+    __effective_range(const basic_string<_CharT, _Traits, _Alloc>& __source)
+    { return __source; }
 
   template<typename _CharT, typename _Traits>
-    inline const _CharT*
-    _S_range_begin(const basic_string_view<_CharT, _Traits>& __str)
-    { return __str.data(); }
+    inline const basic_string_view<_CharT, _Traits>&
+    __effective_range(const basic_string_view<_CharT, _Traits>& __source)
+    { return __source; }
 
-  template<typename _CharT, typename _Traits>
-    inline const _CharT*
-    _S_range_end(const basic_string_view<_CharT, _Traits>& __str)
-    { return __str.data() + __str.size(); }
+  template<typename _Source>
+    inline auto
+    __effective_range(const _Source& __source)
+    {
+      if constexpr (is_pointer_v<decay_t<_Source>>)
+	return basic_string_view{&*__source};
+      else
+	{
+	  // _Source is an input iterator that iterates over an NTCTS.
+	  // Create a basic_string by reading until the null character.
+	  using value_type
+	    = typename iterator_traits<_Source>::value_type;
+	  basic_string<value_type> __str;
+	  _Source __it = __source;
+	  for (value_type __ch = *__it; __ch != value_type(); __ch = *++__it)
+	    __str.push_back(__ch);
+	  return __str;
+	}
+    }
 
-  template<typename _Tp,
-	   typename _Iter = decltype(_S_range_begin(std::declval<_Tp>())),
-	   typename _Val = typename std::iterator_traits<_Iter>::value_type,
-	   typename _UnqualVal = std::remove_const_t<_Val>>
+  // The value type of a Source parameter's effective range.
+  template<typename _Tp>
+    using __value_t = typename remove_reference_t<
+      decltype(__detail::__effective_range(std::declval<_Tp>()))>::value_type;
+
+  // SFINAE helper to check that an effective range has value_type char,
+  // as required by path constructors taking a std::locale parameter.
+  // The type _Tp must have already been checked by _Path<Tp> or _Path2<_Tp>.
+  template<typename _Tp, typename _Val = __value_t<_Tp>>
     using __value_type_is_char
-      = std::enable_if_t<std::is_same_v<_UnqualVal, char>,
-			 _UnqualVal>;
+      = std::enable_if_t<std::is_same_v<_Val, char>, _Val>;
 
-  template<typename _Tp,
-	   typename _Iter = decltype(_S_range_begin(std::declval<_Tp>())),
-	   typename _Val = typename std::iterator_traits<_Iter>::value_type,
-	   typename _UnqualVal = std::remove_const_t<_Val>>
+  // As above, but also allows char8_t, as required by u8path
+  // C++20 [depr.fs.path.factory]
+  template<typename _Tp, typename _Val = __value_t<_Tp>>
     using __value_type_is_char_or_char8_t
-      = std::enable_if_t<__or_v<
-			   std::is_same<_UnqualVal, char>
+      = std::enable_if_t<std::is_same_v<_Val, char>
 #ifdef _GLIBCXX_USE_CHAR8_T
-			   , std::is_same<_UnqualVal, char8_t>
+			 || std::is_same_v<_Val, char8_t>
 #endif
-			   >,
-			 _UnqualVal>;
+			 , _Val>;
 
 } // namespace __detail
   /// @endcond
@@ -251,8 +256,7 @@ namespace __detail
     template<typename _Source,
 	     typename _Require = __detail::_Path<_Source>>
       path(_Source const& __source, format = auto_format)
-      : _M_pathname(_S_convert(__detail::_S_range_begin(__source),
-			       __detail::_S_range_end(__source)))
+      : _M_pathname(_S_convert(__detail::__effective_range(__source)))
       { _M_split_cmpts(); }
 
     template<typename _InputIterator,
@@ -264,9 +268,8 @@ namespace __detail
     template<typename _Source,
 	     typename _Require = __detail::_Path<_Source>,
 	     typename _Require2 = __detail::__value_type_is_char<_Source>>
-      path(_Source const& __source, const locale& __loc, format = auto_format)
-      : _M_pathname(_S_convert_loc(__detail::_S_range_begin(__source),
-				   __detail::_S_range_end(__source), __loc))
+      path(_Source const& __src, const locale& __loc, format = auto_format)
+      : _M_pathname(_S_convert_loc(__detail::__effective_range(__src), __loc))
       { _M_split_cmpts(); }
 
     template<typename _InputIterator,
@@ -309,8 +312,7 @@ namespace __detail
       __detail::_Path<_Source>&
       operator/=(_Source const& __source)
       {
-	_M_append(_S_convert(__detail::_S_range_begin(__source),
-			     __detail::_S_range_end(__source)));
+	_M_append(_S_convert(__detail::__effective_range(__source)));
 	return *this;
       }
 
@@ -318,8 +320,7 @@ namespace __detail
       __detail::_Path<_Source>&
       append(_Source const& __source)
       {
-	_M_append(_S_convert(__detail::_S_range_begin(__source),
-			     __detail::_S_range_end(__source)));
+	_M_append(_S_convert(__detail::__effective_range(__source)));
 	return *this;
       }
 
@@ -351,8 +352,7 @@ namespace __detail
       __detail::_Path<_Source>&
       concat(_Source const& __x)
       {
-	_M_concat(_S_convert(__detail::_S_range_begin(__x),
-			     __detail::_S_range_end(__x)));
+	_M_concat(_S_convert(__detail::__effective_range(__x)));
 	return *this;
       }
 
@@ -523,22 +523,6 @@ namespace __detail
       return __result;
     }
 
-    /// @cond undocumented
-    // Create a basic_string by reading until a null character.
-    template<typename _InputIterator,
-	     typename _Traits = std::iterator_traits<_InputIterator>,
-	     typename _CharT
-	       = typename std::remove_cv_t<typename _Traits::value_type>>
-      static std::basic_string<_CharT>
-      _S_string_from_iter(_InputIterator __source)
-      {
-	std::basic_string<_CharT> __str;
-	for (_CharT __ch = *__source; __ch != _CharT(); __ch = *++__source)
-	  __str.push_back(__ch);
-	return __str;
-      }
-    /// @endcond
-
   private:
     enum class _Type : unsigned char {
       _Multi = 0, _Root_name, _Root_dir, _Filename
@@ -558,43 +542,67 @@ namespace __detail
 
     pair<const string_type*, size_t> _M_find_extension() const noexcept;
 
-    template<typename _CharT>
-      struct _Cvt;
+    // Create a string or string view from an iterator range.
+    template<typename _InputIterator>
+      static auto
+      _S_to_string(_InputIterator __first, _InputIterator __last)
+      {
+	using _EcharT
+	  = typename std::iterator_traits<_InputIterator>::value_type;
+	static_assert(__detail::__is_encoded_char<_EcharT>);
 
-    static basic_string_view<value_type>
-    _S_convert(value_type* __src, __detail::__null_terminated)
-    { return __src; }
+#if __cpp_lib_concepts
+	constexpr bool __contiguous = std::contiguous_iterator<_InputIterator>;
+#else
+	constexpr bool __contiguous
+	  = is_pointer_v<decltype(std::__niter_base(__first))>;
+#endif
+	if constexpr (__contiguous)
+	  {
+	    // For contiguous iterators we can just return a string view.
+	    const auto* __f = std::__to_address(std::__niter_base(__first));
+	    const auto* __l = std::__to_address(std::__niter_base(__last));
+	    return basic_string_view<_EcharT>(__f, __l - __f);
+	  }
+	else
+	  // Conversion requires contiguous characters, so create a string.
+	  return basic_string<_EcharT>(__first, __last);
+      }
 
-    static basic_string_view<value_type>
-    _S_convert(const value_type* __src, __detail::__null_terminated)
-    { return __src; }
+    // path::_S_convert creates a basic_string<value_type> or
+    // basic_string_view<value_type> from a range (either the effective
+    // range of a Source parameter, or a pair of InputIterator parameters),
+    // performing the conversions required by [fs.path.type.cvt].
+    // If the value_type of the range value type is path::value_type,
+    // no encoding conversion is performed. If the range is contiguous
+    // a string_view
 
-    static basic_string_view<value_type>
-    _S_convert(value_type* __first, value_type* __last)
-    { return {__first, __last - __first}; }
+    static string_type
+    _S_convert(string_type __str)
+    { return __str; }
 
-    static basic_string_view<value_type>
-    _S_convert(const value_type* __first, const value_type* __last)
-    { return {__first, __last - __first}; }
+    template<typename _Tp>
+      static auto
+      _S_convert(const _Tp& __str)
+      {
+	if constexpr (is_same_v<_Tp, string_type>)
+	  return __str;
+	else if constexpr (is_same_v<_Tp, basic_string_view<value_type>>)
+	  return __str;
+	else if constexpr (is_same_v<typename _Tp::value_type, value_type>)
+	  return basic_string_view<value_type>(__str.data(), __str.size());
+	else
+	  return _S_convert(__str.data(), __str.data() + __str.size());
+      }
+
+    template<typename _EcharT>
+      static auto
+      _S_convert(const _EcharT* __first, const _EcharT* __last);
 
     template<typename _Iter>
-      static string_type
+      static auto
       _S_convert(_Iter __first, _Iter __last)
-      {
-	using __value_type = typename std::iterator_traits<_Iter>::value_type;
-	return _Cvt<typename remove_cv<__value_type>::type>::
-	  _S_convert(__first, __last);
-      }
-
-    template<typename _InputIterator>
-      static string_type
-      _S_convert(_InputIterator __src, __detail::__null_terminated)
-      {
-	// Read from iterator into basic_string until a null value is seen:
-	auto __s = _S_string_from_iter(__src);
-	// Convert (if needed) from iterator's value type to path::value_type:
-	return string_type(_S_convert(__s.data(), __s.data() + __s.size()));
-      }
+      { return _S_convert(_S_to_string(__first, __last)); }
 
     static string_type
     _S_convert_loc(const char* __first, const char* __last,
@@ -604,16 +612,14 @@ namespace __detail
       static string_type
       _S_convert_loc(_Iter __first, _Iter __last, const std::locale& __loc)
       {
-	const std::string __str(__first, __last);
-	return _S_convert_loc(__str.data(), __str.data()+__str.size(), __loc);
+	const auto __s = _S_to_string(__first, __last);
+	return _S_convert_loc(__s.data(), __s.data() + __s.size(), __loc);
       }
 
-    template<typename _InputIterator>
+    template<typename _Tp>
       static string_type
-      _S_convert_loc(_InputIterator __src, __detail::__null_terminated,
-		     const std::locale& __loc)
+      _S_convert_loc(const _Tp& __s, const std::locale& __loc)
       {
-	const std::string __s = _S_string_from_iter(__src);
 	return _S_convert_loc(__s.data(), __s.data() + __s.size(), __loc);
       }
 
@@ -803,100 +809,66 @@ namespace __detail
     size_t _M_pos;
   };
 
-  // specialize _Cvt for degenerate 'noconv' case
-  template<>
-    struct path::_Cvt<path::value_type>
+  template<typename _EcharT>
+    auto
+    path::_S_convert(const _EcharT* __f, const _EcharT* __l)
     {
-      template<typename _Iter>
-	static string_type
-	_S_convert(_Iter __first, _Iter __last)
-	{ return string_type{__first, __last}; }
-    };
+      static_assert(__detail::__is_encoded_char<_EcharT>);
 
-#if !defined _GLIBCXX_FILESYSTEM_IS_WINDOWS  && defined _GLIBCXX_USE_CHAR8_T
-  // For POSIX converting from char8_t to char is also 'noconv'
-  template<>
-    struct path::_Cvt<char8_t>
-    {
-      template<typename _Iter>
-	static string_type
-	_S_convert(_Iter __first, _Iter __last)
-	{ return string_type(__first, __last); }
-    };
+      if constexpr (is_same_v<_EcharT, value_type>)
+	return basic_string_view<value_type>(__f, __l - __f);
+#if !defined _GLIBCXX_FILESYSTEM_IS_WINDOWS && defined _GLIBCXX_USE_CHAR8_T
+      else if constexpr (is_same_v<_EcharT, char8_t>)
+	// For POSIX converting from char8_t to char is also 'noconv'
+	return string_view(reinterpret_cast<const char*>(__f), __l - __f);
 #endif
-
-  template<typename _CharT>
-    struct path::_Cvt
-    {
-      static string_type
-      _S_convert(const _CharT* __f, const _CharT* __l)
-      {
-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
-	std::wstring __wstr;
-	if constexpr (is_same_v<_CharT, char>)
-	  {
-	    struct _UCvt : std::codecvt<wchar_t, char, std::mbstate_t>
-	    { } __cvt;
-	    if (__str_codecvt_in_all(__f, __l, __wstr, __cvt))
-	      return __wstr;
-	  }
-#ifdef _GLIBCXX_USE_CHAR8_T
-	else if constexpr (is_same_v<_CharT, char8_t>)
-	  {
-	    const char* __f2 = (const char*)__f;
-	    const char* __l2 = (const char*)__l;
-	    std::codecvt_utf8_utf16<wchar_t> __wcvt;
-	    if (__str_codecvt_in_all(__f2, __l2, __wstr, __wcvt))
-	      return __wstr;
-	  }
-#endif
-	else // char16_t or char32_t
-	  {
-	    struct _UCvt : std::codecvt<_CharT, char, std::mbstate_t>
-	    { } __cvt;
-	    std::string __str;
-	    if (__str_codecvt_out_all(__f, __l, __str, __cvt))
-	      {
-		const char* __f2 = __str.data();
-		const char* __l2 = __f2 + __str.size();
-		std::codecvt_utf8_utf16<wchar_t> __wcvt;
-		if (__str_codecvt_in_all(__f2, __l2, __wstr, __wcvt))
-		  return __wstr;
-	      }
-	  }
-#else // ! windows
-	struct _UCvt : std::codecvt<_CharT, char, std::mbstate_t>
-	{ } __cvt;
-	std::string __str;
-	if (__str_codecvt_out_all(__f, __l, __str, __cvt))
-	  return __str;
-#endif
-	_GLIBCXX_THROW_OR_ABORT(filesystem_error(
-	      "Cannot convert character sequence",
-	      std::make_error_code(errc::illegal_byte_sequence)));
-      }
-
-      static string_type
-      _S_convert(_CharT* __f, _CharT* __l)
-      {
-	return _S_convert(const_cast<const _CharT*>(__f),
-			  const_cast<const _CharT*>(__l));
-      }
-
-      template<typename _Iter>
-	static string_type
-	_S_convert(_Iter __first, _Iter __last)
+      else
 	{
-	  const std::basic_string<_CharT> __str(__first, __last);
-	  return _S_convert(__str.data(), __str.data() + __str.size());
+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+	  std::wstring __wstr;
+	  if constexpr (is_same_v<_EcharT, char>)
+	    {
+	      struct _UCvt : std::codecvt<wchar_t, char, std::mbstate_t>
+	      { } __cvt;
+	      if (__str_codecvt_in_all(__f, __l, __wstr, __cvt))
+		return __wstr;
+	    }
+#ifdef _GLIBCXX_USE_CHAR8_T
+	  else if constexpr (is_same_v<_EcharT, char8_t>)
+	    {
+	      const char* __f2 = (const char*)__f;
+	      const char* __l2 = (const char*)__l;
+	      std::codecvt_utf8_utf16<wchar_t> __wcvt;
+	      if (__str_codecvt_in_all(__f2, __l2, __wstr, __wcvt))
+		return __wstr;
+	    }
+#endif
+	  else // char16_t or char32_t
+	    {
+	      struct _UCvt : std::codecvt<_EcharT, char, std::mbstate_t>
+	      { } __cvt;
+	      std::string __str;
+	      if (__str_codecvt_out_all(__f, __l, __str, __cvt))
+		{
+		  const char* __f2 = __str.data();
+		  const char* __l2 = __f2 + __str.size();
+		  std::codecvt_utf8_utf16<wchar_t> __wcvt;
+		  if (__str_codecvt_in_all(__f2, __l2, __wstr, __wcvt))
+		    return __wstr;
+		}
+	    }
+#else // ! windows
+	  struct _UCvt : std::codecvt<_EcharT, char, std::mbstate_t>
+	  { } __cvt;
+	  std::string __str;
+	  if (__str_codecvt_out_all(__f, __l, __str, __cvt))
+	    return __str;
+#endif
+	  _GLIBCXX_THROW_OR_ABORT(filesystem_error(
+		"Cannot convert character sequence",
+		std::make_error_code(errc::illegal_byte_sequence)));
 	}
-
-      template<typename _Iter, typename _Cont>
-	static string_type
-	_S_convert(__gnu_cxx::__normal_iterator<_Iter, _Cont> __first,
-		  __gnu_cxx::__normal_iterator<_Iter, _Cont> __last)
-	{ return _S_convert(__first.base(), __last.base()); }
-    };
+    }
 
   /// @endcond
 
@@ -1030,10 +1002,10 @@ namespace __detail
 
   template<typename _CharT>
     inline __detail::_Path2<_CharT*>&
-    path::operator+=(_CharT __x)
+    path::operator+=(const _CharT __x)
     {
-      auto* __addr = std::__addressof(__x);
-      return concat(__addr, __addr + 1);
+      _M_concat(_S_convert(&__x, &__x + 1));
+      return *this;
     }
 
   inline path&
diff --git a/libstdc++-v3/include/experimental/bits/fs_path.h b/libstdc++-v3/include/experimental/bits/fs_path.h
index 69b823a3466..c91937c66d8 100644
--- a/libstdc++-v3/include/experimental/bits/fs_path.h
+++ b/libstdc++-v3/include/experimental/bits/fs_path.h
@@ -495,6 +495,13 @@ namespace __detail
     _S_convert_loc(const char* __first, const char* __last,
 		   const std::locale& __loc);
 
+    static string_type
+    _S_convert_loc(char* __first, char* __last, const std::locale& __loc)
+    {
+      return _S_convert_loc(const_cast<const char*>(__first),
+			    const_cast<const char*>(__last), __loc);
+    }
+
     template<typename _Iter>
       static string_type
       _S_convert_loc(_Iter __first, _Iter __last, const std::locale& __loc)
diff --git a/libstdc++-v3/src/c++17/fs_path.cc b/libstdc++-v3/src/c++17/fs_path.cc
index 5ff17741f81..cea7aa08601 100644
--- a/libstdc++-v3/src/c++17/fs_path.cc
+++ b/libstdc++-v3/src/c++17/fs_path.cc
@@ -1949,11 +1949,7 @@ path::_S_convert_loc(const char* __first, const char* __last,
     _GLIBCXX_THROW_OR_ABORT(filesystem_error(
 	  "Cannot convert character sequence",
 	  std::make_error_code(errc::illegal_byte_sequence)));
-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
-  return __ws;
-#else
-  return _Cvt<wchar_t>::_S_convert(__ws.data(), __ws.data() + __ws.size());
-#endif
+  return _S_convert(std::move(__ws));
 #else
   return {__first, __last};
 #endif

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [committed 3/3] libstdc++: Refactor filesystem::path string conversions
  2020-05-23  8:44 ` [committed 3/3] libstdc++: Refactor filesystem::path string conversions Jonathan Wakely
@ 2020-06-01 23:14   ` Jonathan Wakely
  0 siblings, 0 replies; 5+ messages in thread
From: Jonathan Wakely @ 2020-06-01 23:14 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 2347 bytes --]

On 23/05/20 09:44 +0100, Jonathan Wakely wrote:
>This simplifies the logic of converting Source arguments and pairs of
>InputIterator arguments into the native string format. For any input
>that is a contiguous range of path::value_type (or char8_t for POSIX)
>a string view can be created and the conversion can be done directly,
>with no intermediate allocation. Previously some cases created a
>basic_string unnecessarily, for example construction from a pair of
>path::string_type::iterators, or a pair of non-const value_type*
>pointers.
>
>        * include/bits/fs_path.h (__detail::_S_range_begin)
>        (__detail::_S_range_end, path::_S_string_from_iter): Replace with
>        overloaded function template __detail::__effective_range.
>        (__detail::__effective_range): New overloaded function template to
>        create a basic_string or basic_string_view for an effective range.
>        (__detail::__value_type_is_char): Use __detail::__effective_range.
>        Do not use remove_const on value type.
>        (__detail::__value_type_is_char_or_char8_t): Likewise.
>        (path::path(const Source&, format))
>        (path::path(const Source&, const locale&))
>        (path::operator/=(const Source&), path::append(const Source&))
>        (path::concat(const Source&)): Use __detail::__effective_range.
>        (path::_S_to_string(InputIterator, InputIterator)): New function
>        template to create a string view if possible, or string otherwise.
>        (path::_S_convert): Add overloads that convert a string returned
>        by __detail::__effective_range. Use if-constexpr to inline conversion
>        logic from all overloads of _Cvt::_S_convert.
>        (path::_S_convert_loc): Add overload that converts a string. Use
>        _S_to_string to avoid allocation when possible.
>        (path::_Cvt): Remove.
>        (path::operator+=(CharT)): Remove indirection through path::concat.
>        * include/experimental/bits/fs_path.h (path::_S_convert_loc): Add
>        overload for non-const pointers, to avoid constructing a std::string.
>        * src/c++17/fs_path.cc (path::_S_convert_loc): Replace conditional
>        compilation with call to _S_convert.

This commit broke *-*-mingw* bootstrap. Fixed with the attached patch.

Tested powerpc64le-linux and x86_64-w64-mingw32, committed to master.


[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 8517 bytes --]

commit cd3f067b82a1331f5fb695879ba5c3d9bb2cca3a
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Jun 2 00:07:05 2020 +0100

    libstdc++: Fix filesystem::u8path for mingw targets (PR 95392)
    
    When I refactored filesystem::path string conversions in
    r11-587-584d52b088f9fcf78704b504c3f1f07e17c1cded I failed to update the
    mingw-specific code in filesystem::u8path, causing a bootstrap failure.
    
    This fixes it, and further refactors the mingw-specific code along the
    same lines as the previous commit. All conversions from UTF-8 strings to
    wide strings now use the same helper function, __wstr_from_utf8.
    
            PR libstdc++/95392
            * include/bits/fs_path.h (path::_S_to_string): Move to
            namespace-scope and rename to ...
            (__detail::__string_from_range): ... this.
            [WINDOWS] (__detail::__wstr_from_utf8): New function template to
            convert a char sequence containing UTF-8 to wstring.
            (path::_S_convert(Iter, Iter)): Adjust call to _S_to_string.
            (path::_S_convert_loc(Iter, Iter, const locale&)): Likewise.
            (u8path(InputIterator, InputIterator)) [WINDOWS]: Use
            __string_from_range to obtain a contiguous range and
            __wstr_from_utf8 to obtain a wide string.
            (u8path(const Source&)) [WINDOWS]: Use __effective_range to
            obtain a contiguous range and __wstr_from_utf8 to obtain a wide
            string.
            (path::_S_convert(const _EcharT*, const _EcharT)) [WINDOWS]:
            Use __wstr_from_utf8.

diff --git a/libstdc++-v3/include/bits/fs_path.h b/libstdc++-v3/include/bits/fs_path.h
index 2d2766ec62e..26ddf0afec4 100644
--- a/libstdc++-v3/include/bits/fs_path.h
+++ b/libstdc++-v3/include/bits/fs_path.h
@@ -211,6 +211,51 @@ namespace __detail
 #endif
 			 , _Val>;
 
+  // Create a string or string view from an iterator range.
+  template<typename _InputIterator>
+    inline auto
+    __string_from_range(_InputIterator __first, _InputIterator __last)
+    {
+      using _EcharT
+	= typename std::iterator_traits<_InputIterator>::value_type;
+      static_assert(__is_encoded_char<_EcharT>);
+
+#if __cpp_lib_concepts
+      constexpr bool __contiguous = std::contiguous_iterator<_InputIterator>;
+#else
+      constexpr bool __contiguous
+	= is_pointer_v<decltype(std::__niter_base(__first))>;
+#endif
+      if constexpr (__contiguous)
+	{
+	  // For contiguous iterators we can just return a string view.
+	  const auto* __f = std::__to_address(std::__niter_base(__first));
+	  const auto* __l = std::__to_address(std::__niter_base(__last));
+	  return basic_string_view<_EcharT>(__f, __l - __f);
+	}
+      else
+	// Conversion requires contiguous characters, so create a string.
+	return basic_string<_EcharT>(__first, __last);
+    }
+
+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+  template<typename _Tp>
+    inline std::wstring
+    __wstr_from_utf8(const _Tp& __str)
+    {
+      static_assert(std::is_same_v<typename _Tp::value_type, char>);
+      std::wstring __wstr;
+      // XXX This assumes native wide encoding is UTF-16.
+      std::codecvt_utf8_utf16<wchar_t> __wcvt;
+      const auto __p = __str.data();
+      if (!__str_codecvt_in_all(__p, __p + __str.size(), __wstr, __wcvt))
+	_GLIBCXX_THROW_OR_ABORT(filesystem_error(
+	      "Cannot convert character sequence",
+	      std::make_error_code(errc::illegal_byte_sequence)));
+      return __wstr;
+    }
+#endif
+
 } // namespace __detail
   /// @endcond
 
@@ -542,33 +587,6 @@ namespace __detail
 
     pair<const string_type*, size_t> _M_find_extension() const noexcept;
 
-    // Create a string or string view from an iterator range.
-    template<typename _InputIterator>
-      static auto
-      _S_to_string(_InputIterator __first, _InputIterator __last)
-      {
-	using _EcharT
-	  = typename std::iterator_traits<_InputIterator>::value_type;
-	static_assert(__detail::__is_encoded_char<_EcharT>);
-
-#if __cpp_lib_concepts
-	constexpr bool __contiguous = std::contiguous_iterator<_InputIterator>;
-#else
-	constexpr bool __contiguous
-	  = is_pointer_v<decltype(std::__niter_base(__first))>;
-#endif
-	if constexpr (__contiguous)
-	  {
-	    // For contiguous iterators we can just return a string view.
-	    const auto* __f = std::__to_address(std::__niter_base(__first));
-	    const auto* __l = std::__to_address(std::__niter_base(__last));
-	    return basic_string_view<_EcharT>(__f, __l - __f);
-	  }
-	else
-	  // Conversion requires contiguous characters, so create a string.
-	  return basic_string<_EcharT>(__first, __last);
-      }
-
     // path::_S_convert creates a basic_string<value_type> or
     // basic_string_view<value_type> from a range (either the effective
     // range of a Source parameter, or a pair of InputIterator parameters),
@@ -602,7 +620,7 @@ namespace __detail
     template<typename _Iter>
       static auto
       _S_convert(_Iter __first, _Iter __last)
-      { return _S_convert(_S_to_string(__first, __last)); }
+      { return _S_convert(__detail::__string_from_range(__first, __last)); }
 
     static string_type
     _S_convert_loc(const char* __first, const char* __last,
@@ -612,7 +630,7 @@ namespace __detail
       static string_type
       _S_convert_loc(_Iter __first, _Iter __last, const std::locale& __loc)
       {
-	const auto __s = _S_to_string(__first, __last);
+	const auto __s = __detail::__string_from_range(__first, __last);
 	return _S_convert_loc(__s.data(), __s.data() + __s.size(), __loc);
       }
 
@@ -738,28 +756,10 @@ namespace __detail
     {
 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
       if constexpr (is_same_v<_CharT, char>)
-	{
-	  // XXX This assumes native wide encoding is UTF-16.
-	  std::codecvt_utf8_utf16<path::value_type> __cvt;
-	  path::string_type __tmp;
-	  if constexpr (is_pointer_v<_InputIterator>)
-	    {
-	      if (__str_codecvt_in_all(__first, __last, __tmp, __cvt))
-		return path{ __tmp };
-	    }
-	  else
-	    {
-	      const std::string __u8str{__first, __last};
-	      const char* const __p = __u8str.data();
-	      if (__str_codecvt_in_all(__p, __p + __u8str.size(), __tmp, __cvt))
-		return path{ __tmp };
-	    }
-	  _GLIBCXX_THROW_OR_ABORT(filesystem_error(
-	      "Cannot convert character sequence",
-	      std::make_error_code(errc::illegal_byte_sequence)));
-	}
+	return path{ __detail::__wstr_from_utf8(
+	    __detail::__string_from_range(__first, __last)) };
       else
-	return path{ __first, __last };
+	return path{ __first, __last }; // constructor handles char8_t
 #else
       // This assumes native normal encoding is UTF-8.
       return path{ __first, __last };
@@ -778,21 +778,12 @@ namespace __detail
     {
 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
       if constexpr (is_same_v<_CharT, char>)
-	{
-	  if constexpr (is_convertible_v<const _Source&, std::string_view>)
-	    {
-	      const std::string_view __s = __source;
-	      return filesystem::u8path(__s.data(), __s.data() + __s.size());
-	    }
-	  else
-	    {
-	      std::string __s = path::_S_string_from_iter(__source);
-	      return filesystem::u8path(__s.data(), __s.data() + __s.size());
-	    }
-	}
+	return path{ __detail::__wstr_from_utf8(
+	    __detail::__effective_range(__source)) };
       else
-	return path{ __source };
+	return path{ __source }; // constructor handles char8_t
 #else
+      // This assumes native normal encoding is UTF-8.
       return path{ __source };
 #endif
     }
@@ -836,11 +827,8 @@ namespace __detail
 #ifdef _GLIBCXX_USE_CHAR8_T
 	  else if constexpr (is_same_v<_EcharT, char8_t>)
 	    {
-	      const char* __f2 = (const char*)__f;
-	      const char* __l2 = (const char*)__l;
-	      std::codecvt_utf8_utf16<wchar_t> __wcvt;
-	      if (__str_codecvt_in_all(__f2, __l2, __wstr, __wcvt))
-		return __wstr;
+	      const auto __f2 = reinterpret_cast<const char*>(__f);
+	      return __detail::__wstr_from_utf8(string_view(__f2, __l - __f));
 	    }
 #endif
 	  else // char16_t or char32_t
@@ -849,13 +837,7 @@ namespace __detail
 	      { } __cvt;
 	      std::string __str;
 	      if (__str_codecvt_out_all(__f, __l, __str, __cvt))
-		{
-		  const char* __f2 = __str.data();
-		  const char* __l2 = __f2 + __str.size();
-		  std::codecvt_utf8_utf16<wchar_t> __wcvt;
-		  if (__str_codecvt_in_all(__f2, __l2, __wstr, __wcvt))
-		    return __wstr;
-		}
+		return __detail::__wstr_from_utf8(__str);
 	    }
 #else // ! windows
 	  struct _UCvt : std::codecvt<_EcharT, char, std::mbstate_t>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-06-01 23:14 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-23  8:40 [committed 0/3] libstdc++: Refactor filesystem::path string conversions Jonathan Wakely
2020-05-23  8:42 ` [committed 1/3] libstdc++: Simplify filesystem::path SFINAE constraints Jonathan Wakely
2020-05-23  8:43 ` [committed 2/3] libstdc++: Remove incorrect static specifiers Jonathan Wakely
2020-05-23  8:44 ` [committed 3/3] libstdc++: Refactor filesystem::path string conversions Jonathan Wakely
2020-06-01 23:14   ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).