Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Construct path using a locale #20

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 28 additions & 34 deletions libcxx/include/__locale
Original file line number Diff line number Diff line change
Expand Up @@ -1352,6 +1352,32 @@ struct _LIBCPP_TEMPLATE_VIS __narrow_to_utf8<32>
}
};

template<class _InternT, class _ExternT, class _StateT, class _OutputIterator>
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 <size_t _Np>
struct __widen_from_utf8
{
Expand Down Expand Up @@ -1388,23 +1414,7 @@ struct _LIBCPP_TEMPLATE_VIS __widen_from_utf8<16>
_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);
}
};

Expand All @@ -1422,23 +1432,7 @@ struct _LIBCPP_TEMPLATE_VIS __widen_from_utf8<32>
_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);
}
};

Expand Down
116 changes: 111 additions & 5 deletions libcxx/include/filesystem
Original file line number Diff line number Diff line change
Expand Up @@ -735,11 +735,111 @@ struct _PathCVT<char> {
}
};

inline _LIBCPP_INLINE_VISIBILITY
wstring __widen_from_char_iter_pair(const char* __b, const char* __e, const locale& __loc) {
wstring __r;
if (!has_facet<codecvt<wchar_t, char, mbstate_t>>(__loc))
return __r;
__r.reserve(__e - __b);
__widen_using_codecvt(use_facet<codecvt<wchar_t, char, mbstate_t>>(__loc) , back_inserter(__r), __b, __e);
return __r;
}

template <class _InputIt,
class = typename enable_if<!is_constructible<const char*, _InputIt>::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 <class _Tp>
struct __is_widenable_from_char_string : public false_type {};

template <class _Traits, class _Alloc>
struct __is_widenable_from_char_string<
basic_string<char, _Traits, _Alloc>>
: true_type {
using _Str = basic_string<char, _Traits, _Alloc>;

static wstring __convert(_Str const& __s, const locale& __loc) {
return __widen_from_char_iter_pair(__s.data(), __s.data() + __s.size(), __loc);
}
};

template <class _Traits>
struct __is_widenable_from_char_string<
basic_string_view<char, _Traits>>
: true_type {
using _Str = basic_string_view<char, _Traits>;
static wstring __convert(_Str const& __sv, const locale& __loc) {
return __widen_from_char_iter_pair(__sv.data(), __sv.data() + __sv.size(), __loc);
}
};

template <class _Source, class _DS = typename decay<_Source>::type,
class _UnqualPtrType =
typename remove_const<typename remove_pointer<_DS>::type>::type>
struct __is_widenable_from_char_array : false_type {};

template <class _Source, class _ECharT>
struct __is_widenable_from_char_array<_Source, _ECharT*, char>
: true_type {

static wstring __convert(const char* __b, const locale& __loc) {
const char __sentinal = char{};
const char* __e = __b;
for (; *__e != __sentinal; ++__e)
;
return __widen_from_char_iter_pair(__b, __e, __loc);
}
};

template <class _Iter, bool _IsIt = __is_input_iterator<_Iter>::value,
class = char>
struct __is_widenable_from_char_iter : false_type {};

template <class _Iter>
struct __is_widenable_from_char_iter <
_Iter, true,
typename remove_reference<typename iterator_traits<_Iter>::value_type>::type >
: true_type {

static wstring __convert(_Iter __i, const locale& __loc) {
const char __sentinal = char{};
string __s;
for (; *__i != __sentinal; ++__i)
__s.push_back(*__i);
return __widen_from_char_iter_pair(__s.data(), __s.data() + __s.size(), __loc);
}
};

template <class _Tp, bool _IsStringT = __is_widenable_from_char_string<_Tp>::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 <class _Tp>
struct __is_widenable_from_char_source<_Tp, true, false, false> : __is_widenable_from_char_string<_Tp> {};

template <class _Tp>
struct __is_widenable_from_char_source<_Tp, false, true, false> : __is_widenable_from_char_array<_Tp> {
};

template <class _Tp>
struct __is_widenable_from_char_source<_Tp, false, false, true> : __is_widenable_from_char_iter<_Tp> {};

class _LIBCPP_TYPE_VIS path {
template <class _SourceOrIter, class _Tp = path&>
using _EnableIfPathable =
typename enable_if<__is_pathable<_SourceOrIter>::value, _Tp>::type;

template <class _SourceOrIter, class _Tp = path&>
using _EnableIfWidenableFromCharSource =
typename enable_if<__is_widenable_from_char_source<_SourceOrIter>::value, _Tp>::type;

template <class _Tp>
using _SourceChar = typename __is_pathable<_Tp>::__char_type;

Expand Down Expand Up @@ -779,12 +879,18 @@ public:
_PathCVT<_ItVal>::__append_range(__pn_, __first, __last);
}

// TODO Implement locale conversions.
template <class _Source, class = _EnableIfPathable<_Source, void> >
path(const _Source& __src, const locale& __loc, format = format::auto_format);
template <class _Source, class = _EnableIfWidenableFromCharSource<_Source> >
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 <class _InputIt>
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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

// <filesystem>

// class path

// template <class Source>
// path(const Source& source);
// template <class InputIterator>
// path(InputIterator first, InputIterator last);


#include "filesystem_include.hpp"
#include <locale>
#include <type_traits>
#include <cassert>

#include "test_macros.h"
#include "test_iterators.h"
#include "min_allocator.h"
#include "filesystem_test_helper.hpp"

template <class ...Args>
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<char>() == Expect);
}
{
const std::string_view S(TestPath);
path p(S, Locale, args...);
assert(p.native() == Expect);
assert(p.string<char>() == Expect);
}
// Char* pointers
{
path p(TestPath, Locale, args...);
assert(p.native() == Expect);
assert(p.string<char>() == Expect);
}
{
path p(TestPath, TestPathEnd, Locale, args...);
assert(p.native() == Expect);
assert(p.string<char>() == Expect);
}
// Iterators
{
using It = input_iterator<const char*>;
path p(It{TestPath}, Locale, args...);
assert(p.native() == Expect);
assert(p.string<char>() == Expect);
}
{
using It = input_iterator<const char*>;
path p(It{TestPath}, It{TestPathEnd}, Locale, args...);
assert(p.native() == Expect);
assert(p.string<char>() == Expect);
}
}

void test_sfinae() {
using namespace fs;
{
using It = const char* const;
static_assert(std::is_constructible<path, It, std::locale>::value, "");
}
{
using It = input_iterator<const char*>;
static_assert(std::is_constructible<path, It, std::locale>::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<const char*, Traits>;
static_assert(std::__is_input_iterator<It>::value, "");
//static_assert(std::is_constructible<path, It, std::locale>::value, "");
}
{
using It = const wchar_t* const;
static_assert(!std::is_constructible<path, It, std::locale>::value, "");
}
{
using It = input_iterator<const wchar_t*>;
static_assert(!std::is_constructible<path, It, std::locale>::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<const wchar_t*, Traits>;
static_assert(!std::is_constructible<path, It, std::locale>::value, "");
}
{
using It = output_iterator<const char*>;
static_assert(!std::is_constructible<path, It, std::locale>::value, "");
}
{
static_assert(!std::is_constructible<path, int*, std::locale>::value, "");
}
}

struct CustomCodeCvt : std::codecvt<wchar_t, char, std::mbstate_t> {
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<wchar_t, char, std::mbstate_t> 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;
}