From 0fdab5eb69ca4b2e9048526b33948af72976c7bc Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Sat, 23 Jul 2016 03:10:56 +0000 Subject: [PATCH] Implement P0392r0. Integrate filesystem::path and string_view. llvm-svn: 276511 --- libcxx/include/experimental/filesystem | 64 ++++++++----- libcxx/src/experimental/filesystem/path.cpp | 91 ++++++++++--------- .../class.path/path.req/is_pathable.pass.cpp | 5 + .../path.member/path.append.pass.cpp | 28 ++++++ .../path.member/path.assign/source.pass.cpp | 27 ++++++ .../path.member/path.compare.pass.cpp | 4 +- .../path.member/path.concat.pass.cpp | 53 ++++++++++- .../path.construct/source.pass.cpp | 7 ++ libcxx/www/cxx1z_status.html | 2 +- 9 files changed, 211 insertions(+), 70 deletions(-) diff --git a/libcxx/include/experimental/filesystem b/libcxx/include/experimental/filesystem index 7de93fdf8f15..0075ad2b084a 100644 --- a/libcxx/include/experimental/filesystem +++ b/libcxx/include/experimental/filesystem @@ -228,7 +228,7 @@ #include #include #include // for quoted -#include +#include #include <__debug> @@ -498,6 +498,21 @@ struct __is_pathable_string, } }; + +template +struct __is_pathable_string, + _Void::__char_type>> +: public __can_convert_char<_ECharT> +{ + using _Str = basic_string_view<_ECharT, _Traits>; + using _Base = __can_convert_char<_ECharT>; + static _ECharT const* __range_begin(_Str const& __s) { return __s.data(); } + static _ECharT const* __range_end(_Str const& __s) { return __s.data() + __s.length(); } + static _ECharT __first_or_null(_Str const& __s) { + return __s.empty() ? _ECharT{} : __s[0]; + } +}; + template ::type, class _UnqualPtrType = typename remove_const< @@ -642,6 +657,7 @@ class _LIBCPP_TYPE_VIS path public: typedef char value_type; typedef basic_string string_type; + typedef _VSTD::string_view __string_view; static _LIBCPP_CONSTEXPR value_type preferred_separator = '/'; // constructors and destructor @@ -787,6 +803,12 @@ public: return *this; } + _LIBCPP_INLINE_VISIBILITY + path& operator+=(__string_view __x) { + __pn_ += __x; + return *this; + } + _LIBCPP_INLINE_VISIBILITY path& operator+=(const value_type* __x) { __pn_ += __x; @@ -799,7 +821,6 @@ public: return *this; } - template typename enable_if<__can_convert_char<_ECharT>::value, path&>::type operator+=(_ECharT __x) @@ -896,30 +917,31 @@ public: std::u32string generic_u32string() const { return string(); } private: - _LIBCPP_FUNC_VIS int __compare(const value_type*) const; - _LIBCPP_FUNC_VIS string_view __root_name() const; - _LIBCPP_FUNC_VIS string_view __root_directory() const; - _LIBCPP_FUNC_VIS string_view __relative_path() const; - _LIBCPP_FUNC_VIS string_view __parent_path() const; - _LIBCPP_FUNC_VIS string_view __filename() const; - _LIBCPP_FUNC_VIS string_view __stem() const; - _LIBCPP_FUNC_VIS string_view __extension() const; + _LIBCPP_FUNC_VIS int __compare(__string_view) const; + _LIBCPP_FUNC_VIS __string_view __root_name() const; + _LIBCPP_FUNC_VIS __string_view __root_directory() const; + _LIBCPP_FUNC_VIS __string_view __relative_path() const; + _LIBCPP_FUNC_VIS __string_view __parent_path() const; + _LIBCPP_FUNC_VIS __string_view __filename() const; + _LIBCPP_FUNC_VIS __string_view __stem() const; + _LIBCPP_FUNC_VIS __string_view __extension() const; public: // compare - _LIBCPP_INLINE_VISIBILITY int compare(const path& __p) const _NOEXCEPT { return __compare(__p.c_str());} - _LIBCPP_INLINE_VISIBILITY int compare(const string_type& __s) const { return __compare(__s.c_str()); } + _LIBCPP_INLINE_VISIBILITY int compare(const path& __p) const _NOEXCEPT { return __compare(__p.__pn_);} + _LIBCPP_INLINE_VISIBILITY int compare(const string_type& __s) const { return __compare(__s); } + _LIBCPP_INLINE_VISIBILITY int compare(__string_view __s) const { return __compare(__s); } _LIBCPP_INLINE_VISIBILITY int compare(const value_type* __s) const { return __compare(__s); } // decomposition - _LIBCPP_INLINE_VISIBILITY path root_name() const { return __root_name().to_string(); } - _LIBCPP_INLINE_VISIBILITY path root_directory() const { return __root_directory().to_string(); } - _LIBCPP_INLINE_VISIBILITY path root_path() const { return root_name().append(__root_directory().to_string()); } - _LIBCPP_INLINE_VISIBILITY path relative_path() const { return __relative_path().to_string(); } - _LIBCPP_INLINE_VISIBILITY path parent_path() const { return __parent_path().to_string(); } - _LIBCPP_INLINE_VISIBILITY path filename() const { return __filename().to_string(); } - _LIBCPP_INLINE_VISIBILITY path stem() const { return __stem().to_string();} - _LIBCPP_INLINE_VISIBILITY path extension() const { return __extension().to_string(); } + _LIBCPP_INLINE_VISIBILITY path root_name() const { return string_type(__root_name()); } + _LIBCPP_INLINE_VISIBILITY path root_directory() const { return string_type(__root_directory()); } + _LIBCPP_INLINE_VISIBILITY path root_path() const { return root_name().append(string_type(__root_directory())); } + _LIBCPP_INLINE_VISIBILITY path relative_path() const { return string_type(__relative_path()); } + _LIBCPP_INLINE_VISIBILITY path parent_path() const { return string_type(__parent_path()); } + _LIBCPP_INLINE_VISIBILITY path filename() const { return string_type(__filename()); } + _LIBCPP_INLINE_VISIBILITY path stem() const { return string_type(__stem());} + _LIBCPP_INLINE_VISIBILITY path extension() const { return string_type(__extension()); } // query _LIBCPP_INLINE_VISIBILITY bool empty() const _NOEXCEPT { return __pn_.empty(); } @@ -945,7 +967,7 @@ public: private: inline _LIBCPP_INLINE_VISIBILITY - path& __assign_view(string_view const& __s) noexcept { __pn_ = __s.to_string(); return *this; } + path& __assign_view(__string_view const& __s) noexcept { __pn_ = string_type(__s); return *this; } string_type __pn_; }; diff --git a/libcxx/src/experimental/filesystem/path.cpp b/libcxx/src/experimental/filesystem/path.cpp index 38c449832f6e..22ad23b42fe5 100644 --- a/libcxx/src/experimental/filesystem/path.cpp +++ b/libcxx/src/experimental/filesystem/path.cpp @@ -7,20 +7,21 @@ // //===----------------------------------------------------------------------===// #include "experimental/filesystem" -#include "experimental/string_view" +#include "string_view" #include "utility" _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM _LIBCPP_CONSTEXPR path::value_type path::preferred_separator; + +using string_view_t = path::__string_view; + namespace { namespace parser { -using string_type = string_view; using value_type = path::value_type; - -using string_view_pair = pair; +using string_view_pair = pair; // status reporting constexpr size_t npos = static_cast(-1); @@ -33,43 +34,43 @@ constexpr value_type const * preferred_separator_str = "/"; constexpr value_type const * dot = "."; // forward // -bool is_separator(string_type const &, size_t); -bool is_root_name(const string_type&, size_t); -bool is_root_directory(string_type const &, size_t); -bool is_trailing_separator(string_type const &, size_t); +bool is_separator(string_view_t const &, size_t); +bool is_root_name(const string_view_t&, size_t); +bool is_root_directory(string_view_t const &, size_t); +bool is_trailing_separator(string_view_t const &, size_t); -size_t start_of(string_type const &, size_t); -size_t end_of(string_type const &, size_t); +size_t start_of(string_view_t const &, size_t); +size_t end_of(string_view_t const &, size_t); -size_t root_name_start(const string_type& s); -size_t root_name_end(const string_type&); +size_t root_name_start(const string_view_t& s); +size_t root_name_end(const string_view_t&); -size_t root_directory_start(string_type const &); -size_t root_directory_end(string_type const &); +size_t root_directory_start(string_view_t const &); +size_t root_directory_end(string_view_t const &); -string_view_pair separate_filename(string_type const &); -string_view extract_raw(string_type const &, size_t); -string_view extract_preferred(string_type const &, size_t); +string_view_pair separate_filename(string_view_t const &); +string_view extract_raw(string_view_t const &, size_t); +string_view extract_preferred(string_view_t const &, size_t); -inline bool is_separator(const string_type& s, size_t pos) { +inline bool is_separator(const string_view_t& s, size_t pos) { return (pos < s.size() && s[pos] == preferred_separator); } -inline bool is_root_name(const string_type& s, size_t pos) { +inline bool is_root_name(const string_view_t& s, size_t pos) { return good(pos) && pos == 0 ? root_name_start(s) == pos : false; } -inline bool is_root_directory(const string_type& s, size_t pos) { +inline bool is_root_directory(const string_view_t& s, size_t pos) { return good(pos) ? root_directory_start(s) == pos : false; } -inline bool is_trailing_separator(const string_type& s, size_t pos) { +inline bool is_trailing_separator(const string_view_t& s, size_t pos) { return (pos < s.size() && is_separator(s, pos) && end_of(s, pos) == s.size()-1 && !is_root_directory(s, pos) && !is_root_name(s, pos)); } -size_t start_of(const string_type& s, size_t pos) { +size_t start_of(const string_view_t& s, size_t pos) { if (pos >= s.size()) return npos; bool in_sep = (s[pos] == preferred_separator); while (pos - 1 < s.size() && @@ -81,7 +82,7 @@ size_t start_of(const string_type& s, size_t pos) { return pos; } -size_t end_of(const string_type& s, size_t pos) { +size_t end_of(const string_view_t& s, size_t pos) { if (pos >= s.size()) return npos; // special case for root name if (pos == 0 && is_root_name(s, pos)) return root_name_end(s); @@ -91,11 +92,11 @@ size_t end_of(const string_type& s, size_t pos) { return pos; } -inline size_t root_name_start(const string_type& s) { +inline size_t root_name_start(const string_view_t& s) { return good(root_name_end(s)) ? 0 : npos; } -size_t root_name_end(const string_type& s) { +size_t root_name_end(const string_view_t& s) { if (s.size() < 2 || s[0] != preferred_separator || s[1] != preferred_separator) { return npos; @@ -113,14 +114,14 @@ size_t root_name_end(const string_type& s) { return index; } -size_t root_directory_start(const string_type& s) { +size_t root_directory_start(const string_view_t& s) { size_t e = root_name_end(s); if (!good(e)) return is_separator(s, 0) ? 0 : npos; return is_separator(s, e + 1) ? e + 1 : npos; } -size_t root_directory_end(const string_type& s) { +size_t root_directory_end(const string_view_t& s) { size_t st = root_directory_start(s); if (!good(st)) return npos; size_t index = st; @@ -129,20 +130,20 @@ size_t root_directory_end(const string_type& s) { return index; } -string_view_pair separate_filename(string_type const & s) { +string_view_pair separate_filename(string_view_t const & s) { if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""}; auto pos = s.find_last_of('.'); - if (pos == string_type::npos) return string_view_pair{s, string_view{}}; + if (pos == string_view_t::npos) return string_view_pair{s, string_view{}}; return string_view_pair{s.substr(0, pos), s.substr(pos)}; } -inline string_view extract_raw(const string_type& s, size_t pos) { +inline string_view extract_raw(const string_view_t& s, size_t pos) { size_t end_i = end_of(s, pos); if (!good(end_i)) return string_view{}; return string_view(s).substr(pos, end_i - pos + 1); } -string_view extract_preferred(const string_type& s, size_t pos) { +string_view extract_preferred(const string_view_t& s, size_t pos) { string_view raw = extract_raw(s, pos); if (raw.empty()) return raw; @@ -260,14 +261,14 @@ path & path::replace_extension(path const & replacement) /////////////////////////////////////////////////////////////////////////////// // path.decompose -string_view path::__root_name() const +string_view_t path::__root_name() const { return parser::is_root_name(__pn_, 0) ? parser::extract_preferred(__pn_, 0) - : string_view{}; + : string_view_t{}; } -string_view path::__root_directory() const +string_view_t path::__root_directory() const { auto start_i = parser::root_directory_start(__pn_); if(!parser::good(start_i)) { @@ -276,49 +277,49 @@ string_view path::__root_directory() const return parser::extract_preferred(__pn_, start_i); } -string_view path::__relative_path() const +string_view_t path::__relative_path() const { if (empty()) { - return {__pn_}; + return __pn_; } auto end_i = parser::root_directory_end(__pn_); if (not parser::good(end_i)) { end_i = parser::root_name_end(__pn_); } if (not parser::good(end_i)) { - return {__pn_}; + return __pn_; } - return string_view(__pn_).substr(end_i+1); + return string_view_t(__pn_).substr(end_i+1); } -string_view path::__parent_path() const +string_view_t path::__parent_path() const { if (empty() || pbegin(*this) == --pend(*this)) { return {}; } auto end_it = --(--pend(*this)); auto end_i = parser::end_of(__pn_, end_it.__pos_); - return string_view(__pn_).substr(0, end_i+1); + return string_view_t(__pn_).substr(0, end_i+1); } -string_view path::__filename() const +string_view_t path::__filename() const { - return empty() ? string_view{} : *--pend(*this); + return empty() ? string_view_t{} : *--pend(*this); } -string_view path::__stem() const +string_view_t path::__stem() const { return parser::separate_filename(__filename()).first; } -string_view path::__extension() const +string_view_t path::__extension() const { return parser::separate_filename(__filename()).second; } //////////////////////////////////////////////////////////////////////////// // path.comparisons -int path::__compare(const value_type* __s) const { +int path::__compare(string_view_t __s) const { path_view_iterator thisIter(this->native()); path_view_iterator sIter(__s); while (!thisIter.is_end() && !sIter.is_end()) { diff --git a/libcxx/test/libcxx/experimental/filesystem/class.path/path.req/is_pathable.pass.cpp b/libcxx/test/libcxx/experimental/filesystem/class.path/path.req/is_pathable.pass.cpp index 94de2108f8b5..61d322524071 100644 --- a/libcxx/test/libcxx/experimental/filesystem/class.path/path.req/is_pathable.pass.cpp +++ b/libcxx/test/libcxx/experimental/filesystem/class.path/path.req/is_pathable.pass.cpp @@ -28,6 +28,7 @@ #include "test_macros.h" #include "test_iterators.h" #include "min_allocator.h" +#include "constexpr_char_traits.hpp" namespace fs = std::experimental::filesystem; @@ -59,6 +60,8 @@ struct MakeTestType { using value_type = CharT; using string_type = std::basic_string; using string_type2 = std::basic_string, min_allocator>; + using string_view_type = std::basic_string_view; + using string_view_type2 = std::basic_string_view>; using cstr_type = CharT* const; using const_cstr_type = const CharT*; using array_type = CharT[25]; @@ -81,6 +84,8 @@ struct MakeTestType { static void Test() { AssertPathable(); AssertPathable(); + AssertPathable(); + AssertPathable(); AssertPathable(); AssertPathable(); AssertPathable(); diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp index 1118497e06a8..93892fbe10e4 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.append.pass.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include "test_macros.h" @@ -77,6 +78,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) using namespace fs; using Ptr = CharT const*; using Str = std::basic_string; + using StrView = std::basic_string_view; using InputIter = input_iterator; const Ptr L = TC.lhs; @@ -99,6 +101,16 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC) } assert(LHS == E); } + // basic_string_view + { + path LHS(L); PathReserve(LHS, ReserveSize); + StrView RHS(R); + { + DisableAllocationGuard g; + LHS /= RHS; + } + assert(LHS == E); + } // CharT* { path LHS(L); PathReserve(LHS, ReserveSize); @@ -153,6 +165,7 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC) using namespace fs; using Ptr = CharT const*; using Str = std::basic_string; + using StrView = std::basic_string_view; using InputIter = input_iterator; const Ptr L = TC.lhs; const Ptr R = TC.rhs; @@ -172,6 +185,21 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC) assert(LHS == E); assert(&Ref == &LHS); } + // basic_string_view + { + path LHS(L); + StrView RHS(R); + path& Ref = (LHS /= RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + { + path LHS(L); + StrView RHS(R); + path& Ref = LHS.append(RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } // Char* { path LHS(L); diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.assign/source.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.assign/source.pass.cpp index 4c2d5112d10b..ae725a88590e 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.assign/source.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.assign/source.pass.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include "test_macros.h" @@ -69,6 +70,32 @@ void RunTestCase(MultiStringType const& MS) { assert(p.string() == TestPath); assert(p.string() == S); } + // basic_string + { + const std::basic_string_view S(TestPath); + path p; PathReserve(p, S.length() + 1); + { + // string provides a contigious iterator. No allocation needed. + DisableAllocationGuard g; + path& pref = (p = S); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string() == TestPath); + assert(p.string() == S); + } + { + const std::basic_string_view S(TestPath); + path p; PathReserve(p, S.length() + 1); + { + DisableAllocationGuard g; + path& pref = p.assign(S); + assert(&pref == &p); + } + assert(p.native() == Expect); + assert(p.string() == TestPath); + assert(p.string() == S); + } ////////////////////////////////////////////////////////////////////////////// // Char* pointers { diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp index 557c1b24d88f..64f57f13fb25 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.compare.pass.cpp @@ -80,6 +80,7 @@ int main() const path p1(TC.LHS); const path p2(TC.RHS); const std::string R(TC.RHS); + const std::string_view RV(TC.RHS); const int E = TC.expect; { // compare(...) functions DisableAllocationGuard g; // none of these operations should allocate @@ -88,7 +89,8 @@ int main() int ret1 = p1.compare(p2); int ret2 = p1.compare(R); int ret3 = p1.compare(TC.RHS); - assert(ret1 == ret2 && ret1 == ret3); + int ret4 = p1.compare(RV); + assert(ret1 == ret2 && ret1 == ret3 && ret1 == ret4); int normalized_ret = ret1 < 0 ? -1 : (ret1 > 0 ? 1 : 0); assert(normalized_ret == E); diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.concat.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.concat.pass.cpp index 6e00afe0b49c..cd627b8c842f 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.concat.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.concat.pass.cpp @@ -14,8 +14,9 @@ // class path // path& operator+=(const path& x); -// path& operator+=(const string_type& x); // Implemented as Source template -// path& operator+=(const value_type* x); // Implemented as Source template +// path& operator+=(const string_type& x); +// path& operator+=(string_view x); +// path& operator+=(const value_type* x); // path& operator+=(value_type x); // template // path& operator+=(const Source& x); @@ -29,6 +30,8 @@ #include #include +#include +#include #include #include "test_macros.h" @@ -82,6 +85,7 @@ void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC) using namespace fs; using Ptr = CharT const*; using Str = std::basic_string; + using StrView = std::basic_string_view; using InputIter = input_iterator; const Ptr L = TC.lhs; @@ -98,6 +102,16 @@ void doConcatSourceAllocTest(ConcatOperatorTestcase const& TC) } assert(LHS == E); } + // basic_string_view + { + path LHS(L); PathReserve(LHS, ReserveSize); + StrView RHS(R); + { + DisableAllocationGuard g; + LHS += RHS; + } + assert(LHS == E); + } // CharT* { path LHS(L); PathReserve(LHS, ReserveSize); @@ -152,6 +166,7 @@ void doConcatSourceTest(ConcatOperatorTestcase const& TC) using namespace fs; using Ptr = CharT const*; using Str = std::basic_string; + using StrView = std::basic_string_view; using InputIter = input_iterator; const Ptr L = TC.lhs; const Ptr R = TC.rhs; @@ -171,6 +186,21 @@ void doConcatSourceTest(ConcatOperatorTestcase const& TC) assert(LHS == E); assert(&Ref == &LHS); } + // basic_string_view + { + path LHS(L); + StrView RHS(R); + path& Ref = (LHS += RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } + { + path LHS(L); + StrView RHS(R); + path& Ref = LHS.concat(RHS); + assert(LHS == E); + assert(&Ref == &LHS); + } // Char* { path LHS(L); @@ -246,6 +276,13 @@ int main() assert(LHS == (const char*)TC.expect); assert(&Ref == &LHS); } + { + path LHS((const char*)TC.lhs); + std::string_view RHS((const char*)TC.rhs); + path& Ref = (LHS += RHS); + assert(LHS == (const char*)TC.expect); + assert(&Ref == &LHS); + } doConcatSourceTest (TC); doConcatSourceTest (TC); doConcatSourceTest(TC); @@ -265,6 +302,18 @@ int main() } assert(LHS == E); } + { + path LHS((const char*)TC.lhs); + std::string_view RHS((const char*)TC.rhs); + const char* E = TC.expect; + PathReserve(LHS, StrLen(E) + 5); + { + DisableAllocationGuard g; + path& Ref = (LHS += RHS); + assert(&Ref == &LHS); + } + assert(LHS == E); + } doConcatSourceAllocTest(TC); doConcatSourceAllocTest(TC); } diff --git a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.construct/source.pass.cpp b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.construct/source.pass.cpp index d89e7c815efb..402225de11bc 100644 --- a/libcxx/test/std/experimental/filesystem/class.path/path.member/path.construct/source.pass.cpp +++ b/libcxx/test/std/experimental/filesystem/class.path/path.member/path.construct/source.pass.cpp @@ -47,6 +47,13 @@ void RunTestCase(MultiStringType const& MS) { assert(p.string() == TestPath); assert(p.string() == S); } + { + const std::basic_string_view S(TestPath); + path p(S); + assert(p.native() == Expect); + assert(p.string() == TestPath); + assert(p.string() == S); + } // Char* pointers { path p(TestPath); diff --git a/libcxx/www/cxx1z_status.html b/libcxx/www/cxx1z_status.html index 57a750599597..9ce2e00c7694 100644 --- a/libcxx/www/cxx1z_status.html +++ b/libcxx/www/cxx1z_status.html @@ -118,7 +118,7 @@ p0346r1LWGA <random> Nomenclature TweakOulu p0358r1LWGFixes for not_fnOuluComplete3.9 p0371r1LWGTemporarily discourage memory_order_consumeOulu - p0392r0LWGAdapting string_view by filesystem pathsOulu + p0392r0LWGAdapting string_view by filesystem pathsOuluComplete4.0 p0393r3LWGMaking Variant Greater EqualOulu P0394r4LWGHotel Parallelifornia: terminate() for Parallel Algorithms Exception HandlingOulu -- GitLab