diff --git a/libcxx/include/__filesystem/path.h b/libcxx/include/__filesystem/path.h --- a/libcxx/include/__filesystem/path.h +++ b/libcxx/include/__filesystem/path.h @@ -19,6 +19,7 @@ #include <__fwd/hash.h> #include <__iterator/back_insert_iterator.h> #include <__iterator/iterator_traits.h> +#include <__memory/pointer_traits.h> #include <__type_traits/decay.h> #include <__type_traits/is_pointer.h> #include <__type_traits/remove_const.h> @@ -443,6 +444,58 @@ #endif /* !_LIBCPP_HAS_NO_CHAR8_T */ #endif /* _LIBCPP_WIN32API */ +# if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) +template ::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI wstring __widen_char_source(_Iterator __first, _Iterator __last, const locale& __loc) { + static_assert(__is_pathable_iter<_Iterator>::value, "this function requires a pathable iterator"); + + wstring __r; + if (!has_facet>(__loc)) + return __r; + + __r.reserve(__last - __first); + std::__widen_using_codecvt( + use_facet>(__loc), + back_inserter(__r), + std::__to_address(__first), + std::__to_address(__last)); + return __r; +} + +template ::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI wstring __widen_char_source(_Iterator __first, _Iterator __last, const locale& __loc) { + static_assert(__is_pathable_iter<_Iterator>::value, "this function requires a pathable iterator"); + string __tmp(__first, __last); + return filesystem::__widen_char_source(__tmp.begin(), __tmp.end(), __loc); +} + +template +_LIBCPP_HIDE_FROM_ABI wstring __widen_char_source(basic_string_view const& __sv, const locale& __loc) { + return filesystem::__widen_char_source(__sv.begin(), __sv.end(), __loc); +} + +template +_LIBCPP_HIDE_FROM_ABI wstring __widen_char_source(basic_string const& __s, const locale& __loc) { + return filesystem::__widen_char_source(__s.begin(), __s.end(), __loc); +} + +template ::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI wstring __widen_char_source(_Iterator __it, const locale& __loc) { + static_assert(__is_pathable_iter<_Iterator>::value, "this function requires a pathable iterator"); + auto __len = char_traits::length(std::__to_address(__it)); + return filesystem::__widen_char_source(std::__to_address(__it), std::__to_address(__it) + __len, __loc); +} + +template ::value, int> = 0> +_LIBCPP_HIDE_FROM_ABI wstring __widen_char_source(_Iterator __it, const locale& __loc) { + static_assert(__is_pathable_iter<_Iterator>::value, "this function requires a pathable iterator"); + string __s; + for (char __c = *__it; __c != '\0'; ++__it, (void)(__c = *__it)) + __s.push_back(__c); + return filesystem::__widen_char_source(__s.begin(), __s.end(), __loc); +} +# endif // !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) + class _LIBCPP_EXPORTED_FROM_ABI path { template using _EnableIfPathable = __enable_if_t<__is_pathable<_SourceOrIter>::value, _Tp>; @@ -493,16 +546,17 @@ _PathCVT<_ItVal>::__append_range(__pn_, __first, __last); } -/* -#if !defined(_LIBCPP_HAS_NO_LOCALIZATION) - // TODO Implement locale conversions. - template > - path(const _Source& __src, const locale& __loc, format = format::auto_format); +# if !defined(_LIBCPP_HAS_NO_LOCALIZATION) && !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) + template = 0> + _LIBCPP_HIDE_FROM_ABI path(const _Source& __src, const locale& __loc, format = format::auto_format) { + _SourceCVT::__append_source(__pn_, filesystem::__widen_char_source(__src, __loc)); + } + template - path(_InputIt __first, _InputIt _last, const locale& __loc, - format = format::auto_format); -#endif -*/ + _LIBCPP_HIDE_FROM_ABI path(_InputIt __first, _InputIt __last, const locale& __loc, format = format::auto_format) { + _SourceCVT::__append_source(__pn_, filesystem::__widen_char_source(__first, __last, __loc)); + } +# endif _LIBCPP_HIDE_FROM_ABI ~path() = default; diff --git a/libcxx/include/__locale b/libcxx/include/__locale --- a/libcxx/include/__locale +++ b/libcxx/include/__locale @@ -1557,6 +1557,30 @@ } }; +_LIBCPP_SUPPRESS_DEPRECATED_PUSH +template +_LIBCPP_HIDE_FROM_ABI _OutputIterator __widen_using_codecvt( + codecvt<_InternT, _ExternT, _StateT> const& __cvt, _OutputIterator __s, const char* __nb, const char* __ne) { + codecvt_base::result __r = codecvt_base::ok; + mbstate_t __mb; + while (__nb < __ne && __r != codecvt_base::error) { + static const int __sz = 32; + _InternT __buf[__sz]; + _InternT* __bn; + const char* __nn = __nb; + __r = __cvt.in(__mb, __nb, __ne - __nb > __sz ? __nb + __sz : __ne, __nn, __buf, __buf + __sz, __bn); + if (__r == codecvt_base::error || __nn == __nb) + std::__throw_runtime_error("locale not supported"); + for (const _InternT* __p = __buf; __p < __bn; ++__p) { + *__s = (wchar_t)*__p; + ++__s; + } + __nb = __nn; + } + return __s; +} +_LIBCPP_SUPPRESS_DEPRECATED_POP + template struct __widen_from_utf8 { @@ -1595,23 +1619,7 @@ _OutputIterator operator()(_OutputIterator __s, const char* __nb, const char* __ne) const { - result __r = ok; - mbstate_t __mb; - while (__nb < __ne && __r != error) - { - const int __sz = 32; - char16_t __buf[__sz]; - char16_t* __bn; - const char* __nn = __nb; - __r = do_in(__mb, __nb, __ne - __nb > __sz ? __nb+__sz : __ne, __nn, - __buf, __buf+__sz, __bn); - if (__r == codecvt_base::error || __nn == __nb) - __throw_runtime_error("locale not supported"); - for (const char16_t* __p = __buf; __p < __bn; ++__p, ++__s) - *__s = *__p; - __nb = __nn; - } - return __s; + return std::__widen_using_codecvt(*this, __s, __nb, __ne); } }; @@ -1631,23 +1639,7 @@ _OutputIterator operator()(_OutputIterator __s, const char* __nb, const char* __ne) const { - result __r = ok; - mbstate_t __mb; - while (__nb < __ne && __r != error) - { - const int __sz = 32; - char32_t __buf[__sz]; - char32_t* __bn; - const char* __nn = __nb; - __r = do_in(__mb, __nb, __ne - __nb > __sz ? __nb+__sz : __ne, __nn, - __buf, __buf+__sz, __bn); - if (__r == codecvt_base::error || __nn == __nb) - __throw_runtime_error("locale not supported"); - for (const char32_t* __p = __buf; __p < __bn; ++__p, ++__s) - *__s = *__p; - __nb = __nn; - } - return __s; + return std::__widen_using_codecvt(*this, __s, __nb, __ne); } }; diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source.pass.cpp --- a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source.pass.cpp +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source.pass.cpp @@ -116,7 +116,7 @@ static_assert(std::is_constructible::value, ""); } { - using It = cpp17_output_iterator; + using It = cpp17_output_iterator; static_assert(!std::is_constructible::value, ""); } diff --git a/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source_and_locale.pass.cpp b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source_and_locale.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source_and_locale.pass.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03 +// UNSUPPORTED: availability-filesystem-missing +// UNSUPPORTED: no-localization, no-wide-characters + +// + +// class path + +// template +// path(const Source& source, const locale& loc, format = format::auto_format); +// +// template +// path(InputIterator first, InputIterator last, const locale& loc, format = format::auto_format); + +#include "filesystem_include.h" +#include +#include +#include +#include + +#include "../../path_helper.h" +#include "test_macros.h" +#include "test_iterators.h" +#include "min_allocator.h" + +template +void RunTestCase(const char* TestPath, const char* Expect, const std::locale& Locale, Args... args) { + const char* TestPathEnd = StrEnd(TestPath); + const std::size_t Size = TestPathEnd - TestPath; + const std::size_t SSize = StrEnd(Expect) - Expect; + assert(Size == SSize); + // StringTypes + { + const std::string S(TestPath); + fs::path p(S, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + { + const std::string_view S(TestPath); + fs::path p(S, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + // Char* pointers + { + fs::path p(TestPath, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + { + fs::path p(TestPath, TestPathEnd, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + // Iterators + { + using It = cpp17_input_iterator; + fs::path p(It{TestPath}, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + { + using It = cpp17_input_iterator; + fs::path p(It{TestPath}, It{TestPathEnd}, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } +} + +void test_sfinae() { + { + using It = cpp17_output_iterator; + static_assert(!std::is_constructible::value, ""); + } + { + using It = int*; + static_assert(!std::is_constructible::value, ""); + } +} + +struct CustomCodeCvt : std::codecvt { +protected: + result do_in(state_type&, + const extern_type* from, + const extern_type* from_end, + const extern_type*& from_next, + intern_type* to, + intern_type* to_end, + intern_type*& to_next) const override { + for (; from < from_end && to < to_end; ++from, ++to) + *to = 'o'; + + from_next = from; + to_next = to; + + return result::ok; + } +}; + +int main(int, char**) { + std::locale Locale; + + // Ensure std::codecvt is used. + std::locale CustomLocale(Locale, new CustomCodeCvt()); + std::string TestPath("aaaa"); + std::string Expect("oooo"); + RunTestCase(TestPath.c_str(), Expect.c_str(), CustomLocale); + RunTestCase(TestPath.c_str(), Expect.c_str(), CustomLocale, fs::path::format::auto_format); + RunTestCase(TestPath.c_str(), Expect.c_str(), CustomLocale, fs::path::format::native_format); + RunTestCase(TestPath.c_str(), Expect.c_str(), CustomLocale, fs::path::format::generic_format); + + for (auto const& MS : PathList) { + RunTestCase(MS, MS, Locale); + RunTestCase(MS, MS, Locale, fs::path::format::auto_format); + RunTestCase(MS, MS, Locale, fs::path::format::native_format); + RunTestCase(MS, MS, Locale, fs::path::format::generic_format); + } + + test_sfinae(); + + return 0; +}