summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <shieldwed@outlook.com>2019-12-14 15:52:53 +0000
committerDaniel Wilhelm <shieldwed@outlook.com>2019-12-14 15:52:53 +0000
commitcc75e50ca861529d50601d247fd66f806fcb23a8 (patch)
treee2c5c7b1f98e64011b1ee8ca4e9bb9157510dfe7 /zen
parentMerge branch '10.17' into 'master' (diff)
parentadd upstream 10.18 (diff)
downloadFreeFileSync-cc75e50ca861529d50601d247fd66f806fcb23a8.tar.gz
FreeFileSync-cc75e50ca861529d50601d247fd66f806fcb23a8.tar.bz2
FreeFileSync-cc75e50ca861529d50601d247fd66f806fcb23a8.zip
Merge branch '10.18' into 'master'10.18
add upstream 10.18 See merge request opensource-tracking/FreeFileSync!15
Diffstat (limited to 'zen')
-rw-r--r--zen/dir_watcher.cpp2
-rw-r--r--zen/dir_watcher.h2
-rw-r--r--zen/error_log.h1
-rw-r--r--zen/file_error.h6
-rw-r--r--zen/format_unit.cpp2
-rw-r--r--zen/globals.h104
-rw-r--r--zen/http.cpp2
-rw-r--r--zen/legacy_compiler.h20
-rw-r--r--zen/open_ssl.cpp2
-rw-r--r--zen/process_priority.cpp4
-rw-r--r--zen/scope_guard.h36
-rw-r--r--zen/stl_tools.h70
-rw-r--r--zen/string_base.h52
-rw-r--r--zen/string_tools.h17
-rw-r--r--zen/string_traits.h18
-rw-r--r--zen/sys_error.h5
-rw-r--r--zen/type_traits.h18
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
bgstack15