diff options
author | B Stack <bgstack15@gmail.com> | 2019-11-20 08:36:44 -0500 |
---|---|---|
committer | B Stack <bgstack15@gmail.com> | 2019-11-20 08:36:44 -0500 |
commit | ed50041589974d31296cb30dc1897f7fba6336c2 (patch) | |
tree | e2c5c7b1f98e64011b1ee8ca4e9bb9157510dfe7 /zen | |
parent | Merge branch '10.17' into 'master' (diff) | |
download | FreeFileSync-ed50041589974d31296cb30dc1897f7fba6336c2.tar.gz FreeFileSync-ed50041589974d31296cb30dc1897f7fba6336c2.tar.bz2 FreeFileSync-ed50041589974d31296cb30dc1897f7fba6336c2.zip |
add upstream 10.18
Diffstat (limited to 'zen')
-rw-r--r-- | zen/dir_watcher.cpp | 2 | ||||
-rw-r--r-- | zen/dir_watcher.h | 2 | ||||
-rw-r--r-- | zen/error_log.h | 1 | ||||
-rw-r--r-- | zen/file_error.h | 6 | ||||
-rw-r--r-- | zen/format_unit.cpp | 2 | ||||
-rw-r--r-- | zen/globals.h | 104 | ||||
-rw-r--r-- | zen/http.cpp | 2 | ||||
-rw-r--r-- | zen/legacy_compiler.h | 20 | ||||
-rw-r--r-- | zen/open_ssl.cpp | 2 | ||||
-rw-r--r-- | zen/process_priority.cpp | 4 | ||||
-rw-r--r-- | zen/scope_guard.h | 36 | ||||
-rw-r--r-- | zen/stl_tools.h | 70 | ||||
-rw-r--r-- | zen/string_base.h | 52 | ||||
-rw-r--r-- | zen/string_tools.h | 17 | ||||
-rw-r--r-- | zen/string_traits.h | 18 | ||||
-rw-r--r-- | zen/sys_error.h | 5 | ||||
-rw-r--r-- | zen/type_traits.h | 18 |
17 files changed, 228 insertions, 133 deletions
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index f5ed0488..94632ea4 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -101,7 +101,7 @@ DirWatcher::~DirWatcher() } -std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>& requestUiRefresh, std::chrono::milliseconds cbInterval) //throw FileError +std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>& requestUiUpdate, std::chrono::milliseconds cbInterval) //throw FileError { std::vector<std::byte> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1)); diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h index 875a0098..4d514e89 100644 --- a/zen/dir_watcher.h +++ b/zen/dir_watcher.h @@ -56,7 +56,7 @@ public: }; //extract accumulated changes since last call - std::vector<Entry> getChanges(const std::function<void()>& requestUiRefresh, std::chrono::milliseconds cbInterval); //throw FileError + std::vector<Entry> getChanges(const std::function<void()>& requestUiUpdate, std::chrono::milliseconds cbInterval); //throw FileError private: DirWatcher (const DirWatcher&) = delete; diff --git a/zen/error_log.h b/zen/error_log.h index 4a3f5f2c..5115e6ef 100644 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -60,7 +60,6 @@ private: - //######################## implementation ########################## inline void ErrorLog::logMsg(const std::wstring& msg, MessageType type) diff --git a/zen/file_error.h b/zen/file_error.h index e47fb9f4..55935eba 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -30,9 +30,9 @@ private: #define DEFINE_NEW_FILE_ERROR(X) struct X : public zen::FileError { X(const std::wstring& msg) : FileError(msg) {} X(const std::wstring& msg, const std::wstring& descr) : FileError(msg, descr) {} }; -DEFINE_NEW_FILE_ERROR(ErrorTargetExisting); -DEFINE_NEW_FILE_ERROR(ErrorFileLocked); -DEFINE_NEW_FILE_ERROR(ErrorMoveUnsupported); +DEFINE_NEW_FILE_ERROR(ErrorTargetExisting) +DEFINE_NEW_FILE_ERROR(ErrorFileLocked) +DEFINE_NEW_FILE_ERROR(ErrorMoveUnsupported) //CAVEAT: thread-local Win32 error code is easily overwritten => evaluate *before* making any (indirect) system calls: diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 3e75278b..f2df4153 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -117,7 +117,7 @@ std::wstring roundToBlock(double timeInHigh, if (unitLowPerHigh > blockSizeLow) output += L" " + formatUnitTime(roundedtimeInLow % unitLowPerHigh, unitLow); return output; -}; +} } diff --git a/zen/globals.h b/zen/globals.h index e0bfd362..e59d64c1 100644 --- a/zen/globals.h +++ b/zen/globals.h @@ -23,6 +23,20 @@ Solve static destruction order fiasco by providing shared ownership and serializ ATTENTION: function-static globals have the compiler generate "magic statics" == compiler-genenerated locking code which will crash or leak memory when accessed after global is "dead" => "solved" by FunStatGlobal, but we can't have "too many" of these... */ + +class PodSpinMutex +{ +public: + bool tryLock(); + void lock(); + void unlock(); + bool isLocked(); + +private: + std::atomic_flag flag_; //= ATOMIC_FLAG_INIT; rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction! +}; + + template <class T> class Global //don't use for function-scope statics! { @@ -30,7 +44,8 @@ public: Global() { static_assert(std::is_trivially_constructible_v<Pod>&& std::is_trivially_destructible_v<Pod>, "this memory needs to live forever"); - assert(!pod_.inst && !pod_.spinLock); //we depend on static zero-initialization! + assert(!pod_.spinLock.isLocked()); //we depend on static zero-initialization! + assert(!pod_.inst); // } explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); } @@ -39,8 +54,9 @@ public: std::shared_ptr<T> get() //=> return std::shared_ptr to let instance life time be handled by caller (MT usage!) { - while (pod_.spinLock.exchange(true)) ; - ZEN_ON_SCOPE_EXIT(pod_.spinLock = false); + pod_.spinLock.lock(); + ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); + if (pod_.inst) return *pod_.inst; return nullptr; @@ -52,8 +68,9 @@ public: if (newInst) tmpInst = new std::shared_ptr<T>(std::move(newInst)); { - while (pod_.spinLock.exchange(true)) ; - ZEN_ON_SCOPE_EXIT(pod_.spinLock = false); + pod_.spinLock.lock(); + ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); + std::swap(pod_.inst, tmpInst); } delete tmpInst; @@ -62,9 +79,9 @@ public: private: struct Pod { - std::atomic<bool> spinLock; // { false }; rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction! + PodSpinMutex spinLock; //rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction! //serialize access; can't use std::mutex: has non-trival destructor - std::shared_ptr<T>* inst; // = nullptr; + std::shared_ptr<T>* inst; //= nullptr; } pod_; }; @@ -92,8 +109,9 @@ public: static_assert(std::is_trivially_constructible_v<FunStatGlobal>&& std::is_trivially_destructible_v<FunStatGlobal>, "this class must not generate code for magic statics!"); - while (pod_.spinLock.exchange(true)) ; - ZEN_ON_SCOPE_EXIT(pod_.spinLock = false); + pod_.spinLock.lock(); + ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); + if (pod_.inst) return *pod_.inst; return nullptr; @@ -105,8 +123,8 @@ public: if (newInst) tmpInst = new std::shared_ptr<T>(std::move(newInst)); { - while (pod_.spinLock.exchange(true)) ; - ZEN_ON_SCOPE_EXIT(pod_.spinLock = false); + pod_.spinLock.lock(); + ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); std::swap(pod_.inst, tmpInst); registerDestruction(); @@ -116,8 +134,8 @@ public: void initOnce(std::unique_ptr<T> (*getInitialValue)()) { - while (pod_.spinLock.exchange(true)) ; - ZEN_ON_SCOPE_EXIT(pod_.spinLock = false); + pod_.spinLock.lock(); + ZEN_ON_SCOPE_EXIT(pod_.spinLock.unlock()); if (!pod_.cleanUpEntry.cleanUpFun) { @@ -132,7 +150,7 @@ private: //call while holding pod_.spinLock void registerDestruction() { - assert(pod_.spinLock); + assert(pod_.spinLock.isLocked()); if (!pod_.cleanUpEntry.cleanUpFun) { @@ -149,7 +167,7 @@ private: struct Pod { - std::atomic<bool> spinLock; // { false }; rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction! + PodSpinMutex spinLock; //rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction! //serialize access; can't use std::mutex: has non-trival destructor std::shared_ptr<T>* inst; // = nullptr; CleanUpEntry cleanUpEntry; @@ -162,20 +180,20 @@ void registerGlobalForDestruction(CleanUpEntry& entry) { static struct { - std::atomic<bool> spinLock; - CleanUpEntry* head; + PodSpinMutex spinLock; + CleanUpEntry* head; } cleanUpList; static_assert(std::is_trivially_constructible_v<decltype(cleanUpList)>&& std::is_trivially_destructible_v<decltype(cleanUpList)>, "we must not generate code for magic statics!"); - while (cleanUpList.spinLock.exchange(true)) ; - ZEN_ON_SCOPE_EXIT(cleanUpList.spinLock = false); + cleanUpList.spinLock.lock(); + ZEN_ON_SCOPE_EXIT(cleanUpList.spinLock.unlock()); std::atexit([] { - while (cleanUpList.spinLock.exchange(true)) ; - ZEN_ON_SCOPE_EXIT(cleanUpList.spinLock = false); + cleanUpList.spinLock.lock(); + ZEN_ON_SCOPE_EXIT(cleanUpList.spinLock.unlock()); (*cleanUpList.head->cleanUpFun)(cleanUpList.head->callbackData); cleanUpList.head = cleanUpList.head->prev; //nicely clean up in reverse order of construction @@ -185,6 +203,50 @@ void registerGlobalForDestruction(CleanUpEntry& entry) cleanUpList.head = &entry; } + +//------------------------------------------------------------------------------------------ +#if __cpp_lib_atomic_wait +#error implement + rewiew improvements +#endif + + +inline +bool PodSpinMutex::tryLock() +{ + return !flag_.test_and_set(std::memory_order_acquire); +} + + +inline +void PodSpinMutex::lock() +{ + while (!tryLock()) +#if __cpp_lib_atomic_wait + flag_.wait(true, std::memory_order_relaxed); +#else + ; +#endif +} + + +inline +void PodSpinMutex::unlock() +{ + flag_.clear(std::memory_order_release); +#if __cpp_lib_atomic_wait + flag_.notify_one(); +#endif +} + + +inline +bool PodSpinMutex::isLocked() +{ + if (!tryLock()) + return true; + unlock(); + return false; +} } #endif //GLOBALS_H_8013740213748021573485 diff --git a/zen/http.cpp b/zen/http.cpp index 93651d0b..4f2c5205 100644 --- a/zen/http.cpp +++ b/zen/http.cpp @@ -12,6 +12,8 @@ using namespace zen; + + class HttpInputStream::Impl { public: diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h index d0b4d3fe..6ce1d765 100644 --- a/zen/legacy_compiler.h +++ b/zen/legacy_compiler.h @@ -8,19 +8,25 @@ #define LEGACY_COMPILER_H_839567308565656789 +#if !__cpp_lib_erase_if +#include <vector> +#include <set> +#include <map> +#endif + +//https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations +//https://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros +//https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html namespace std { -//https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html -//https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations - +//--------------------------------------------------------------------------------- #if __cpp_lib_span - #error get rid of workarounds + #error get rid of workaround: #endif -//requires C++20! until then, this should suffice... template <class T> class span { @@ -51,8 +57,12 @@ private: const size_t size_; T* const data_; }; + +//--------------------------------------------------------------------------------- + } + namespace zen { double from_chars(const char* first, const char* last); diff --git a/zen/open_ssl.cpp b/zen/open_ssl.cpp index ce05de53..dc9c8a19 100644 --- a/zen/open_ssl.cpp +++ b/zen/open_ssl.cpp @@ -28,7 +28,7 @@ void zen::openSslInit() //see Curl_ossl_cleanup(): https://github.com/curl/curl/blob/master/lib/vtls/openssl.c //excplicitly init OpenSSL on main thread: seems to initialize atomically! But it still might help to avoid issues: - [[maybe_unused]] const int rv = ::OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, nullptr); + [[maybe_unused]] const int rv = ::OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT | OPENSSL_INIT_NO_LOAD_CONFIG, nullptr); assert(rv == 1); //https://www.openssl.org/docs/man1.1.0/ssl/OPENSSL_init_ssl.html } diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp index f80dd022..5aa9a0ce 100644 --- a/zen/process_priority.cpp +++ b/zen/process_priority.cpp @@ -18,8 +18,8 @@ PreventStandby::~PreventStandby() {} //solution for GNOME?: http://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Inhibit struct ScheduleForBackgroundProcessing::Impl {}; -ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() {}; -ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() {}; +ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() {} +ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() {} /* struct ScheduleForBackgroundProcessing diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 3a79d841..5d3ac411 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -18,7 +18,7 @@ namespace zen { //Scope Guard /* - auto guardAio = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { ::CloseHandle(hDir); }); + auto guardAio = zen::makeGuard<ScopeGuardRunMode::onExit>([&] { ::CloseHandle(hDir); }); ... guardAio.dismiss(); @@ -30,34 +30,35 @@ Scope Exit: enum class ScopeGuardRunMode { - ON_EXIT, - ON_SUCCESS, - ON_FAIL + onExit, + onSuccess, + onFail }; //partially specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant" template <typename F> inline -void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>) noexcept +void runScopeGuardDestructor(F& fun, bool failed, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::onExit>) { - try { fun(); } - catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"! + if (!failed) + fun(); //throw X + else + try { fun(); } + catch (...) { assert(false); } } template <typename F> inline -void runScopeGuardDestructor(F& fun, int exeptionCountOld, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>) +void runScopeGuardDestructor(F& fun, bool failed, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::onSuccess>) { - const bool failed = std::uncaught_exceptions() > exeptionCountOld; if (!failed) fun(); //throw X } template <typename F> inline -void runScopeGuardDestructor(F& fun, int exeptionCountOld, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>) noexcept +void runScopeGuardDestructor(F& fun, bool failed, std::integral_constant<ScopeGuardRunMode, ScopeGuardRunMode::onFail>) noexcept { - const bool failed = std::uncaught_exceptions() > exeptionCountOld; if (failed) try { fun(); } catch (...) { assert(false); } @@ -75,10 +76,13 @@ public: exeptionCount_(other.exeptionCount_), dismissed_(other.dismissed_) { other.dismissed_ = true; } - ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS) + ~ScopeGuard() noexcept(runMode == ScopeGuardRunMode::onFail) { if (!dismissed_) - runScopeGuardDestructor(fun_, exeptionCount_, std::integral_constant<ScopeGuardRunMode, runMode>()); + { + const bool failed = std::uncaught_exceptions() > exeptionCount_; + runScopeGuardDestructor(fun_, failed, std::integral_constant<ScopeGuardRunMode, runMode>()); + } } void dismiss() { dismissed_ = true; } @@ -104,8 +108,8 @@ auto makeGuard(F&& fun) { return ScopeGuard<runMode, std::decay_t<F>>(std::forwa #define ZEN_CHECK_CASE_FOR_CONSTANT_IMPL(X) L ## X -#define ZEN_ON_SCOPE_EXIT(X) [[maybe_unused]] auto ZEN_CONCAT(scopeGuard, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_EXIT >([&]{ X; }); -#define ZEN_ON_SCOPE_FAIL(X) [[maybe_unused]] auto ZEN_CONCAT(scopeGuard, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_FAIL >([&]{ X; }); -#define ZEN_ON_SCOPE_SUCCESS(X) [[maybe_unused]] auto ZEN_CONCAT(scopeGuard, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_SUCCESS>([&]{ X; }); +#define ZEN_ON_SCOPE_EXIT(X) [[maybe_unused]] auto ZEN_CONCAT(scopeGuard, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::onExit >([&]{ X; }); +#define ZEN_ON_SCOPE_FAIL(X) [[maybe_unused]] auto ZEN_CONCAT(scopeGuard, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::onFail >([&]{ X; }); +#define ZEN_ON_SCOPE_SUCCESS(X) [[maybe_unused]] auto ZEN_CONCAT(scopeGuard, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::onSuccess>([&]{ X; }); #endif //SCOPE_GUARD_H_8971632487321434 diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 9014b0f7..d09010ad 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -20,16 +20,6 @@ //enhancements for <algorithm> namespace zen { -//erase selected elements from any container: -template <class T, class Alloc, class Predicate> -void eraseIf(std::vector<T, Alloc>& v, Predicate p); - -template <class T, class LessType, class Alloc, class Predicate> -void eraseIf(std::set<T, LessType, Alloc>& s, Predicate p); - -template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate> -void eraseIf(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p); - //append STL containers template <class T, class Alloc, class C> void append(std::vector<T, Alloc>& v, const C& c); @@ -46,10 +36,7 @@ void removeDuplicates(std::vector<T, Alloc>& v); template <class T, class Alloc, class CompLess> void removeDuplicates(std::vector<T, Alloc>& v, CompLess less); -//binary search returning an iterator -template <class Iterator, class T, class CompLess> -Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less); - +//searching STL containers template <class BidirectionalIterator, class T> BidirectionalIterator findLast(BidirectionalIterator first, BidirectionalIterator last, const T& value); @@ -58,6 +45,9 @@ template <class BidirectionalIterator1, class BidirectionalIterator2> BidirectionalIterator1 searchLast(BidirectionalIterator1 first1, BidirectionalIterator1 last1, BidirectionalIterator2 first2, BidirectionalIterator2 last2); +//binary search returning an iterator +template <class Iterator, class T, class CompLess> +Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less); //read-only variant of std::merge; input: two sorted ranges template <class Iterator, class FunctionLeftOnly, class FunctionBoth, class FunctionRightOnly> @@ -96,7 +86,9 @@ template <class T> class SharedRef //why is there no std::shared_ref??? { public: - SharedRef() = delete; //no suprise memory allocations => always construct with makeSharedRef() + SharedRef() = delete; //no surprise memory allocations! + + explicit SharedRef(std::shared_ptr<T> ptr) : ref_(std::move(ptr)) { assert(ref_); } template <class U> SharedRef(const SharedRef<U>& other) : ref_(other.ref_) {} @@ -104,12 +96,10 @@ public: /**/ T& ref() { return *ref_; }; const T& ref() const { return *ref_; }; - std::shared_ptr<T> ptr() { return ref_; }; + std::shared_ptr< T> ptr() { return ref_; }; + std::shared_ptr<const T> ptr() const { return ref_; }; private: - explicit SharedRef(std::shared_ptr<T>&& ptr) : ref_(std::move(ptr)) { assert(ref_); } - - template <class U, class... Args> friend SharedRef<U> makeSharedRef(Args&& ... args); template <class U> friend class SharedRef; std::shared_ptr<T> ref_; //always bound @@ -117,6 +107,7 @@ private: template <class T, class... Args> inline SharedRef<T> makeSharedRef(Args&& ... args) { return SharedRef<T>(std::make_shared<T>(std::forward<Args>(args)...)); } + //=========================================================================== @@ -124,36 +115,6 @@ SharedRef<T> makeSharedRef(Args&& ... args) { return SharedRef<T>(std::make_shar //######################## implementation ######################## - -template <class T, class Alloc, class Predicate> inline -void eraseIf(std::vector<T, Alloc>& v, Predicate p) -{ - v.erase(std::remove_if(v.begin(), v.end(), p), v.end()); -} - - -namespace impl -{ -template <class S, class Predicate> inline -void setOrMapEraseIf(S& s, Predicate p) -{ - for (auto it = s.begin(); it != s.end();) - if (p(*it)) - s.erase(it++); - else - ++it; -} -} - - -template <class T, class LessType, class Alloc, class Predicate> inline -void eraseIf(std::set<T, LessType, Alloc>& s, Predicate p) { impl::setOrMapEraseIf(s, p); } //don't make this any more generic! e.g. must not compile for std::vector!!! - - -template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate> inline -void eraseIf(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p) { impl::setOrMapEraseIf(m, p); } - - template <class T, class Alloc, class C> inline void append(std::vector<T, Alloc>& v, const C& c) { v.insert(v.end(), c.begin(), c.end()); } @@ -242,6 +203,17 @@ BidirectionalIterator1 searchLast(const BidirectionalIterator1 first1, Bid } +//--------------------------------------------------------------------------------------- +//http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0458r2.html + +template <class Container, class ValueType, typename = std::enable_if_t<!IsStringLikeV<Container>>> inline +bool contains(const Container& c, const ValueType& val, int dummy = 0 /*overload string_tools.h contains()*/) +{ + return c.find(val) != c.end(); +} +//--------------------------------------------------------------------------------------- + + //read-only variant of std::merge; input: two sorted ranges template <class Iterator, class FunctionLeftOnly, class FunctionBoth, class FunctionRightOnly> inline void mergeTraversal(Iterator first1, Iterator last1, diff --git a/zen/string_base.h b/zen/string_base.h index a417b7f6..6c835c72 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -12,6 +12,9 @@ #include <cstdint> #include <atomic> #include "string_tools.h" +#if __cpp_impl_three_way_comparison && __cpp_lib_three_way_comparison + #include <compare> +#endif //Zbase - a policy based string class optimizing performance and flexibility @@ -189,11 +192,10 @@ private: length (static_cast<uint32_t>(len)), capacity(static_cast<uint32_t>(cap)) { - //static_assert(ATOMIC_INT_LOCK_FREE == 2); //2: "The atomic type is always lock-free" - static_assert(decltype(refCount)::is_always_lock_free); //C++17 variant (not yet supported on GCC 6.3) + static_assert(decltype(refCount)::is_always_lock_free); } - std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default! + std::atomic<uint32_t> refCount { 1 }; //std:atomic is uninitialized by default! uint32_t length; const uint32_t capacity; //allocated size without null-termination }; @@ -289,10 +291,22 @@ private: Char* rawStr_; }; + +#if __cpp_impl_three_way_comparison && __cpp_lib_three_way_comparison + #error implement! +#endif + + template <class Char, template <class> class SP> bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs); template <class Char, template <class> class SP> bool operator==(const Zbase<Char, SP>& lhs, const Char* rhs); template <class Char, template <class> class SP> inline bool operator==(const Char* lhs, const Zbase<Char, SP>& rhs) { return operator==(rhs, lhs); } +#if __cpp_impl_three_way_comparison && __cpp_lib_three_way_comparison +template <class Char, template <class> class SP> std::strong_ordering operator<=>(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs); +template <class Char, template <class> class SP> std::strong_ordering operator<=>(const Zbase<Char, SP>& lhs, const Char* rhs); +template <class Char, template <class> class SP> std::strong_ordering operator<=>(const Char* lhs, const Zbase<Char, SP>& rhs); + +#else template <class Char, template <class> class SP> inline bool operator!=(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { return !operator==(lhs, rhs); } template <class Char, template <class> class SP> inline bool operator!=(const Zbase<Char, SP>& lhs, const Char* rhs) { return !operator==(lhs, rhs); } template <class Char, template <class> class SP> inline bool operator!=(const Char* lhs, const Zbase<Char, SP>& rhs) { return !operator==(lhs, rhs); } @@ -300,6 +314,7 @@ template <class Char, template <class> class SP> inline bool operator!=(const Ch template <class Char, template <class> class SP> bool operator<(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs); template <class Char, template <class> class SP> bool operator<(const Zbase<Char, SP>& lhs, const Char* rhs); template <class Char, template <class> class SP> bool operator<(const Char* lhs, const Zbase<Char, SP>& rhs); +#endif template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs) += rhs; } template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Char* rhs) { return Zbase<Char, SP>(lhs) += rhs; } @@ -313,11 +328,6 @@ template <class Char, template <class> class SP> inline Zbase<Char, SP> operator template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+( Char lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(&lhs, 1) += rhs; } template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Char* lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs ) += rhs; } -#if __cpp_impl_three_way_comparison -#error implement: -std::strong_ordering operator<=>(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) -bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs); -#endif @@ -476,6 +486,31 @@ bool operator==(const Zbase<Char, SP>& lhs, const Char* rhs) } +#if __cpp_impl_three_way_comparison && __cpp_lib_three_way_comparison +template <class Char, template <class> class SP> inline +std::strong_ordering operator<=>(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) +{ + return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), //respect embedded 0 + rhs.begin(), rhs.end()); +} + + +template <class Char, template <class> class SP> inline +std::strong_ordering operator<=>(const Zbase<Char, SP>& lhs, const Char* rhs) +{ + return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), //respect embedded 0 + rhs, rhs + strLength(rhs)); +} + + +template <class Char, template <class> class SP> inline +std::strong_ordering operator<=>(const Char* lhs, const Zbase<Char, SP>& rhs) +{ + return std::lexicographical_compare_three_way(lhs, lhs + strLength(lhs), //respect embedded 0 + rhs.begin(), rhs.end()); +} + +#else template <class Char, template <class> class SP> inline bool operator<(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { @@ -498,6 +533,7 @@ bool operator<(const Char* lhs, const Zbase<Char, SP>& rhs) return std::lexicographical_compare(lhs, lhs + strLength(lhs), //respect embedded 0 rhs.begin(), rhs.end()); } +#endif template <class Char, template <class> class SP> inline diff --git a/zen/string_tools.h b/zen/string_tools.h index dcb5a54a..7f9a07ff 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -32,7 +32,7 @@ template <class Char> Char asciiToLower(Char c); template <class Char> Char asciiToUpper(Char c); //both S and T can be strings or char/wchar_t arrays or single char/wchar_t -template <class S, class T> bool contains(const S& str, const T& term); +template <class S, class T, typename = std::enable_if_t<IsStringLikeV<S>>> bool contains(const S& str, const T& term); template <class S, class T> bool startsWith (const S& str, const T& prefix); template <class S, class T> bool startsWithAsciiNoCase(const S& str, const T& prefix); @@ -103,7 +103,6 @@ template <class T, class S> T copyStringTo(S&& str); - //---------------------- implementation ---------------------- template <class Char> inline bool isWhiteSpace(Char c) @@ -279,7 +278,7 @@ int compareAsciiNoCase(const S& lhs, const T& rhs) } -template <class S, class T> inline +template <class S, class T, typename> inline bool contains(const S& str, const T& term) { static_assert(std::is_same_v<GetCharTypeT<S>, GetCharTypeT<T>>); @@ -415,15 +414,15 @@ std::vector<S> split(const S& str, const T& delimiter, SplitType st) namespace impl { -ZEN_INIT_DETECT_MEMBER(append); +ZEN_INIT_DETECT_MEMBER(append) //either call operator+=(S(str, len)) or append(str, len) -template <class S, class InputIterator> inline -std::enable_if_t<HasMember_append<S>::value> stringAppend(S& str, InputIterator first, InputIterator last) { str.append(first, last); } +template <class S, class InputIterator, typename = std::enable_if_t<HasMemberV_append<S>>> inline +void stringAppend(S& str, InputIterator first, InputIterator last) { str.append(first, last); } //inefficient append: keep disabled until really needed -//template <class S, class InputIterator> inline -//std::enable_if_t<!HasMember_append<S>::value> stringAppend(S& str, InputIterator first, InputIterator last) { str += S(first, last); } +//template <class S, class InputIterator, typename = std::enable_if_t<!HasMemberV_append<S>>> inline +//void stringAppend(S& str, InputIterator first, InputIterator last) { str += S(first, last); } } @@ -850,6 +849,8 @@ char unhexify(char high, char low) }; return static_cast<unsigned char>(16 * unhexifyDigit(high) + unhexifyDigit(low)); //[!] convert to unsigned char first, then to char (which may be signed) } + + } #endif //STRING_TOOLS_H_213458973046 diff --git a/zen/string_traits.h b/zen/string_traits.h index d0f34d54..f1269130 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -92,14 +92,18 @@ template <> struct GetCharTypeImpl<std::basic_string_view<const char >, false> template <> struct GetCharTypeImpl<std::basic_string_view<const wchar_t>, false> { using Type = wchar_t; }; -ZEN_INIT_DETECT_MEMBER_TYPE(value_type); -ZEN_INIT_DETECT_MEMBER(c_str); //we don't know the exact declaration of the member attribute and it may be in a base class! -ZEN_INIT_DETECT_MEMBER(length); // +ZEN_INIT_DETECT_MEMBER_TYPE(value_type) +ZEN_INIT_DETECT_MEMBER(c_str) //we don't know the exact declaration of the member attribute and it may be in a base class! +ZEN_INIT_DETECT_MEMBER(length) // template <class S> class StringTraits { - using CleanType = std::remove_cv_t<std::remove_reference_t<S>>; //std::remove_cvref requires C++20 +#if __cpp_lib_remove_cvref + using CleanType = std::remove_cvref_t<S>; +#else + using CleanType = std::remove_cv_t<std::remove_reference_t<S>>; +#endif using NonArrayType = std::remove_extent_t <CleanType>; using NonPtrType = std::remove_pointer_t<NonArrayType>; using UndecoratedType = std::remove_cv_t <NonPtrType>; //handle "const char* const" @@ -107,9 +111,9 @@ class StringTraits public: enum { - isStringClass = HasMemberType_value_type<CleanType>::value && - HasMember_c_str <CleanType>::value && - HasMember_length <CleanType>::value + isStringClass = HasMemberTypeV_value_type<CleanType>&& + HasMemberV_c_str <CleanType>&& + HasMemberV_length <CleanType> }; using CharType = typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type; diff --git a/zen/sys_error.h b/zen/sys_error.h index 7c746258..f3b38250 100644 --- a/zen/sys_error.h +++ b/zen/sys_error.h @@ -85,10 +85,11 @@ std::wstring formatSystemError(const std::wstring& functionName, long long lastE inline std::wstring formatSystemError(const std::wstring& functionName, ErrorCode ec) { - //const std::wstring errorCode = printNumber<std::wstring>(L"0x%08x", static_cast<int>(ec)); + const std::wstring errorDescr = formatSystemErrorRaw(ec); const std::wstring errorCode = numberTo<std::wstring>(ec); + //const std::wstring errorCode = printNumber<std::wstring>(L"0x%08x", static_cast<int>(ec)); - return formatSystemError(functionName, replaceCpy(_("Error Code %x"), L"%x", errorCode), formatSystemErrorRaw(ec)); + return formatSystemError(functionName, replaceCpy(_("Error Code %x"), L"%x", errorCode), errorDescr); } diff --git a/zen/type_traits.h b/zen/type_traits.h index 9823bb44..03fbd768 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -68,19 +68,19 @@ template <class T> using IsArithmetic = std::bool_constant<IsInteger <T>::val /* Detect data or function members of a class by name: ZEN_INIT_DETECT_MEMBER + HasMember_ Example: 1. ZEN_INIT_DETECT_MEMBER(c_str); - 2. HasMember_c_str<T>::value -> use boolean + 2. HasMemberV_c_str<T> -> use boolean */ /* Detect data or function members of a class by name *and* type: ZEN_INIT_DETECT_MEMBER2 + HasMember_ Example: 1. ZEN_INIT_DETECT_MEMBER2(size, size_t (T::*)() const); - 2. HasMember_size<T>::value -> use as boolean + 2. HasMember_size<T>::value -> use as boolean */ /* Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_ Example: 1. ZEN_INIT_DETECT_MEMBER_TYPE(value_type); - 2. HasMemberType_value_type<T>::value -> use as boolean + 2. HasMemberTypeV_value_type<T> -> use as boolean */ //########## Sorting ############################## @@ -159,8 +159,7 @@ template <> struct IsSignedInt<long long int> : std::true_type {}; template<class T> \ struct HasMemberImpl_##NAME<false, T> : std::false_type {}; \ \ - template<typename T> \ - struct HasMember_##NAME : std::bool_constant<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {}; + template<class T> constexpr bool HasMemberV_##NAME = HasMemberImpl_##NAME<std::is_class_v<T>, T>::value; //#################################################################### @@ -178,7 +177,10 @@ template <> struct IsSignedInt<long long int> : std::true_type {}; template <class T> static No& hasMember(...); \ public: \ enum { value = sizeof(hasMember<U>(nullptr)) == sizeof(Yes) }; \ - }; + }; \ + \ + template<class T> constexpr bool HasMemberV_##NAME = HasMember_##NAME<T>::value; + //#################################################################### #define ZEN_INIT_DETECT_MEMBER_TYPE(TYPENAME) \ @@ -195,7 +197,9 @@ template <> struct IsSignedInt<long long int> : std::true_type {}; template <class U> static No& hasMemberType(...); \ public: \ enum { value = sizeof(hasMemberType<T>(nullptr)) == sizeof(Yes) }; \ - }; + }; \ + \ + template<class T> constexpr bool HasMemberTypeV_##TYPENAME = HasMemberType_##TYPENAME<T>::value; } #endif //TYPE_TRAITS_H_3425628658765467 |