Index: libcxx/include/__locale =================================================================== --- libcxx/include/__locale +++ libcxx/include/__locale @@ -1352,6 +1352,32 @@ } }; +template +inline _LIBCPP_INLINE_VISIBILITY +_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) + { + 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) + __throw_runtime_error("locale not supported"); + for (const _InternT* __p = __buf; __p < __bn; ++__p, ++__s) + *__s = (wchar_t)*__p; + __nb = __nn; + } + return __s; +} + template struct __widen_from_utf8 { @@ -1388,23 +1414,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 = (wchar_t)*__p; - __nb = __nn; - } - return __s; + return __widen_using_codecvt(*this, __s, __nb, __ne); } }; @@ -1422,23 +1432,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 = (wchar_t)*__p; - __nb = __nn; - } - return __s; + return __widen_using_codecvt(*this, __s, __nb, __ne); } }; Index: libcxx/include/filesystem =================================================================== --- libcxx/include/filesystem +++ libcxx/include/filesystem @@ -735,11 +735,108 @@ } }; +inline _LIBCPP_INLINE_VISIBILITY +wstring __widen_from_char_iter_pair(const char* __b, const char* __e, const locale& __loc) { + wstring __r; + if (!has_facet>(__loc)) + return __r; + __r.reserve(__e - __b); + __widen_using_codecvt(use_facet>(__loc) , back_inserter(__r), __b, __e); + return __r; +} + +template ::value>::type> +inline _LIBCPP_INLINE_VISIBILITY +wstring __widen_from_char_iter_pair(_InputIt __b, _InputIt __e, const locale& __loc) { + const string __s(__b, __e); + return __widen_from_char_iter_pair(__s.data(), __s.data() + __s.size(), __loc); +} + +template +struct __is_widenable_from_char_string : public false_type {}; + +template +struct __is_widenable_from_char_string< + basic_string> + : true_type { + using _Str = basic_string; + + static wstring __convert(_Str const& __s, const locale& __loc) { + return __widen_from_char_iter_pair(__s.data(), __s.data() + __s.size(), __loc); + } +}; + +template +struct __is_widenable_from_char_string< + basic_string_view> + : true_type { + using _Str = basic_string_view; + static wstring __convert(_Str const& __sv, const locale& __loc) { + return __widen_from_char_iter_pair(__sv.data(), __sv.data() + __sv.size(), __loc); + } +}; + +template ::type, + class _UnqualPtrType = + typename remove_const::type>::type> +struct __is_widenable_from_char_array : false_type {}; + +template +struct __is_widenable_from_char_array<_Source, _ECharT*, char> + : true_type { + + static wstring __convert(const char* __b, const locale& __loc) { + const char* __e = __b; + for (; *__e != '\0'; ++__e) + ; + return __widen_from_char_iter_pair(__b, __e, __loc); + } +}; + +template ::value, + class = char> +struct __is_widenable_from_char_iter : false_type {}; + +template +struct __is_widenable_from_char_iter < + _Iter, true, + typename remove_reference::value_type>::type > + : true_type { + + static wstring __convert(_Iter __i, const locale& __loc) { + string __s; + for (; *__i != '\0'; ++__i) + __s.push_back(*__i); + return __widen_from_char_iter_pair(__s.data(), __s.data() + __s.size(), __loc); + } +}; + +template ::value, + bool IsCharArray = __is_widenable_from_char_array<_Tp>::value, + bool _IsIterT = !IsCharArray && __is_widenable_from_char_iter<_Tp>::value> +struct __is_widenable_from_char_source : false_type { + static_assert(!_IsStringT && !IsCharArray && !_IsIterT, "Must all be false"); +}; + +template +struct __is_widenable_from_char_source<_Tp, true, false, false> : __is_widenable_from_char_string<_Tp> {}; + +template +struct __is_widenable_from_char_source<_Tp, false, true, false> : __is_widenable_from_char_array<_Tp> {}; + +template +struct __is_widenable_from_char_source<_Tp, false, false, true> : __is_widenable_from_char_iter<_Tp> {}; + class _LIBCPP_TYPE_VIS path { template using _EnableIfPathable = typename enable_if<__is_pathable<_SourceOrIter>::value, _Tp>::type; + template + using _EnableIfWidenableFromCharSource = + typename enable_if<__is_widenable_from_char_source<_SourceOrIter>::value, _Tp>::type; + template using _SourceChar = typename __is_pathable<_Tp>::__char_type; @@ -779,12 +876,18 @@ _PathCVT<_ItVal>::__append_range(__pn_, __first, __last); } - // TODO Implement locale conversions. - template > - path(const _Source& __src, const locale& __loc, format = format::auto_format); + template > + path(const _Source& __src, const locale& __loc, + format = format::auto_format) { + using _Widener = __is_widenable_from_char_source<_Source>; + _SourceCVT<_VSTD::wstring>::__append_source(__pn_, _Widener::__convert(__src, __loc)); + } + template - path(_InputIt __first, _InputIt _last, const locale& __loc, - format = format::auto_format); + path(_InputIt __first, _InputIt __last, const locale& __loc, + format = format::auto_format) { + _SourceCVT<_VSTD::wstring>::__append_source(__pn_, __widen_from_char_iter_pair(__first, __last, __loc)); + } _LIBCPP_INLINE_VISIBILITY ~path() = default; Index: libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source_and_locale.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/input.output/filesystems/class.path/path.member/path.construct/source_and_locale.pass.cpp @@ -0,0 +1,164 @@ +//===----------------------------------------------------------------------===// +// +// 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++98, c++03 + +// + +// class path + +// template +// path(const Source& source); +// template +// path(InputIterator first, InputIterator last); + + +#include "filesystem_include.hpp" +#include +#include +#include + +#include "test_macros.h" +#include "test_iterators.h" +#include "min_allocator.h" +#include "filesystem_test_helper.hpp" + +template +void RunTestCase(const char* TestPath, const char* Expect, std::locale Locale, Args... args) { + using namespace fs; + 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); + path p(S, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + { + const std::string_view S(TestPath); + path p(S, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + // Char* pointers + { + path p(TestPath, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + { + path p(TestPath, TestPathEnd, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + // Iterators + { + using It = input_iterator; + path p(It{TestPath}, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } + { + using It = input_iterator; + path p(It{TestPath}, It{TestPathEnd}, Locale, args...); + assert(p.native() == Expect); + assert(p.string() == Expect); + } +} + +void test_sfinae() { + using namespace fs; + { + using It = const char* const; + static_assert(std::is_constructible::value, ""); + } + { + using It = input_iterator; + static_assert(std::is_constructible::value, ""); + } + { + struct Traits { + using iterator_category = std::input_iterator_tag; + using value_type = const char; + using pointer = const char*; + using reference = const char&; + using difference_type = std::ptrdiff_t; + }; + using It = input_iterator; + static_assert(std::__is_input_iterator::value, ""); + //static_assert(std::is_constructible::value, ""); + } + { + using It = const wchar_t* const; + static_assert(!std::is_constructible::value, ""); + } + { + using It = input_iterator; + static_assert(!std::is_constructible::value, ""); + } + { + struct Traits { + using iterator_category = std::input_iterator_tag; + using value_type = const wchar_t; + using pointer = const wchar_t*; + using reference = const wchar_t&; + using difference_type = std::ptrdiff_t; + }; + using It = input_iterator; + static_assert(!std::is_constructible::value, ""); + } + { + using It = output_iterator; + static_assert(!std::is_constructible::value, ""); + } + { + 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; +}