diff --git a/Help/dev/source.rst b/Help/dev/source.rst index c36f3349e6..3aad19427d 100644 --- a/Help/dev/source.rst +++ b/Help/dev/source.rst @@ -61,6 +61,11 @@ are accessible under the ``cm/`` directory. The headers under ``cm/`` can be used in place of the standard ones when extended features are needed. For example ```` can be used in place of ````. +The class ``cm::filesystem::path``, from the ```` header, is +fully compatible with the class ``std::filesystem::path`` regarding the API but +is a specific implementation (derived from the ``std::filesystem::path`` class) +to ensure a behavior independent of the current locale. + Available features are: * From ``C++14``: diff --git a/Source/Checks/cm_cxx_filesystem.cxx b/Source/Checks/cm_cxx_filesystem.cxx index ccbb3399a8..b9eb1b48d5 100644 --- a/Source/Checks/cm_cxx_filesystem.cxx +++ b/Source/Checks/cm_cxx_filesystem.cxx @@ -31,6 +31,17 @@ int main() b1.lexically_normal()) { return 1; } + + // LWG 3070 not implemented on some old Visual Studio compilers (2017, for + // example) + if (!std::filesystem::path("/a:/b:").lexically_relative("/a:/c:").empty()) { + return 1; + } + // LWG 3096 not implemented on some old Visual Studio compilers (2017, for + // example) + if (fs::path("/a").lexically_relative("/a/.") != ".") { + return 1; + } #endif // If std::string is copy-on-write, the std::filesystem::path diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 82ffb98363..cbb7fdf281 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -37,6 +37,7 @@ set(CMakeLib_TESTS testCMExtAlgorithm.cxx testCMExtEnumSet.cxx testList.cxx + testCMFilesystemPath.cxx testCMakePath.cxx ) if(CMake_ENABLE_DEBUGGER) @@ -50,9 +51,6 @@ if(CMake_ENABLE_DEBUGGER) testDebuggerThread.cxx ) endif() -if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM) - list(APPEND CMakeLib_TESTS testCMFilesystemPath.cxx) -endif() add_executable(testUVProcessChainHelper testUVProcessChainHelper.cxx) target_link_libraries(testUVProcessChainHelper CMakeLib) diff --git a/Utilities/std/cm/bits/fs_path.cxx b/Utilities/std/cm/bits/fs_path.cxx index b552308183..431916332c 100644 --- a/Utilities/std/cm/bits/fs_path.cxx +++ b/Utilities/std/cm/bits/fs_path.cxx @@ -24,10 +24,45 @@ # include # include +#endif + namespace cm { namespace filesystem { namespace internals { +// class unicode_helper +unicode_helper::utf8_state unicode_helper::decode(utf8_state const state, + std::uint8_t const fragment, + std::uint32_t& codepoint) +{ + std::uint32_t const utf8_state_info[] = { + // encoded states + 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, + 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, + 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, + 0x99999999u, 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, + 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, + 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, + 0u, 0u, + }; + std::uint8_t category = fragment < 128 + ? 0 + : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; + codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) + : (0xffu >> category) & fragment); + return state == s_reject + ? s_reject + : static_cast( + (utf8_state_info[category + 16] >> (state << 2)) & 0xf); +} +} +} + +#if !defined(CMake_HAVE_CXX_FILESYSTEM) + +namespace filesystem { +namespace internals { + class path_parser { # if defined(__SUNPRO_CC) && defined(__sparc) @@ -480,54 +515,6 @@ private: cm::string_view Entry; }; -// class unicode_helper -void unicode_helper::append(std::string& str, std::uint32_t codepoint) -{ - if (codepoint <= 0x7f) { - str.push_back(static_cast(codepoint)); - } else if (codepoint >= 0x80 && codepoint <= 0x7ff) { - str.push_back(static_cast((codepoint >> 6) + 192)); - str.push_back(static_cast((codepoint & 0x3f) + 128)); - } else if ((codepoint >= 0x800 && codepoint <= 0xd7ff) || - (codepoint >= 0xe000 && codepoint <= 0xffff)) { - str.push_back(static_cast((codepoint >> 12) + 224)); - str.push_back(static_cast(((codepoint & 0xfff) >> 6) + 128)); - str.push_back(static_cast((codepoint & 0x3f) + 128)); - } else if (codepoint >= 0x10000 && codepoint <= 0x10ffff) { - str.push_back(static_cast((codepoint >> 18) + 240)); - str.push_back(static_cast(((codepoint & 0x3ffff) >> 12) + 128)); - str.push_back(static_cast(((codepoint & 0xfff) >> 6) + 128)); - str.push_back(static_cast((codepoint & 0x3f) + 128)); - } else { - append(str, 0xfffd); - } -} - -unicode_helper::utf8_state unicode_helper::decode(utf8_state const state, - std::uint8_t const fragment, - std::uint32_t& codepoint) -{ - std::uint32_t const utf8_state_info[] = { - // encoded states - 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, - 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, - 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, - 0x99999999u, 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, - 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, - 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, - 0u, 0u, - }; - std::uint8_t category = fragment < 128 - ? 0 - : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; - codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) - : (0xffu >> category) & fragment); - return state == s_reject - ? s_reject - : static_cast( - (utf8_state_info[category + 16] >> (state << 2)) & 0xf); -} - } // internals // Class path @@ -1018,13 +1005,5 @@ std::size_t hash_value(path const& p) noexcept return value; } } // filesystem -} // cm - -#else - -// Avoid empty translation unit. -void cm_filesystem_path_cxx() -{ -} - #endif +} // cm diff --git a/Utilities/std/cm/filesystem b/Utilities/std/cm/filesystem index 81edd71906..8b1b03ddff 100644 --- a/Utilities/std/cm/filesystem +++ b/Utilities/std/cm/filesystem @@ -7,6 +7,16 @@ #include "cmSTL.hxx" // IWYU pragma: keep +#include +#include +#include +#include +#include + +#include +#include +#include + #if defined(CMake_HAVE_CXX_FILESYSTEM) # include // IWYU pragma: export @@ -14,17 +24,10 @@ #else # include -# include # include -# include # include -# include -# include # include -# include -# include -# include # if defined(_WIN32) && !defined(__CYGWIN__) # include @@ -35,24 +38,14 @@ namespace cm { namespace filesystem { -#if defined(CMake_HAVE_CXX_FILESYSTEM) - -using std::filesystem::path; -using std::filesystem::swap; -using std::filesystem::hash_value; - -#else - -# if !defined(CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR) +#if !defined(CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR) // Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile // the source_traits for iterator check. So disable it for now. -# define CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR 0 -# endif +# define CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR 0 +#endif namespace internals { -class path_parser; - class unicode_helper { protected: @@ -81,7 +74,28 @@ protected: return (c & 0xfffffc00) == 0xdc00; } - static void append(std::string& str, std::uint32_t codepoint); + template + static void append(std::basic_string& str, std::uint32_t codepoint) + { + if (codepoint <= 0x7f) { + str.push_back(static_cast(codepoint)); + } else if (codepoint >= 0x80 && codepoint <= 0x7ff) { + str.push_back(static_cast((codepoint >> 6) + 192)); + str.push_back(static_cast((codepoint & 0x3f) + 128)); + } else if ((codepoint >= 0x800 && codepoint <= 0xd7ff) || + (codepoint >= 0xe000 && codepoint <= 0xffff)) { + str.push_back(static_cast((codepoint >> 12) + 224)); + str.push_back(static_cast(((codepoint & 0xfff) >> 6) + 128)); + str.push_back(static_cast((codepoint & 0x3f) + 128)); + } else if (codepoint >= 0x10000 && codepoint <= 0x10ffff) { + str.push_back(static_cast((codepoint >> 18) + 240)); + str.push_back(static_cast(((codepoint & 0x3ffff) >> 12) + 128)); + str.push_back(static_cast(((codepoint & 0xfff) >> 6) + 128)); + str.push_back(static_cast((codepoint & 0x3f) + 128)); + } else { + append(str, 0xfffd); + } + } static utf8_state decode(utf8_state const state, std::uint8_t const fragment, std::uint32_t& codepoint); @@ -98,31 +112,38 @@ class unicode::type> { public: // UTF32 -> UTF8 - static std::string to_utf8(std::wstring const& str) + template + static std::basic_string to_utf8(wchar_t const* str) { - std::string result; - for (auto c : str) { + std::basic_string result; + while (auto c = *str++) { append(result, c); } return result; } - static std::string to_utf8(Char c) + template + static std::basic_string to_utf8(std::wstring const& str) { - std::string result; + return unicode::template to_utf8(str.c_str()); + } + template + static std::basic_string to_utf8(wchar_t c) + { + std::basic_string result; append(result, c); return result; } // UTF8 -> UTF32 - static std::wstring from_utf8(std::string const& str) + template + static std::wstring from_utf8(UTF8Char const* str, std::size_t length) { std::wstring result; - result.reserve(str.length()); - auto iter = str.begin(); + result.reserve(length); utf8_state state = s_start; std::uint32_t codepoint = 0; - while (iter < str.end()) { - if ((state = decode(state, static_cast(*iter++), + while (*str) { + if ((state = decode(state, static_cast(*str++), codepoint)) == s_start) { result += static_cast(codepoint); codepoint = 0; @@ -137,7 +158,19 @@ public: } return result; } - static std::wstring from_utf8(char c) + template + static std::wstring from_utf8(UTF8Char const* str) + { + return unicode::from_utf8( + str, std::strlen(reinterpret_cast(str))); + } + template + static std::wstring from_utf8(std::basic_string const& str) + { + return unicode::from_utf8(str.c_str(), str.length()); + } + template + static std::wstring from_utf8(UTF8Char c) { std::wstring result; utf8_state state = s_start; @@ -159,19 +192,19 @@ class unicode::type> { public: // UTF16 -> UTF8 - static std::string to_utf8(std::wstring const& str) + template + static std::basic_string to_utf8(wchar_t const* str) { - std::string result; - for (auto iter = str.begin(); iter != str.end(); ++iter) { - std::uint32_t c = *iter; + std::basic_string result; + for (; *str; ++str) { + std::uint32_t c = *str; if (is_surrogate(c)) { - ++iter; - if (iter != str.end() && is_high_surrogate(c) && - is_low_surrogate(*iter)) { - append(result, (std::uint32_t(c) << 10) + *iter - 0x35fdc00); + ++str; + if (*str && is_high_surrogate(c) && is_low_surrogate(*str)) { + append(result, (std::uint32_t(c) << 10) + *str - 0x35fdc00); } else { append(result, 0xfffd); - if (iter == str.end()) { + if (!*str) { break; } } @@ -181,9 +214,15 @@ public: } return result; } - static std::string to_utf8(Char c) + template + static std::basic_string to_utf8(std::wstring const& str) { - std::string result; + return unicode::template to_utf8(str.c_str()); + } + template + static std::basic_string to_utf8(wchar_t c) + { + std::basic_string result; if (is_surrogate(c)) { append(result, 0xfffd); } else { @@ -193,15 +232,15 @@ public: } // UTF8 -> UTF16 - static std::wstring from_utf8(std::string const& str) + template + static std::wstring from_utf8(UTF8Char const* str, std::size_t length) { std::wstring result; - result.reserve(str.length()); - auto iter = str.begin(); + result.reserve(length); utf8_state state = s_start; std::uint32_t codepoint = 0; - while (iter < str.end()) { - if ((state = decode(state, static_cast(*iter++), + while (*str) { + if ((state = decode(state, static_cast(*str++), codepoint)) == s_start) { if (codepoint <= 0xffff) { result += static_cast(codepoint); @@ -224,7 +263,19 @@ public: } return result; } - static std::wstring from_utf8(char c) + template + static std::wstring from_utf8(UTF8Char const* str) + { + return unicode::from_utf8( + str, std::strlen(reinterpret_cast(str))); + } + template + static std::wstring from_utf8(std::basic_string const& str) + { + return unicode::from_utf8(str.c_str(), str.length()); + } + template + static std::wstring from_utf8(UTF8Char c) { std::wstring result; utf8_state state = s_start; @@ -258,6 +309,10 @@ public: { return unicode::from_utf8(in); } + std::wstring operator()(std::string&& in) + { + return unicode::from_utf8(in); + } std::wstring operator()(char const* in) { return unicode::from_utf8(in); @@ -270,19 +325,27 @@ class unicode_converter public: std::string operator()(std::wstring const& in) { - return unicode::to_utf8(in); + return unicode::to_utf8(in); + } + std::string operator()(std::wstring&& in) + { + return unicode::to_utf8(in); } std::string operator()(wchar_t const* in) { - return unicode::to_utf8(in); + return unicode::to_utf8(in); + } + std::string operator()(wchar_t in) + { + return unicode::to_utf8(in); } - std::string operator()(wchar_t in) { return unicode::to_utf8(in); } }; template <> class unicode_converter { public: std::string operator()(std::string const& in) { return in; } + std::string operator()(std::string&& in) { return in; } std::string operator()(char const* in) { return std::string(in); } std::string operator()(char in) { return std::string(1, in); } }; @@ -291,9 +354,96 @@ class unicode_converter { public: std::wstring operator()(std::wstring const& in) { return in; } + std::wstring operator()(std::wstring&& in) { return in; } std::wstring operator()(wchar_t const* in) { return std::wstring(in); } std::wstring operator()(wchar_t in) { return std::wstring(1, in); } }; +#if defined(__cpp_char8_t) +template <> +class unicode_converter +{ +public: + std::u8string operator()(std::string const& in) + { + return std::u8string{ reinterpret_cast(in.c_str()) }; + } + std::u8string operator()(std::string&& in) + { + return std::u8string{ reinterpret_cast(in.c_str()) }; + } + std::u8string operator()(char const* in) + { + return std::u8string{ reinterpret_cast(in) }; + } + std::u8string operator()(char in) + { + return std::u8string{ 1, static_cast(in) }; + } +}; +template <> +class unicode_converter +{ +public: + std::string operator()(std::u8string const& in) + { + return std::string{ reinterpret_cast(in.c_str()) }; + } + std::string operator()(std::u8string&& in) + { + return std::string{ reinterpret_cast(in.c_str()) }; + } + std::string operator()(char8_t const* in) + { + return std::string{ reinterpret_cast(in) }; + } + std::string operator()(char8_t in) + { + return std::string{ 1, static_cast(in) }; + } +}; +template <> +class unicode_converter +{ +public: + std::u8string operator()(std::wstring const& in) + { + return unicode::to_utf8(in); + } + std::u8string operator()(std::wstring&& in) + { + return unicode::to_utf8(in); + } + std::u8string operator()(wchar_t const* in) + { + return unicode::to_utf8(in); + } + std::u8string operator()(wchar_t in) + { + return unicode::to_utf8(in); + } +}; +template <> +class unicode_converter +{ +public: + std::wstring operator()(std::u8string const& in) + { + return unicode::from_utf8(in); + } + std::wstring operator()(std::u8string&& in) + { + return unicode::from_utf8(in); + } + std::wstring operator()(char8_t const* in) + { + return unicode::from_utf8(in); + } + std::wstring operator()(char8_t in) + { + return unicode::from_utf8(in); + } +}; +#endif template struct string_converter @@ -334,6 +484,18 @@ struct string_converter return std::basic_string(unicode_converter()(in)); } template + static std::basic_string to(std::string&& in) + { + return std::basic_string( + unicode_converter()(std::move(in))); + } + template + static std::basic_string to(cm::string_view in) + { + return std::basic_string( + unicode_converter()(std::string{ in.begin(), in.end() })); + } + template static std::basic_string to(char const* in) { return std::basic_string(unicode_converter()(in)); @@ -343,6 +505,24 @@ struct string_converter { return std::basic_string(unicode_converter()(in)); } + + template + static std::basic_string to(Iterator it) + { + char e = '\0'; + std::string tmp; + for (; *it != e; ++it) { + tmp.push_back(*it); + } + return std::basic_string( + unicode_converter()(std::move(tmp))); + } + template + static std::basic_string to(Iterator begin, Iterator end) + { + return std::basic_string( + unicode_converter()(std::string{ begin, end })); + } }; template <> struct string_converter @@ -378,6 +558,12 @@ struct string_converter return std::basic_string(unicode_converter()(in)); } template + static std::basic_string to(std::wstring&& in) + { + return std::basic_string( + unicode_converter()(std::move(in))); + } + template static std::basic_string to(wchar_t const* in) { return std::basic_string(unicode_converter()(in)); @@ -387,7 +573,95 @@ struct string_converter { return std::basic_string(unicode_converter()(in)); } + + template + static std::basic_string to(Iterator it) + { + wchar_t e = '\0'; + std::wstring tmp; + for (; *it != e; ++it) { + tmp.push_back(*it); + } + return std::basic_string( + unicode_converter()(std::move(tmp))); + } + template + static std::basic_string to(Iterator begin, Iterator end) + { + return std::basic_string( + unicode_converter()(std::wstring{ begin, end })); + } }; +#if defined(__cpp_char8_t) +template <> +struct string_converter +{ + // some compilers, like gcc 4.8 does not implement the following C++11 + // signature: + // std::string::string(const string&, const Allocator&) + // As workaround, use char* pointer. + template + static std::basic_string to(std::u8string const& in, + Alloc const& a) + { + return std::basic_string( + unicode_converter()(in).c_str(), a); + } + template + static std::basic_string to(char8_t const* in, + Alloc const& a) + { + return std::basic_string( + unicode_converter()(in).c_str(), a); + } + template + static std::basic_string to(char8_t in, Alloc const& a) + { + return std::basic_string( + unicode_converter()(in).c_str(), a); + } + + template + static std::basic_string to(std::u8string const& in) + { + return std::basic_string(unicode_converter()(in)); + } + template + static std::basic_string to(std::u8string&& in) + { + return std::basic_string( + unicode_converter()(std::move(in))); + } + template + static std::basic_string to(char8_t const* in) + { + return std::basic_string(unicode_converter()(in)); + } + template + static std::basic_string to(char8_t in) + { + return std::basic_string(unicode_converter()(in)); + } + + template + static std::basic_string to(Iterator it) + { + char8_t e = '\0'; + std::u8string tmp; + for (; *it != e; ++it) { + tmp.push_back(*it); + } + return std::basic_string( + unicode_converter()(std::move(tmp))); + } + template + static std::basic_string to(Iterator begin, Iterator end) + { + return std::basic_string( + unicode_converter()(std::u8string{ begin, end })); + } +}; +#endif template struct source_traits @@ -400,6 +674,12 @@ struct source_traits using value_type = T; }; +template +struct source_traits +{ + using value_type = cm::decay_t; +}; + template struct source_traits> { @@ -413,111 +693,14 @@ struct source_traits using value_type = cm::string_view::value_type; }; -# if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR +#if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR template struct source_traits::value, void>> { using value_type = typename std::iterator_traits::type>::value_type; }; -# endif - -template -struct source_converter -{ -}; - -template <> -struct source_converter -{ - template - static void append_range(std::string& p, Iterator b, Iterator e) - { - if (b == e) { - return; - } - p.append(b, e); - } - template - static void append_range(std::string& p, Iterator b) - { - char e = '\0'; - - if (*b == e) { - return; - } - for (; *b != e; ++b) { - p.push_back(*b); - } - } - - static void append_source(std::string& p, cm::string_view const s) - { - append_range(p, s.begin(), s.end()); - } - template - static void append_source(std::string& p, - std::basic_string const& s) - { - append_range(p, s.begin(), s.end()); - } - template - static void append_source(std::string& p, Source const& s) - { - append_range(p, s); - } - - static void set_source(std::string& p, std::string&& s) { p = std::move(s); } -}; - -template <> -struct source_converter -{ - template - static void append_range(std::string& p, Iterator b, Iterator e) - { - if (b == e) { - return; - } - - std::wstring tmp(b, e); - std::string dest = string_converter::to(tmp); - p.append(dest.begin(), dest.end()); - } - template - static void append_range(std::string& p, Iterator b) - { - wchar_t e = '\0'; - - if (*b == e) { - return; - } - std::wstring tmp; - for (; *b != e; ++b) { - tmp.push_back(*b); - } - - std::string dest = string_converter::to(tmp); - p.append(dest.begin(), dest.end()); - } - - template - static void append_source(std::string& p, - std::basic_string const& s) - { - append_range(p, s.begin(), s.end()); - } - template - static void append_source(std::string& p, Source const& s) - { - append_range(p, s); - } - - static void set_source(std::string& p, std::wstring&& s) - { - p = string_converter::to(s); - } -}; +#endif template struct is_pathable_string : std::false_type @@ -580,7 +763,7 @@ struct is_pathable_iterator< { }; -# if defined(__SUNPRO_CC) && defined(__sparc) +#if defined(__SUNPRO_CC) && defined(__sparc) // Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile // the full 'is_pathable' check. We use it only to improve error messages // via 'enable_if' when calling methods with incorrect types. Just @@ -589,7 +772,7 @@ template struct is_pathable : std::true_type { }; -# else +#else template struct is_pathable : bool_constant::value || @@ -597,11 +780,700 @@ struct is_pathable is_pathable_iterator::value> { }; +#endif + +template +struct source_converter +{ +}; + +template <> +struct source_converter +{ + template + static void append_range(std::string& p, Iterator b, Iterator e) + { + if (b == e) { + return; + } + p.append(b, e); + } + template + static void append_range(std::string& p, Iterator b) + { + char e = '\0'; + + if (*b == e) { + return; + } + for (; *b != e; ++b) { + p.push_back(*b); + } + } + + static void append_source(std::string& p, cm::string_view s) + { + append_range(p, s.begin(), s.end()); + } + static void append_source(std::string& p, std::string const& s) + { + append_range(p, s.begin(), s.end()); + } + template + static void append_source(std::string& p, Source const& s) + { + append_range(p, s); + } + + static void set_source(std::string& p, std::string&& s) { p = std::move(s); } + + static std::string from(cm::string_view s) + { + return std::string{ s.begin(), s.end() }; + } + static std::string from(std::string const& s) { return s; } + static std::string from(std::string&& s) { return s; } + template + static std::string from(Source const& s) + { + std::string result; + append_source(result, s); + return result; + } + template + static std::string from(Iterator first, Iterator last) + { + return std::string{ first, last }; + } +}; +template <> +struct source_converter +{ + static std::wstring from(std::string const& s) + { + return string_converter::to(s); + } + static std::wstring from(std::string&& s) + { + return string_converter::to(std::move(s)); + } + template + static std::wstring from(Source const& s) + { + return string_converter::to(s); + } + template + static std::wstring from(Iterator first, Iterator last) + { + return string_converter::to(first, last); + } +}; + +template <> +struct source_converter +{ + template + static void append_range(std::wstring& p, Iterator b, Iterator e) + { + if (b == e) { + return; + } + p.append(b, e); + } + template + static void append_range(std::wstring& p, Iterator b) + { + char e = '\0'; + + if (*b == e) { + return; + } + for (; *b != e; ++b) { + p.push_back(*b); + } + } + + template + static void append_source(std::wstring& p, Source const& s) + { + append_range(p, s); + } + + static std::wstring from(std::wstring const& s) { return s; } + static std::wstring from(std::wstring&& s) { return s; } + template + static std::wstring from(Source const& s) + { + std::wstring result; + append_source(result, s); + return result; + } + template + static std::wstring from(Iterator first, Iterator last) + { + return std::wstring{ first, last }; + } +}; +template <> +struct source_converter +{ + template + static void append_range(std::string& p, Iterator b, Iterator e) + { + if (b == e) { + return; + } + + std::string dest = string_converter::to(b, e); + p.append(dest.begin(), dest.end()); + } + template + static void append_range(std::string& p, Iterator b) + { + wchar_t e = '\0'; + + if (*b == e) { + return; + } + + std::string dest = string_converter::to(b); + p.append(dest.begin(), dest.end()); + } + + static void append_source(std::string& p, std::wstring const& s) + { + append_range(p, s.begin(), s.end()); + } + template + static void append_source(std::string& p, Source const& s) + { + append_range(p, s); + } + + static void set_source(std::string& p, std::wstring&& s) + { + p = string_converter::to(std::move(s)); + } + + static std::string from(std::wstring const& s) + { + return string_converter::to(s); + } + static std::string from(std::wstring&& s) + { + return string_converter::to(std::move(s)); + } + template + static std::string from(Source const& s) + { + return string_converter::to(s); + } + template + static std::string from(Iterator first, Iterator last) + { + return string_converter::to(first, last); + } +}; + +#if defined(__cpp_char8_t) +template <> +struct source_converter +{ + static std::u8string from(std::string const& s) + { + return string_converter::to(s); + } + static std::u8string from(std::string&& s) + { + return string_converter::to(std::move(s)); + } + template + static std::u8string from(Source const& s) + { + return string_converter::to(s); + } + template + static std::u8string from(Iterator first, Iterator last) + { + return string_converter::to(first, last); + } +}; +template <> +struct source_converter +{ + static std::string from(std::u8string const& s) + { + return string_converter::to(s); + } + static std::string from(std::u8string&& s) + { + return string_converter::to(std::move(s)); + } + template + static std::string from(Source const& s) + { + return string_converter::to(s); + } + template + static std::string from(Iterator first, Iterator last) + { + return string_converter::to(first, last); + } +}; +template <> +struct source_converter +{ + static std::u8string from(std::wstring const& s) + { + return string_converter::to(s); + } + static std::u8string from(std::wstring&& s) + { + return string_converter::to(std::move(s)); + } + template + static std::u8string from(Source const& s) + { + return string_converter::to(s); + } + template + static std::u8string from(Iterator first, Iterator last) + { + return string_converter::to(first, last); + } +}; +template <> +struct source_converter +{ + static std::wstring from(std::u8string const& s) + { + return string_converter::to(s); + } + static std::wstring from(std::u8string&& s) + { + return string_converter::to(std::move(s)); + } + template + static std::wstring from(Source const& s) + { + return string_converter::to(s); + } + template + static std::wstring from(Iterator first, Iterator last) + { + return string_converter::to(first, last); + } +}; +#endif +} + +#if defined(CMake_HAVE_CXX_FILESYSTEM) + +class path : public std::filesystem::path +{ +private: + using base = std::filesystem::path; + // define the char type to convert to +# if defined(__cpp_char8_t) && defined(_WIN32) && !defined(__CYGWIN__) + using target_type = char8_t; +# else + using target_type = base::value_type; # endif + + template + using enable_if_pathable = + enable_if_t::value, path&>; + + template + using string_converter = filesystem::internals::source_converter< + typename filesystem::internals::source_traits::value_type, Target>; + template + using range_converter = filesystem::internals::source_converter< + typename std::iterator_traits::value_type, Target>; + +public: + class iterator; + using const_iterator = iterator; + + // Constructors + // ============ + path() noexcept {} + path(path const& p) + : base(p) + { + } + path(path&& p) noexcept + : base(std::move(p)) + { + } + path(string_type&& source, format fmt = format::auto_format) + : base(string_converter::from(std::move(source)), fmt) + { + } + template > + path(Source const& source, format fmt = format::auto_format) + : base(string_converter::from(source), fmt) + { + } + template > + path(Iterator first, Iterator last, format fmt = format::auto_format) + : base(range_converter::from(first, last), fmt) + { + } + + ~path() = default; + + // Assignments + // =========== + path& operator=(path const& p) + { + if (this != &p) { + this->base::operator=(static_cast(p)); + } + return *this; + } + path& operator=(path&& p) noexcept + { + if (this != &p) { + this->base::operator=(std::move(static_cast(p))); + } + return *this; + } + + path& operator=(string_type&& source) + { + return this->assign(std::move(source)); + } + template > + path& operator=(Source const& source) + { + return this->assign(source); + } + + path& assign(string_type&& source) + { + this->base::assign(string_converter::from(std::move(source))); + return *this; + } + template > + path& assign(Source const& source) + { + this->base::assign(string_converter::from(source)); + return *this; + } + template > + path& assign(Iterator first, Iterator last) + { + this->base::assign(range_converter::from(first, last)); + return *this; + } + + // Concatenation + // ============= + path& operator/=(path const& p) + { + this->base::operator/=(static_cast(p)); + return *this; + } + template + path& operator/=(Source const& source) + { + return this->append(source); + } + template > + path& append(Source const& source) + { + this->base::append(string_converter::from(source)); + return *this; + } + template > + path& append(Iterator first, Iterator last) + { + this->base::append(range_converter::from(first, last)); + return *this; + } + + path& operator+=(path const& p) + { + this->base::operator+=(static_cast(p)); + return *this; + } + path& operator+=(string_type const& str) { return this->concat(str); } + path& operator+=(cm::string_view str) + { + this->concat(str.begin(), str.end()); + return *this; + } + path& operator+=(value_type const* str) { return this->concat(str); } + path& operator+=(value_type const c) + { + return this->concat(string_type{ 1, c }); + } + template + path& operator+=(Source const& source) + { + return this->concat(source); + } + template > + path& concat(Source const& source) + { + this->base::concat(string_converter::from(source)); + return *this; + } + template > + path& concat(Iterator first, Iterator last) + { + this->base::concat(range_converter::from(first, last)); + return *this; + } + + // Format observers + // ================ + string_type const& native() const noexcept + { +# if defined(_WIN32) && !defined(__CYGWIN__) + this->native_path_ = this->wstring(); + return this->native_path_; +# else + return this->base::native(); +# endif + } + value_type const* c_str() const noexcept { return this->native().c_str(); } + operator string_type() const { return this->native(); } + + std::string string() const + { +# if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(__cpp_char8_t) + return string_converter::from( + this->base::u8string()); +# else + return string_converter::from( + this->base::wstring()); +# endif +# else + return this->base::string(); +# endif + } + std::wstring wstring() const { return this->base::wstring(); } +# if defined(__cpp_char8_t) + std::u8string u8string() const +# else + std::string u8string() const +# endif + { +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__cpp_char8_t) + return string_converter::from( + this->base::wstring()); +# else + return this->base::u8string(); +# endif + } + + std::string generic_string() const + { +# if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(__cpp_char8_t) + return string_converter::from( + this->base::generic_u8string()); +# else + return string_converter::from( + this->base::generic_wstring()); +# endif +# else + return this->base::generic_string(); +# endif + } + std::wstring generic_wstring() const + { + return this->base::generic_wstring(); + } +# if defined(__cpp_char8_t) + std::u8string generic_u8string() const +# else + std::string generic_u8string() const +# endif + { +# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__cpp_char8_t) + return string_converter::from( + this->base::generic_wstring()); +# else + return this->base::generic_u8string(); +# endif + } + + // Generation + // ========== + path lexically_normal() const { return this->base::lexically_normal(); } + + path lexically_relative(path const& root) const + { + return this->base::lexically_relative(root); + } + + path lexically_proximate(path const& root) const + { + return this->base::lexically_proximate(root); + } + + // Decomposition + // ============= + path root_name() const { return this->base::root_name(); } + + path root_directory() const { return this->base::root_directory(); } + + path root_path() const { return this->base::root_path(); } + + path relative_path() const { return this->base::relative_path(); } + + path parent_path() const { return this->base::parent_path(); } + + path filename() const { return this->base::filename(); } + + path stem() const { return this->base::stem(); } + path extension() const { return this->base::extension(); } + + // Iterators + // ========= + inline iterator begin() const; + inline iterator end() const; + + // Non-members + // =========== + friend inline path operator/(path const& lhs, path const& rhs) + { + path result{ lhs }; + result /= rhs; + + return result; + } + +private: + friend class iterator; + path(base&& p) + : base(std::move(p)) + { + } + mutable string_type native_path_; +}; + +class path::iterator +{ +public: + using iterator_category = std::filesystem::path::iterator::iterator_category; + + using value_type = path; + using difference_type = std::filesystem::path::iterator::difference_type; + using pointer = path const*; + using reference = path const&; + + iterator() = default; + iterator(iterator const& other) + : iterator_(other.iterator_) + { + } + + ~iterator() = default; + + iterator& operator=(iterator const& other) + { + this->iterator_ = other.iterator_; + return *this; + } + + reference operator*() const + { + this->path_element_ = path{ *this->iterator_ }; + return this->path_element_; + } + + pointer operator->() const + { + this->path_element_ = path{ *this->iterator_ }; + return &this->path_element_; + } + + iterator& operator++() + { + this->iterator_++; + return *this; + } + + iterator operator++(int) + { + iterator it(*this); + this->operator++(); + return *this; + } + + iterator& operator--() + { + this->iterator_--; + return *this; + } + + iterator operator--(int) + { + iterator it(*this); + this->operator--(); + return it; + } + +private: + friend class path; + friend bool operator==(iterator const&, iterator const&); + + iterator(std::filesystem::path::iterator it) + : iterator_(it) + { + } + + std::filesystem::path::iterator iterator_; + mutable path path_element_; +}; + +inline path::iterator path::begin() const +{ + return iterator{ this->base::begin() }; +} +inline path::iterator path::end() const +{ + return iterator{ this->base::end() }; +} + +// Non-member functions +// ==================== +inline bool operator==(path::iterator const& lhs, path::iterator const& rhs) +{ + return lhs.iterator_ == rhs.iterator_; +} + +inline bool operator!=(path::iterator const& lhs, path::iterator const& rhs) +{ + return !(lhs == rhs); +} + +inline void swap(path& lhs, path& rhs) noexcept +{ + lhs.swap(rhs); +} + +inline std::size_t hash_value(path const& p) noexcept +{ + return std::filesystem::hash_value(p); +} + +#else + +namespace internals { + +class path_parser; + } class path { +protected: using path_type = std::string; template @@ -664,7 +1536,7 @@ public: path_type::value_type>::append_source(this->path_, source); } template > - path(Iterator const first, Iterator last, format fmt = auto_format) + path(Iterator first, Iterator last, format fmt = auto_format) { (void)fmt; internals::source_converter< @@ -690,7 +1562,10 @@ public: } return *this; } - path& operator=(string_type&& source) { return this->assign(source); } + path& operator=(string_type&& source) + { + return this->assign(std::move(source)); + } template > path& operator=(Source const& source) { @@ -873,13 +1748,13 @@ public: Alloc>( this->path_, a); } - std::string const string() const { return this->path_; } + std::string string() const { return this->path_; } std::wstring wstring() const { - std::string out = this->string(); return internals::string_converter::to< - std::wstring::value_type>(out); + std::wstring::value_type>(this->string()); } + std::string u8string() const { return this->path_; } template < typename Char, typename Traits = std::char_traits, @@ -899,10 +1774,10 @@ public: std::string generic_string() const { return this->get_generic(); } std::wstring generic_wstring() const { - auto dest = this->generic_string(); return internals::string_converter::to< - std::wstring::value_type>(dest); + std::wstring::value_type>(this->generic_string()); } + std::string generic_u8string() const { return this->get_generic(); } // Compare // =======