diff options
Diffstat (limited to 'zen')
45 files changed, 716 insertions, 657 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h index 14fcae9c..8b745caf 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BASIC_MATH_HEADER_34726398432 -#define BASIC_MATH_HEADER_34726398432 +#ifndef BASIC_MATH_H_3472639843265675 +#define BASIC_MATH_H_3472639843265675 #include <algorithm> #include <iterator> @@ -14,6 +14,7 @@ #include <functional> #include <cassert> + namespace numeric { template <class T> @@ -26,10 +27,10 @@ template <class T> int sign(T value); //returns -1/0/1 template <class T> -const T& min(const T& a, const T& b, const T& c); +T min(T a, T b, T c); template <class T> -const T& max(const T& a, const T& b, const T& c); +T max(T a, T b, T c); template <class T> void clamp(T& val, const T& minVal, const T& maxVal); //make sure minVal <= val && val <= maxVal @@ -114,14 +115,14 @@ int sign(T value) //returns -1/0/1 template <class T> inline -const T& min(const T& a, const T& b, const T& c) +T min(T a, T b, T c) //don't follow std::min's "const T&(const T&, const T&)" API { return std::min(std::min(a, b), c); } template <class T> inline -const T& max(const T& a, const T& b, const T& c) +T max(T a, T b, T c) { return std::max(std::max(a, b), c); } @@ -396,4 +397,4 @@ double norm2(InputIterator first, InputIterator last) } } -#endif //BASIC_MATH_HEADER_34726398432 +#endif //BASIC_MATH_H_3472639843265675 diff --git a/zen/build_info.h b/zen/build_info.h index 7c738847..09e1c5a7 100644 --- a/zen/build_info.h +++ b/zen/build_info.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BUILDINFO_H_5928539285603428657 -#define BUILDINFO_H_5928539285603428657 +#ifndef BUILD_INFO_H_5928539285603428657 +#define BUILD_INFO_H_5928539285603428657 namespace zen { @@ -35,4 +35,4 @@ namespace zen #endif } -#endif //BUILDINFO_H_5928539285603428657 +#endif //BUILD_INFO_H_5928539285603428657 diff --git a/zen/deprecate.h b/zen/deprecate.h index 49d8386b..5cac14c3 100644 --- a/zen/deprecate.h +++ b/zen/deprecate.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DEPRECATE_HEADER_2348970348 -#define DEPRECATE_HEADER_2348970348 +#ifndef DEPRECATE_H_234897087787348 +#define DEPRECATE_H_234897087787348 //compiler macros: http://predef.sourceforge.net/precomp.html #ifdef __GNUC__ @@ -18,4 +18,4 @@ #error add your platform here! #endif -#endif //DEPRECATE_HEADER_2348970348 +#endif //DEPRECATE_H_234897087787348 diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 3bab8d34..97ecafe5 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -14,6 +14,7 @@ #include "device_notify.h" #include "win.h" //includes "windows.h" #include "long_path_prefix.h" + #include "optional.h" #elif defined ZEN_LINUX #include <map> @@ -119,9 +120,7 @@ public: void reportError(const std::wstring& msg, const std::wstring& description, DWORD errorCode) //throw() { std::lock_guard<std::mutex> dummy(lockAccess); - - ErrorInfo newInfo = { copyStringTo<BasicWString>(msg), copyStringTo<BasicWString>(description), errorCode }; - errorInfo = std::make_unique<ErrorInfo>(newInfo); + errorInfo = ErrorInfo({ copyStringTo<BasicWString>(msg), copyStringTo<BasicWString>(description), errorCode }); } private: @@ -136,7 +135,7 @@ private: BasicWString descr; DWORD errorCode; }; - std::unique_ptr<ErrorInfo> errorInfo; //non-empty if errors occurred in thread + Opt<ErrorInfo> errorInfo; //non-empty if errors occurred in thread }; @@ -147,8 +146,7 @@ public: ReadChangesAsync(const Zstring& directory, //make sure to not leak-in thread-unsafe types! const std::shared_ptr<SharedData>& shared) : shared_(shared), - dirpathPf(appendSeparator(directory)), - hDir(INVALID_HANDLE_VALUE) + dirpathPf(appendSeparator(directory)) { hDir = ::CreateFile(applyLongPathPrefix(dirpathPf).c_str(), //_In_ LPCTSTR lpFileName, FILE_LIST_DIRECTORY, //_In_ DWORD dwDesiredAccess, @@ -218,7 +216,7 @@ public: } //async I/O is a resource that needs to be guarded since it will write to local variable "buffer"! - zen::ScopeGuard guardAio = zen::makeGuard([&] + auto guardAio = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { //Canceling Pending I/O Operations: http://msdn.microsoft.com/en-us/library/aa363789(v=vs.85).aspx #ifdef ZEN_WIN_VISTA_AND_LATER @@ -265,7 +263,7 @@ private: std::shared_ptr<SharedData> shared_; //worker thread only: Zstring dirpathPf; //thread safe! - HANDLE hDir; + HANDLE hDir = INVALID_HANDLE_VALUE; }; @@ -277,11 +275,9 @@ public: InterruptibleThread& worker) : notificationHandle(registerFolderRemovalNotification(hDir, //throw FileError displayPath, - [this] { this->onRequestRemoval (); }, //noexcept! + [this]{ this->onRequestRemoval (); }, //noexcept! [this](bool successful) { this->onRemovalFinished(); })), // - worker_(worker), - removalRequested(false), - operationComplete(false) {} + worker_(worker) {} ~HandleVolumeRemoval() { @@ -311,8 +307,8 @@ private: DeviceNotificationHandle* notificationHandle; InterruptibleThread& worker_; - bool removalRequested; - bool operationComplete; + bool removalRequested = false; + bool operationComplete = false; }; } @@ -375,9 +371,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() #elif defined ZEN_LINUX struct DirWatcher::Pimpl { - Pimpl() : notifDescr() {} - - int notifDescr; + int notifDescr = 0; std::map<int, Zstring> watchDescrs; //watch descriptor and (sub-)directory name (postfixed with separator) -> owned by "notifDescr" }; @@ -387,14 +381,14 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError pimpl_(std::make_unique<Pimpl>()) { //get all subdirectories - std::vector<Zstring> fullDirList { baseDirPath }; + std::vector<Zstring> fullFolderList { baseDirPath }; { std::function<void (const Zstring& path)> traverse; - traverse = [&traverse, &fullDirList](const Zstring& path) + traverse = [&traverse, &fullFolderList](const Zstring& path) { traverseFolder(path, nullptr, - [&](const DirInfo& di ) { fullDirList.push_back(di.fullPath); traverse(di.fullPath); }, + [&](const DirInfo& di ) { fullFolderList.push_back(di.fullPath); traverse(di.fullPath); }, nullptr, //don't traverse into symlinks (analog to windows build) [&](const std::wstring& errorMsg) { throw FileError(errorMsg); }); }; @@ -407,7 +401,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError if (pimpl_->notifDescr == -1) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"inotify_init"); - zen::ScopeGuard guardDescr = zen::makeGuard([&] { ::close(pimpl_->notifDescr); }); + ZEN_ON_SCOPE_FAIL( ::close(pimpl_->notifDescr); ); //set non-blocking mode bool initSuccess = false; @@ -420,7 +414,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"fcntl"); //add watches - for (const Zstring& subDirPath : fullDirList) + for (const Zstring& subDirPath : fullFolderList) { int wd = ::inotify_add_watch(pimpl_->notifDescr, subDirPath.c_str(), IN_ONLYDIR | //"Only watch pathname if it is a directory." @@ -445,8 +439,6 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError pimpl_->watchDescrs.emplace(wd, appendSeparator(subDirPath)); } - - guardDescr.dismiss(); } @@ -557,8 +549,7 @@ void eventCallback(ConstFSEventStreamRef streamRef, struct DirWatcher::Pimpl { - Pimpl() : eventStream() {} - FSEventStreamRef eventStream; + FSEventStreamRef eventStream = nullptr; std::vector<DirWatcher::Entry> changedFiles; }; @@ -583,6 +574,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : FSEventStreamContext context = {}; context.info = &pimpl_->changedFiles; + //can this fail?? not documented! pimpl_->eventStream = ::FSEventStreamCreate(nullptr, //CFAllocatorRef allocator, &eventCallback, //FSEventStreamCallback callback, &context, //FSEventStreamContext* context, @@ -591,22 +583,16 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : 0, //CFTimeInterval latency, in seconds kFSEventStreamCreateFlagWatchRoot | kFSEventStreamCreateFlagFileEvents); //FSEventStreamCreateFlags flags - //can this fail?? not documented! - - zen::ScopeGuard guardCreate = zen::makeGuard([&] { ::FSEventStreamRelease(pimpl_->eventStream); }); + ZEN_ON_SCOPE_FAIL( ::FSEventStreamRelease(pimpl_->eventStream); ); + //no-fail: ::FSEventStreamScheduleWithRunLoop(pimpl_->eventStream, //FSEventStreamRef streamRef, ::CFRunLoopGetCurrent(), //CFRunLoopRef runLoop; CFRunLoopGetCurrent(): failure not documented! kCFRunLoopDefaultMode); //CFStringRef runLoopMode - //no-fail - - zen::ScopeGuard guardRunloop = zen::makeGuard([&] { ::FSEventStreamInvalidate(pimpl_->eventStream); }); + ZEN_ON_SCOPE_FAIL( ::FSEventStreamInvalidate(pimpl_->eventStream); ); if (!::FSEventStreamStart(pimpl_->eventStream)) throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"Function call failed: FSEventStreamStart"); //no error code documented! - - guardCreate .dismiss(); - guardRunloop.dismiss(); } diff --git a/zen/error_log.h b/zen/error_log.h index 81892a25..d282ea4b 100644 --- a/zen/error_log.h +++ b/zen/error_log.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ERRORLOGGING_H_INCLUDED -#define ERRORLOGGING_H_INCLUDED +#ifndef ERROR_LOG_H_8917590832147915 +#define ERROR_LOG_H_8917590832147915 #include <cassert> #include <algorithm> @@ -86,7 +86,7 @@ namespace template <class String> String formatMessageImpl(const LogEntry& entry) //internal linkage { - auto getTypeName = [&]() -> std::wstring + auto getTypeName = [&] { switch (entry.type) { @@ -132,4 +132,4 @@ template <class String> inline String formatMessage(const LogEntry& entry) { return formatMessageImpl<String>(entry); } } -#endif //ERRORLOGGING_H_INCLUDED +#endif //ERROR_LOG_H_8917590832147915 diff --git a/zen/file_access.cpp b/zen/file_access.cpp index e04673d3..2ca373aa 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -8,7 +8,6 @@ #include <map> #include <algorithm> #include <stdexcept> -#include "int64.h" #include "file_traverser.h" #include "scope_guard.h" #include "symlink_target.h" @@ -17,6 +16,7 @@ #ifdef ZEN_WIN #include <Aclapi.h> + #include "int64.h" #include "privilege.h" #include "long_path_prefix.h" #include "win_ver.h" @@ -24,7 +24,6 @@ #include <zen/vista_file_op.h> //requires COM initialization! #endif - #elif defined ZEN_LINUX #include <sys/vfs.h> //statfs #include <sys/time.h> //lutimes @@ -135,7 +134,7 @@ bool zen::somethingExists(const Zstring& itemPath) namespace { #ifdef ZEN_WIN -bool isFatDrive(const Zstring& filePath) //throw() +bool isFatDrive(const Zstring& filePath) //noexcept { const DWORD bufferSize = MAX_PATH + 1; std::vector<wchar_t> buffer(bufferSize); @@ -202,12 +201,17 @@ std::uint64_t zen::getFilesize(const Zstring& filePath) //throw FileError THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"CreateFile"); ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); - //why not use ::GetFileSizeEx() instead??? - BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; - if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) - THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileInformationByHandle"); + LARGE_INTEGER fileSize = {}; + if (!::GetFileSizeEx(hFile, //_In_ HANDLE hFile, + &fileSize)) //_Out_ PLARGE_INTEGER lpFileSize + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileSizeEx"); + return fileSize.QuadPart; - return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); + //alternative: + //BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; + //if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) + // THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileInformationByHandle"); + //return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); #elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat fileInfo = {}; @@ -312,53 +316,56 @@ void zen::removeDirectorySimple(const Zstring& dirPath) //throw FileError return; } #endif - throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(functionName, ec)); } - //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have - //successfully been *marked* for deletion, but some application still has a handle open! - //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 - //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html + /* + Windows: may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have + successfully been *marked* for deletion, but some application still has a handle open! + e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 + Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html + Alternatives: 1. move file/empty folder to some other location, then DeleteFile()/RemoveDirectory() + 2. use CreateFile/FILE_FLAG_DELETE_ON_CLOSE *without* FILE_SHARE_DELETE instead of DeleteFile() => early failure + */ } namespace { -void removeDirectoryImpl(const Zstring& dirPath) //throw FileError +void removeDirectoryImpl(const Zstring& folderPath) //throw FileError { - assert(dirExists(dirPath)); //[!] no symlinks in this context!!! - //attention: check if dirPath is a symlink! Do NOT traverse into it deleting contained files!!! + assert(dirExists(folderPath)); //[!] no symlinks in this context!!! + //attention: check if folderPath is a symlink! Do NOT traverse into it deleting contained files!!! - std::vector<Zstring> fileList; - std::vector<Zstring> dirLinkList; - std::vector<Zstring> dirList; + std::vector<Zstring> filePaths; + std::vector<Zstring> folderSymlinkPaths; + std::vector<Zstring> folderPaths; //get all files and directories from current directory (WITHOUT subdirectories!) - traverseFolder(dirPath, - [&](const FileInfo& fi) { fileList.push_back(fi.fullPath); }, - [&](const DirInfo& di) { dirList .push_back(di.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies! + traverseFolder(folderPath, + [&](const FileInfo& fi) { filePaths.push_back(fi.fullPath); }, + [&](const DirInfo& di) { folderPaths .push_back(di.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies! [&](const SymlinkInfo& si) { #ifdef ZEN_WIN if (dirExists(si.fullPath)) //dir symlink - dirLinkList.push_back(si.fullPath); + folderSymlinkPaths.push_back(si.fullPath); else //file symlink, broken symlink #endif - fileList.push_back(si.fullPath); + filePaths.push_back(si.fullPath); }, [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); - for (const Zstring& filePath : fileList) + for (const Zstring& filePath : filePaths) removeFile(filePath); //throw FileError - for (const Zstring& dirLinkPath : dirLinkList) - removeDirectorySimple(dirLinkPath); //throw FileError + for (const Zstring& symlinkPath : folderSymlinkPaths) + removeDirectorySimple(symlinkPath); //throw FileError //delete directories recursively - for (const Zstring& subDirPath : dirList) - removeDirectoryImpl(subDirPath); //throw FileError; call recursively to correctly handle symbolic links + for (const Zstring& subFolderPath : folderPaths) + removeDirectoryImpl(subFolderPath); //throw FileError; call recursively to correctly handle symbolic links - removeDirectorySimple(dirPath); //throw FileError + removeDirectorySimple(folderPath); //throw FileError } } @@ -442,7 +449,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro } #elif defined ZEN_LINUX || defined ZEN_MAC - //rename() will never fail with EEXIST, but always overwrite! + //rename() will never fail with EEXIST, but always (atomically) overwrite! => equivalent to SetFileInformationByHandle() + FILE_RENAME_INFO::ReplaceIfExists //=> Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy //=> OS X: no solution @@ -458,7 +465,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro throw FileError(errorMsg, errorDescr); }; - if (!EqualFilePath()(pathSource, pathTarget)) //OS X: changing file name case is not an "already exists" error! + if (!equalFilePath(pathSource, pathTarget)) //OS X: changing file name case is not an "already exists" error! if (somethingExists(pathTarget)) throwException(EEXIST); @@ -511,7 +518,7 @@ Zstring findUnused8Dot3Name(const Zstring& filePath) //find a unique 8.3 short n if (!somethingExists(output)) //ensure uniqueness return output; } - throw std::runtime_error(std::string("100000000 files, one for each number, exist in this directory? You're kidding...") + utfCvrtTo<std::string>(pathPrefix) + + throw std::runtime_error(std::string("100,000,000 files, one for each number, exist in this directory? You're kidding...") + utfCvrtTo<std::string>(pathPrefix) + "\n" + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); } @@ -529,8 +536,8 @@ bool have8dot3NameClash(const Zstring& filePath) if (!shortName.empty() && !longName .empty() && - EqualFilePath()(origName, shortName) && - !EqualFilePath()(shortName, longName)) + equalFilePath(origName, shortName) && + !equalFilePath(shortName, longName)) { //for filePath short and long file name are equal and another unrelated file happens to have the same short name //e.g. filePath == "TESTWE~1", but another file is existing named "TestWeb" with short name ""TESTWE~1" @@ -896,9 +903,9 @@ void setFileTimeRaw(const Zstring& filePath, const struct ::timespec& modTime, P #elif defined ZEN_MAC struct AttrBufFileTimes { - std::uint32_t length; - struct ::timespec createTime; //keep order; see docs! - struct ::timespec writeTime; // + std::uint32_t length = 0; + struct ::timespec createTime = {}; //keep order; see docs! + struct ::timespec writeTime = {}; // } __attribute__((aligned(4), packed)); @@ -915,7 +922,7 @@ void setFileTimeRaw(const Zstring& filePath, attribs.bitmapcount = ATTR_BIT_MAP_COUNT; attribs.commonattr = (createTime ? ATTR_CMN_CRTIME : 0) | ATTR_CMN_MODTIME; - AttrBufFileTimes newTimes = {}; + AttrBufFileTimes newTimes; if (createTime) { newTimes.createTime.tv_sec = createTime->tv_sec; @@ -944,7 +951,7 @@ void getFileTimeRaw(int fd, //throw FileError attribs.bitmapcount = ATTR_BIT_MAP_COUNT; attribs.commonattr = ATTR_CMN_CRTIME | ATTR_CMN_MODTIME; - AttrBufFileTimes fileTimes = {}; + AttrBufFileTimes fileTimes; const int rv = ::fgetattrlist(fd, //int fd, &attribs, //struct ::attrlist* attrList, @@ -1080,7 +1087,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli if (rv3 < 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtPath(target)), L"setfilecon"); } -#endif //HAVE_SELINUX +#endif //copy permissions for files, directories or symbolic links: requires admin rights @@ -1340,7 +1347,9 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions) { #ifdef ZEN_WIN - //special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS! +auto getErrorMsg = [](const Zstring& path){ return replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(path)); }; + +//special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS! Zstring dirTmp = removeLongPathPrefix(endsWith(targetPath, FILE_NAME_SEPARATOR) ? beforeLast(targetPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE) : targetPath); @@ -1349,16 +1358,19 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, { dirTmp += FILE_NAME_SEPARATOR; //we do not support "C:" to represent a relative path! - const ErrorCode lastError = somethingExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; //don't use dirExists() => harmonize with ErrorTargetExisting! - - const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(dirTmp)); - const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); + const DWORD ec = somethingExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; //don't use dirExists() => harmonize with ErrorTargetExisting! + const std::wstring errorDescr = formatSystemError(L"CreateDirectory", ec); - if (lastError == ERROR_ALREADY_EXISTS) - throw ErrorTargetExisting(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); //[!] this is NOT a ErrorTargetPathMissing case! + if (ec == ERROR_ALREADY_EXISTS) + throw ErrorTargetExisting(getErrorMsg(dirTmp), errorDescr); + throw FileError(getErrorMsg(dirTmp), errorDescr); //[!] this is NOT a ErrorTargetPathMissing case! } + //deliberately don't support creating irregular folders like "...." https://social.technet.microsoft.com/Forums/windows/en-US/ffee2322-bb6b-4fdf-86f9-8f93cf1fa6cb/ + if (endsWith(targetPath, L' ') || + endsWith(targetPath, L'.')) + throw FileError(getErrorMsg(targetPath), replaceCpy(_("%x is not a regular directory name."), L"%x", fmtPath(afterLast(targetPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)))); + //don't use ::CreateDirectoryEx: //- it may fail with "wrong parameter (error code 87)" when source is on mapped online storage //- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)! @@ -1383,14 +1395,13 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, if (ec != ERROR_SUCCESS) { - const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(targetPath)); const std::wstring errorDescr = formatSystemError(L"CreateDirectory", ec); if (ec == ERROR_ALREADY_EXISTS) - throw ErrorTargetExisting(errorMsg, errorDescr); + throw ErrorTargetExisting(getErrorMsg(targetPath), errorDescr); else if (ec == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); + throw ErrorTargetPathMissing(getErrorMsg(targetPath), errorDescr); + throw FileError(getErrorMsg(targetPath), errorDescr); } } @@ -1489,13 +1500,12 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, /*int rv =*/ ::copyfile(sourcePath.c_str(), targetPath.c_str(), 0, COPYFILE_XATTR); #endif - zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectorySimple(targetPath); } catch (FileError&) {} }); //ensure cleanup: + ZEN_ON_SCOPE_FAIL(try { removeDirectorySimple(targetPath); } + catch (FileError&) {}); //ensure cleanup: //enforce copying file permissions: it's advertized on GUI... if (copyFilePermissions) copyItemPermissions(sourcePath, targetPath, ProcSymlink::FOLLOW); //throw FileError - - guardNewDir.dismiss(); //target has been created successfully! } } @@ -1529,7 +1539,8 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), functionName); //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist! - zen::ScopeGuard guardNewLink = zen::makeGuard([&] + + auto cleanUp = [&] { try { @@ -1541,7 +1552,8 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool removeFile(targetLink); //throw FileError } catch (FileError&) {} - }); + }; + ZEN_ON_SCOPE_FAIL(cleanUp()); //file times: essential for sync'ing a symlink: enforce this! (don't just try!) #ifdef ZEN_WIN @@ -1573,8 +1585,6 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool if (copyFilePermissions) copyItemPermissions(sourceLink, targetLink, ProcSymlink::DIRECT); //throw FileError - - guardNewLink.dismiss(); //target has been created successfully! } @@ -1798,7 +1808,8 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw throw FileError(errorMsg, errorDescr); } - ScopeGuard guardTarget = makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: guard just after opening target and before managing hFileTarget + ZEN_ON_SCOPE_FAIL(try { removeFile(targetFile); } + catch (FileError&) {} ); //transactional behavior: guard just after opening target and before managing hFileTarget ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget)); //---------------------------------------------------------------------- @@ -1807,7 +1818,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"GetFileInformationByHandle"); //return up-to-date file attributes - InSyncAttributes newAttrib = {}; + InSyncAttributes newAttrib; newAttrib.fileSize = get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh); newAttrib.modificationTime = filetimeToTimeT(fileInfoSource.ftLastWriteTime); //no DST hack (yet) newAttrib.sourceFileId = extractFileId(fileInfoSource); @@ -1923,7 +1934,6 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw &fileInfoSource.ftLastWriteTime)) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(targetFile)), L"SetFileTime"); - guardTarget.dismiss(); return newAttrib; } @@ -1938,20 +1948,17 @@ struct CallbackData const Zstring& targetFile) : sourceFile_(sourceFile), targetFile_(targetFile), - onUpdateCopyStatus_(onUpdateCopyStatus), - fileInfoSrc(), - fileInfoTrg(), - bytesReported() {} + onUpdateCopyStatus_(onUpdateCopyStatus) {} const Zstring& sourceFile_; const Zstring& targetFile_; const std::function<void(std::int64_t bytesDelta)>& onUpdateCopyStatus_; //optional std::exception_ptr exception; //out - BY_HANDLE_FILE_INFORMATION fileInfoSrc; //out: modified by CopyFileEx() at beginning - BY_HANDLE_FILE_INFORMATION fileInfoTrg; // + BY_HANDLE_FILE_INFORMATION fileInfoSrc{}; //out: modified by CopyFileEx() at beginning + BY_HANDLE_FILE_INFORMATION fileInfoTrg{}; // - std::int64_t bytesReported; //used internally to calculate bytes transferred delta + std::int64_t bytesReported = 0; //used internally to calculate bytes transferred delta }; @@ -2060,7 +2067,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE try { activatePrivilege(SE_RESTORE_NAME); } catch (const FileError&) { backupPrivilegesActive = false; } - zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); + auto guardTarget = zen::makeGuard<ScopeGuardRunMode::ON_FAIL>([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: guard just before starting copy, we don't trust ::CopyFileEx(), do we? ;) DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS; @@ -2152,9 +2159,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE // - perf: recent measurements show no slow down at all for buffered USB sticks! setFileTimeRaw(targetFile, &cbd.fileInfoSrc.ftCreationTime, cbd.fileInfoSrc.ftLastWriteTime, ProcSymlink::FOLLOW); //throw FileError - guardTarget.dismiss(); //target has been created successfully! - - InSyncAttributes newAttrib = {}; + InSyncAttributes newAttrib; newAttrib.fileSize = get64BitUInt(cbd.fileInfoSrc.nFileSizeLow, cbd.fileInfoSrc.nFileSizeHigh); newAttrib.modificationTime = filetimeToTimeT(cbd.fileInfoSrc.ftLastWriteTime); newAttrib.sourceFileId = extractFileId(cbd.fileInfoSrc); @@ -2228,8 +2233,9 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError } if (onUpdateCopyStatus) onUpdateCopyStatus(0); //throw X! - InSyncAttributes newAttrib = {}; - zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); + InSyncAttributes newAttrib; + ZEN_ON_SCOPE_FAIL( try { removeFile(targetFile); } + catch (FileError&) {} ); //transactional behavior: place guard after ::open() and before lifetime of FileOutput: //=> don't delete file that existed previously!!! { @@ -2278,7 +2284,6 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError setFileTime(targetFile, sourceInfo.st_mtime, ProcSymlink::FOLLOW); //throw FileError #endif - guardTarget.dismiss(); //target has been created successfully! return newAttrib; } #endif @@ -2303,15 +2308,12 @@ InSyncAttributes zen::copyNewFile(const Zstring& sourceFile, const Zstring& targ { const InSyncAttributes attr = copyFileOsSpecific(sourceFile, targetFile, onUpdateCopyStatus); //throw FileError, ErrorTargetExisting, ErrorFileLocked - if (copyFilePermissions) - { - //at this point we know we created a new file, so it's fine to delete it for cleanup! - zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {}}); + //at this point we know we created a new file, so it's fine to delete it for cleanup! + ZEN_ON_SCOPE_FAIL(try { removeFile(targetFile); } + catch (FileError&) {}); + if (copyFilePermissions) copyItemPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError - guardTargetFile.dismiss(); //target has been created successfully! - } - return attr; } diff --git a/zen/file_access.h b/zen/file_access.h index 3588f79b..ec5bda66 100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -55,8 +55,8 @@ void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copy struct InSyncAttributes { - std::uint64_t fileSize; - std::int64_t modificationTime; //time_t UTC compatible + std::uint64_t fileSize = 0; + std::int64_t modificationTime = 0; //time_t UTC compatible FileId sourceFileId; FileId targetFileId; }; diff --git a/zen/file_error.h b/zen/file_error.h index 7be52282..f41a878a 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILEERROR_H_INCLUDED_839567308565656789 -#define FILEERROR_H_INCLUDED_839567308565656789 +#ifndef FILE_ERROR_H_839567308565656789 +#define FILE_ERROR_H_839567308565656789 #include <string> #include "zstring.h" @@ -49,7 +49,7 @@ DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume); __pragma(warning(suppress: 4127)) /*"conditional expression is constant"*/ \ } while (false) -#else //variant witout "__pragma": +#else //same thing witout "__pragma": #define THROW_LAST_FILE_ERROR(msg, functionName) \ do { const ErrorCode ecInternal = getLastError(); throw FileError(msg, formatSystemError(functionName, ecInternal)); } while (false) #endif @@ -66,4 +66,4 @@ inline std::wstring fmtPath(const Zstring& displayPath) { return fmtPath(utfCvrt inline std::wstring fmtPath(const wchar_t* displayPath) { return fmtPath(std::wstring(displayPath)); } } -#endif //FILEERROR_H_INCLUDED_839567308565656789 +#endif //FILE_ERROR_H_839567308565656789 diff --git a/zen/file_id_def.h b/zen/file_id_def.h index c33edf81..24e45795 100644 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILE_ID_INTERNAL_HEADER_013287632486321493 -#define FILE_ID_INTERNAL_HEADER_013287632486321493 +#ifndef FILE_ID_DEF_H_013287632486321493 +#define FILE_ID_DEF_H_013287632486321493 #include <utility> @@ -66,4 +66,4 @@ FileId extractFileId(const struct ::stat& fileInfo) #endif } -#endif //FILE_ID_INTERNAL_HEADER_013287632486321493 +#endif //FILE_ID_DEF_H_013287632486321493 diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 68e852da..5e8b7a1d 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -150,14 +150,11 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock //------------------------------------------------------------------------------------------------------ - ScopeGuard constructorGuard = zen::makeGuard([&] //destructor call would lead to member double clean-up!!! - { -#ifdef ZEN_WIN - ::CloseHandle(fileHandle); +#ifdef ZEN_WIN //destructor call would lead to member double clean-up!!! + ZEN_ON_SCOPE_FAIL(::CloseHandle(fileHandle)); #elif defined ZEN_LINUX || defined ZEN_MAC - ::close(fileHandle); + ZEN_ON_SCOPE_FAIL(::close(fileHandle)); #endif - }); #ifdef ZEN_LINUX //handle still un-owned => need constructor guard //optimize read-ahead on input file: @@ -167,8 +164,6 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock #elif defined ZEN_MAC //"dtruss" doesn't show use of "fcntl() F_RDAHEAD/F_RDADVISE" for "cp") #endif - - constructorGuard.dismiss(); } diff --git a/zen/file_io.h b/zen/file_io.h index 52be7f95..5bcf4189 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FILEIO_89578342758342572345 -#define FILEIO_89578342758342572345 +#ifndef FILE_IO_H_89578342758342572345 +#define FILE_IO_H_89578342758342572345 #include "file_error.h" @@ -88,4 +88,4 @@ private: }; } -#endif //FILEIO_89578342758342572345 +#endif //FILE_IO_H_89578342758342572345 diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index facce75c..7fd6c596 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -6,9 +6,9 @@ #include "file_traverser.h" #include "file_error.h" -#include "int64.h" #ifdef ZEN_WIN + #include "int64.h" #include "long_path_prefix.h" #include "file_access.h" #include "symlink_target.h" @@ -68,8 +68,8 @@ void zen::traverseFolder(const Zstring& dirPath, //skip "." and ".." const wchar_t* const itemNameRaw = findData.cFileName; - if (itemNameRaw[0] == 0) - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name."); + if (itemNameRaw[0] == 0) + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name."); if (itemNameRaw[0] == L'.' && (itemNameRaw[1] == 0 || (itemNameRaw[1] == L'.' && itemNameRaw[2] == 0))) @@ -106,15 +106,15 @@ void zen::traverseFolder(const Zstring& dirPath, std::vector<char> bufferUtfDecomposed; #endif - DIR* dirObj = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" - if (!dirObj) + DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" + if (!folder) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir"); - ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash + ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash for (;;) { struct ::dirent* dirEntry = nullptr; - if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) + if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r"); //don't retry but restart dir traversal on error! http://blogs.msdn.com/b/oldnewthing/archive/2014/06/12/10533529.aspx @@ -125,7 +125,7 @@ void zen::traverseFolder(const Zstring& dirPath, const char* itemNameRaw = dirEntry->d_name; if (itemNameRaw[0] == 0) - throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); if (itemNameRaw[0] == '.' && (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0))) diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 75c7660c..9f850e01 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FOLDER_TRAVERSER_H_INCLUDED_3127463214871234 -#define FOLDER_TRAVERSER_H_INCLUDED_3127463214871234 +#ifndef FILER_TRAVERSER_H_127463214871234 +#define FILER_TRAVERSER_H_127463214871234 #include <cstdint> #include <functional> @@ -41,4 +41,4 @@ void traverseFolder(const Zstring& dirPath, //noexcept const std::function<void (const std::wstring& errorMsg)>& onError); // } -#endif //FOLDER_TRAVERSER_H_INCLUDED_3127463214871234 +#endif //FILER_TRAVERSER_H_127463214871234 diff --git a/zen/fixed_list.h b/zen/fixed_list.h index 61ce3f16..362577d5 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef FIXED_LIST_01238467085684139453534 -#define FIXED_LIST_01238467085684139453534 +#ifndef FIXED_LIST_H_01238467085684139453534 +#define FIXED_LIST_H_01238467085684139453534 #include <cassert> #include <iterator> + namespace zen { //std::list(C++11)-like class for inplace element construction supporting non-copyable/movable types @@ -20,9 +21,9 @@ class FixedList struct Node { template <class... Args> - Node(Args&& ... args) : next(nullptr), val(std::forward<Args>(args)...) {} + Node(Args&& ... args) : val(std::forward<Args>(args)...) {} - Node* next; //singly linked list is sufficient + Node* next = nullptr; //singly linked list is sufficient T val; }; @@ -35,14 +36,14 @@ public: class ListIterator : public std::iterator<std::forward_iterator_tag, U> { public: - ListIterator(NodeT* it = nullptr) : iter(it) {} - ListIterator& operator++() { iter = iter->next; return *this; } - inline friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { return lhs.iter == rhs.iter; } + ListIterator(NodeT* it = nullptr) : it_(it) {} + ListIterator& operator++() { it_ = it_->next; return *this; } + inline friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { return lhs.it_ == rhs.it_; } inline friend bool operator!=(const ListIterator& lhs, const ListIterator& rhs) { return !(lhs == rhs); } - U& operator* () const { return iter->val; } - U* operator->() const { return &iter->val; } + U& operator* () const { return it_->val; } + U* operator->() const { return &it_->val; } private: - NodeT* iter; + NodeT* it_; }; typedef T value_type; @@ -154,4 +155,4 @@ private: }; } -#endif //FIXED_LIST_01238467085684139453534 +#endif //FIXED_LIST_H_01238467085684139453534 diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 9624458c..e9c686aa 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -8,12 +8,12 @@ #include "basic_math.h" #include "i18n.h" #include "time.h" -#include "int64.h" #include <cwchar> //swprintf #include <ctime> #include <cstdio> #ifdef ZEN_WIN + #include "int64.h" #include "win.h" //includes "windows.h" #include "win_ver.h" @@ -25,9 +25,18 @@ using namespace zen; +std::wstring zen::formatTwoDigitPrecision(double value) +{ + //print two digits: 0,1 | 1,1 | 11 + if (numeric::abs(value) < 9.95) //9.99 must not be formatted as "10.0" + return printNumber<std::wstring>(L"%.1f", value); + return numberTo<std::wstring>(numeric::round(value)); +} + + std::wstring zen::formatThreeDigitPrecision(double value) { - //print at least three digits: 0,01 | 0,11 | 1,11 | 11,1 | 111 + //print three digits: 0,01 | 0,11 | 1,11 | 11,1 | 111 if (numeric::abs(value) < 9.995) //9.999 must not be formatted as "10.00" return printNumber<std::wstring>(L"%.2f", value); if (numeric::abs(value) < 99.95) //99.99 must not be formatted as "100.0" @@ -197,7 +206,7 @@ private: return inst; } - IntegerFormat() : fmt(), valid_(false) + IntegerFormat() { //all we want is default NUMBERFMT, but set NumDigits to 0 fmt.NumDigits = 0; @@ -224,10 +233,10 @@ private: } } - NUMBERFMT fmt; + NUMBERFMT fmt = {}; std::wstring thousandSep; std::wstring decimalSep; - bool valid_; + bool valid_ = false; }; } #endif diff --git a/zen/format_unit.h b/zen/format_unit.h index d50baa32..eacb8d46 100644 --- a/zen/format_unit.h +++ b/zen/format_unit.h @@ -19,7 +19,8 @@ std::wstring remainingTimeToString(double timeInSec); std::wstring fractionToString(double fraction); //within [0, 1] std::wstring utcToLocalTimeString(std::int64_t utcTime); //like Windows Explorer would... -std::wstring formatThreeDigitPrecision(double value); //= *at least* three digits +std::wstring formatTwoDigitPrecision (double value); //format with fixed number of digits +std::wstring formatThreeDigitPrecision(double value); //(unless value is too large) template <class NumberType> std::wstring toGuiString(NumberType number); //format integer number including thousands separator @@ -4,16 +4,15 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef GUID_H_INCLUDED_80425780237502345 -#define GUID_H_INCLUDED_80425780237502345 +#ifndef GUID_H_80425780237502345 +#define GUID_H_80425780237502345 #include <string> #include <boost/uuid/uuid.hpp> #ifdef __GNUC__ //boost should clean this mess up #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wshadow" - #pragma GCC diagnostic ignored "-Wuninitialized" + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #include <boost/uuid/uuid_generators.hpp> @@ -35,4 +34,4 @@ std::string generateGUID() //creates a 16 byte GUID } } -#endif //GUID_H_INCLUDED_80425780237502345 +#endif //GUID_H_80425780237502345 @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef I18_N_HEADER_3843489325045 -#define I18_N_HEADER_3843489325045 +#ifndef I18_N_H_3843489325044253425456 +#define I18_N_H_3843489325044253425456 #include <string> #include <memory> @@ -28,18 +28,24 @@ namespace zen { -//implement handler to enable program wide localizations: implement THREAD-SAFE ACCESS! +//implement handler to enable program wide localizations: struct TranslationHandler { + //THREAD-SAFETY: "const" member must model thread-safe access! + TranslationHandler() {} virtual ~TranslationHandler() {} //C++11: std::wstring should be thread-safe like an int - virtual std::wstring translate(const std::wstring& text) = 0; //simple translation - virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) = 0; + virtual std::wstring translate(const std::wstring& text) const = 0; //simple translation + virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) const = 0; + +private: + TranslationHandler (const TranslationHandler&) = delete; + TranslationHandler& operator=(const TranslationHandler&) = delete; }; -void setTranslator(std::unique_ptr<TranslationHandler>&& newHandler = nullptr); //take ownership -TranslationHandler* getTranslator(); +void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler = nullptr); //take ownership +const TranslationHandler* getTranslator(); @@ -59,12 +65,13 @@ namespace implementation inline std::wstring translate(const std::wstring& text) { - if (TranslationHandler* t = getTranslator()) + if (const TranslationHandler* t = getTranslator()) return t->translate(text); return text; } + //translate plural forms: "%x day" "%x days" //returns "1 day" if n == 1; "123 days" if n == 123 for english language inline @@ -72,7 +79,7 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, { assert(contains(plural, L"%x")); - if (TranslationHandler* t = getTranslator()) + if (const TranslationHandler* t = getTranslator()) { std::wstring translation = t->translate(singular, plural, n); assert(!contains(translation, L"%x")); @@ -82,6 +89,7 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n)); } + template <class T> inline std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n) { @@ -89,19 +97,22 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, return translate(singular, plural, static_cast<std::int64_t>(n)); } + inline -std::unique_ptr<TranslationHandler>& globalHandler() +std::unique_ptr<const TranslationHandler>& globalHandler() { - static std::unique_ptr<TranslationHandler> inst; //external linkage even in header! + static std::unique_ptr<const TranslationHandler> inst; //external linkage even in header! return inst; } } + inline -void setTranslator(std::unique_ptr<TranslationHandler>&& newHandler) { implementation::globalHandler() = std::move(newHandler); } +void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler) { implementation::globalHandler() = std::move(newHandler); } + inline -TranslationHandler* getTranslator() { return implementation::globalHandler().get(); } +const TranslationHandler* getTranslator() { return implementation::globalHandler().get(); } } -#endif //I18_N_HEADER_3843489325045 +#endif //I18_N_H_3843489325044253425456 diff --git a/zen/int64.h b/zen/int64.h deleted file mode 100644 index bc01a4c2..00000000 --- a/zen/int64.h +++ /dev/null @@ -1,71 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FFS_LARGE_64_BIT_INTEGER_H_INCLUDED -#define FFS_LARGE_64_BIT_INTEGER_H_INCLUDED - -#include <cstdint> - -#ifdef ZEN_WIN - #include "win.h" -#endif - - -namespace zen -{ -#ifdef ZEN_WIN -inline -std::int64_t get64BitInt(DWORD low, LONG high) -{ - static_assert(sizeof(low) + sizeof(high) == sizeof(std::int64_t), ""); - - LARGE_INTEGER cvt = {}; - cvt.LowPart = low; - cvt.HighPart = high; - return cvt.QuadPart; -} - -std::int64_t get64BitInt(std::uint64_t low, std::int64_t high) = delete; - - -inline -std::uint64_t get64BitUInt(DWORD low, DWORD high) -{ - static_assert(sizeof(low) + sizeof(high) == sizeof(std::uint64_t), ""); - - ULARGE_INTEGER cvt = {}; - cvt.LowPart = low; - cvt.HighPart = high; - return cvt.QuadPart; -} - -std::int64_t get64BitUInt(std::uint64_t low, std::uint64_t high) = delete; - - -//convert FILETIME (number of 100-nanosecond intervals since January 1, 1601 UTC) -// to time_t (number of seconds since Jan. 1st 1970 UTC) -// -//FAT32 time is preserved exactly: FAT32 -> toTimeT -> tofiletime -> FAT32 -inline -std::int64_t filetimeToTimeT(const FILETIME& ft) -{ - return static_cast<std::int64_t>(get64BitUInt(ft.dwLowDateTime, ft.dwHighDateTime) / 10000000U) - get64BitInt(3054539008UL, 2); //caveat: signed/unsigned arithmetics! - //timeshift between ansi C time and FILETIME in seconds == 11644473600s -} - -inline -FILETIME timetToFileTime(std::int64_t utcTime) -{ - ULARGE_INTEGER cvt = {}; - cvt.QuadPart = (utcTime + get64BitInt(3054539008UL, 2)) * 10000000U; //caveat: signed/unsigned arithmetics! - - const FILETIME output = { cvt.LowPart, cvt.HighPart }; - return output; -} -#endif -} - -#endif //FFS_LARGE_64_BIT_INTEGER_H_INCLUDED diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h index 6f6ebfdc..ef8e5dc8 100644 --- a/zen/long_path_prefix.h +++ b/zen/long_path_prefix.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef LONGPATHPREFIX_H_INCLUDED -#define LONGPATHPREFIX_H_INCLUDED +#ifndef LONG_PATH_PREFIX_H_3984678473567247567 +#define LONG_PATH_PREFIX_H_3984678473567247567 #include "win.h" #include "zstring.h" @@ -60,7 +60,7 @@ Zstring applyLongPathPrefixImpl(const Zstring& path) assert(!path.empty()); //nicely check almost all WinAPI accesses! assert(!zen::isWhiteSpace(path[0])); - //http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#naming_conventions)) + //http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#naming_conventions /* - special names like ".NUL" create all kinds of trouble (e.g. CreateDirectory() reports success, but does nothing) unless prefix is supplied => accept as limitation @@ -131,4 +131,4 @@ Zstring zen::ntPathToWin32Path(const Zstring& path) //noexcept return path; } -#endif //LONGPATHPREFIX_H_INCLUDED +#endif //LONG_PATH_PREFIX_H_3984678473567247567 diff --git a/zen/optional.h b/zen/optional.h index d65820c2..c01d95ff 100644 --- a/zen/optional.h +++ b/zen/optional.h @@ -8,6 +8,7 @@ #define OPTIONAL_H_2857428578342203589 #include <cassert> +#include <type_traits> namespace zen { @@ -36,36 +37,52 @@ template <class T> class Opt { public: - Opt() : value() , valid(false) {} - Opt(NoValue) : value() , valid(false) {} - Opt(const T& val) : value(val), valid(true ) {} + Opt() {} + Opt(NoValue) {} + Opt(const T& val) : valid(true) { new (&rawMem) T(val); } //throw X - Opt(const Opt& tmp) : value(tmp.valid ? tmp.value : T()), valid(tmp.valid) {} + Opt(const Opt& other) : valid(other.valid) + { + if (const T* val = other.get()) + new (&rawMem) T(*val); //throw X + } - Opt& operator=(const Opt& tmp) + ~Opt() { if (T* val = get()) val->~T(); } + + Opt& operator=(const Opt& other) //strong exception-safety iff T::operator=() is strongly exception-safe { - if (tmp.valid) - value = tmp.value; - valid = tmp.valid; + if (T* val = get()) + { + if (const T* valOther = other.get()) + *val = *valOther; //throw X + else + { + valid = false; + val->~T(); + } + } + else if (const T* valOther = other.get()) + { + new (&rawMem) T(*valOther); //throw X + valid = true; + } return *this; } - ////rvalue optimization: only basic exception safety: - // Opt(Opt&& tmp) : value(std::move(tmp.value)), valid(tmp.valid) {} - explicit operator bool() const { return valid; } //thank you C++11!!! - const T& operator*() const { assert(valid); return value; } - /**/ T& operator*() { assert(valid); return value; } + const T* get() const { return valid ? reinterpret_cast<const T*>(&rawMem) : nullptr; } + T* get() { return valid ? reinterpret_cast< T*>(&rawMem) : nullptr; } - const T* operator->() const { assert(valid); return &value; } - /**/ T* operator->() { assert(valid); return &value; } + const T& operator*() const { return *get(); } + /**/ T& operator*() { return *get(); } - void reset() { valid = false; } + const T* operator->() const { return get(); } + /**/ T* operator->() { return get(); } private: - T value; - bool valid; + std::aligned_storage_t<sizeof(T), alignof(T)> rawMem; //don't require T to be default-constructible! + bool valid = false; }; } @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef DEBUG_PERF_HEADER_83947184145342652456 -#define DEBUG_PERF_HEADER_83947184145342652456 +#ifndef PERF_H_83947184145342652456 +#define PERF_H_83947184145342652456 #include "deprecate.h" #include "tick_count.h" @@ -17,6 +17,7 @@ #include <iostream> #endif + //############# two macros for quick performance measurements ############### #define PERF_START zen::PerfTimer perfTest; #define PERF_STOP perfTest.showResult(); @@ -30,19 +31,14 @@ public: class TimerError {}; ZEN_DEPRECATE - PerfTimer() : //throw TimerError - ticksPerSec_(ticksPerSec()), - resultShown(false), - startTime(getTicksNow()), - paused(false), - elapsedUntilPause(0) + PerfTimer() : startTime(getTicksNow()) //throw TimerError { //std::clock() - "counts CPU time in Linux GCC and wall time in VC++" - WTF!??? if (ticksPerSec_ == 0) throw TimerError(); } - ~PerfTimer() { if (!resultShown) try { showResult(); } catch (TimerError&) {} } + ~PerfTimer() { if (!resultShown) try { showResult(); } catch (TimerError&) { assert(false); } } void pause() { @@ -102,12 +98,12 @@ private: return now; } - const std::int64_t ticksPerSec_; - bool resultShown; + const std::int64_t ticksPerSec_ = ticksPerSec(); //return 0 on error + bool resultShown = false; TickVal startTime; - bool paused; - int64_t elapsedUntilPause; + bool paused = false; + int64_t elapsedUntilPause = 0; }; } -#endif //DEBUG_PERF_HEADER_83947184145342652456 +#endif //PERF_H_83947184145342652456 diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp index 017eaa8b..2d1abafa 100644 --- a/zen/process_priority.cpp +++ b/zen/process_priority.cpp @@ -30,11 +30,6 @@ PreventStandby::~PreventStandby() } -#ifndef PROCESS_MODE_BACKGROUND_BEGIN - #define PROCESS_MODE_BACKGROUND_BEGIN 0x00100000 // Windows Server 2003 and Windows XP/2000: This value is not supported! - #define PROCESS_MODE_BACKGROUND_END 0x00200000 // -#endif - struct ScheduleForBackgroundProcessing::Pimpl {}; diff --git a/zen/process_priority.h b/zen/process_priority.h index 72ce972c..48b95c9e 100644 --- a/zen/process_priority.h +++ b/zen/process_priority.h @@ -3,8 +3,8 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef PREVENTSTANDBY_H_83421759082143245 -#define PREVENTSTANDBY_H_83421759082143245 +#ifndef PROCESS_PRIORITY_H_83421759082143245 +#define PROCESS_PRIORITY_H_83421759082143245 #include <memory> #include "file_error.h" @@ -35,4 +35,4 @@ private: }; } -#endif //PREVENTSTANDBY_H_83421759082143245 +#endif //PROCESS_PRIORITY_H_83421759082143245 diff --git a/zen/recycler.cpp b/zen/recycler.cpp index d49e686c..59d2729a 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -12,6 +12,8 @@ #ifdef ZEN_WIN_VISTA_AND_LATER #include "vista_file_op.h" + #else + #include "com_tools.h" #endif #elif defined ZEN_LINUX @@ -179,12 +181,18 @@ bool zen::recycleBinExists(const Zstring& dirPath, const std::function<void ()>& #else //excessive runtime if recycle bin exists, is full and drive is slow: - auto ft = runAsync([dirPath]() + auto ft = runAsync([dirPath]() -> HRESULT { - SHQUERYRBINFO recInfo = {}; - recInfo.cbSize = sizeof(recInfo); - return ::SHQueryRecycleBin(dirPath.c_str(), //__in_opt LPCTSTR pszRootPath, - &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo + try + { + ComInitializer ci; //throw SysError + + SHQUERYRBINFO recInfo = {}; + recInfo.cbSize = sizeof(recInfo); + return ::SHQueryRecycleBin(dirPath.c_str(), //__in_opt LPCTSTR pszRootPath, + &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo + } + catch (SysError&) { assert(false); return ERROR_GEN_FAILURE; } }); while (ft.wait_for(std::chrono::milliseconds(50)) != std::future_status::ready) diff --git a/zen/recycler.h b/zen/recycler.h index 3df7fda2..4b5658cb 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef RECYCLER_H_INCLUDED_18345067341545 -#define RECYCLER_H_INCLUDED_18345067341545 +#ifndef RECYCLER_H_18345067341545 +#define RECYCLER_H_18345067341545 #include <vector> #include <functional> @@ -48,4 +48,4 @@ void recycleOrDelete(const std::vector<Zstring>& filePaths, //throw FileError, r #endif } -#endif //RECYCLER_H_INCLUDED_18345067341545 +#endif //RECYCLER_H_18345067341545 diff --git a/zen/scope_guard.h b/zen/scope_guard.h index b5564c9b..791764de 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -4,79 +4,116 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZEN_SCOPEGUARD_8971632487321434 -#define ZEN_SCOPEGUARD_8971632487321434 +#ifndef SCOPE_GUARD_H_8971632487321434 +#define SCOPE_GUARD_H_8971632487321434 #include <cassert> +#include <exception> #include <type_traits> //std::decay -//best of Zen, Loki and C++11 +//best of Zen, Loki and C++17 + + +#ifdef ZEN_WIN +inline int getUncaughtExceptionCount() { return std::uncaught_exceptions(); } + +#elif defined ZEN_LINUX || defined ZEN_MAC +//std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP +#ifdef ZEN_LINUX + static_assert(__GNUC__ < 5 || (__GNUC__ == 5 && (__GNUC_MINOR__ < 2 || (__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); +#else + static_assert(__clang_major__ < 7 || (__clang_major__ == 7 && __clang_minor__ <= 0), "check std::uncaught_exceptions support"); +#endif + +namespace __cxxabiv1 +{ +struct __cxa_eh_globals; +extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept; +} + +inline int getUncaughtExceptionCount() +{ + return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*))); +} +#endif + namespace zen { //Scope Guard /* - zen::ScopeGuard lockAio = zen::makeGuard([&] { ::CloseHandle(hDir); }); + auto guardAio = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { ::CloseHandle(hDir); }); ... - lockAio.dismiss(); -*/ + guardAio.dismiss(); -//Scope Exit -/* +Scope Exit: ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); + ZEN_ON_SCOPE_FAIL(UndoPreviousWork()); + ZEN_ON_SCOPE_SUCCESS(NotifySuccess()); */ -class ScopeGuardBase +enum class ScopeGuardRunMode { -public: - void dismiss() { dismissed_ = true; } - -protected: - ScopeGuardBase() {} - ScopeGuardBase(ScopeGuardBase&& other) : dismissed_(other.dismissed_) { other.dismiss(); } //take over responsibility - ~ScopeGuardBase() {} //[!] protected non-virtual base class destructor - - bool isDismissed() const { return dismissed_; } - -private: - ScopeGuardBase (const ScopeGuardBase&) = delete; - ScopeGuardBase& operator=(const ScopeGuardBase&) = delete; - - bool dismissed_ = false; + ON_EXIT, + ON_SUCCESS, + ON_FAIL }; -template <typename F> -class ScopeGuardImpl : public ScopeGuardBase +template <ScopeGuardRunMode runMode, typename F> +class ScopeGuard { public: - explicit ScopeGuardImpl(const F& fun) : fun_(fun) {} - explicit ScopeGuardImpl( F&& fun) : fun_(std::move(fun)) {} - ScopeGuardImpl(ScopeGuardImpl&& other) : ScopeGuardBase(std::move(other)), fun_(std::move(other.fun_)) {} + explicit ScopeGuard(const F& fun) : fun_(fun) {} + explicit ScopeGuard( F&& fun) : fun_(std::move(fun)) {} + + ScopeGuard(ScopeGuard&& other) : fun_(std::move(other.fun_)), + exeptionCount(other.exeptionCount), + dismissed(other.dismissed) { other.dismissed = true; } - ~ScopeGuardImpl() + ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS) { - if (!this->isDismissed()) - try +#ifdef _MSC_VER + #pragma warning(suppress: 4127) //"conditional expression is constant" +#endif + if (!dismissed) + { + if (runMode != ScopeGuardRunMode::ON_EXIT) { - fun_(); + const bool failed = getUncaughtExceptionCount() > exeptionCount; + if ((runMode == ScopeGuardRunMode::ON_FAIL) != failed) + return; } - catch (...) { assert(false); } + + if (runMode == ScopeGuardRunMode::ON_SUCCESS) + fun_(); //throw X + else + try { fun_(); } + catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"! + } } + void dismiss() { dismissed = true; } + private: + ScopeGuard (const ScopeGuard&) = delete; + ScopeGuard& operator=(const ScopeGuard&) = delete; + F fun_; + const int exeptionCount = getUncaughtExceptionCount(); + bool dismissed = false; }; -typedef ScopeGuardBase&& ScopeGuard; -template <class F> inline -ScopeGuardImpl<typename std::decay<F>::type> makeGuard(F&& fun) { return ScopeGuardImpl<typename std::decay<F>::type>(std::forward<F>(fun)); } +template <ScopeGuardRunMode runMode, class F> inline +auto makeGuard(F&& fun) { return ScopeGuard<runMode, std::decay_t<F>>(std::forward<F>(fun)); } } #define ZEN_CONCAT_SUB(X, Y) X ## Y #define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y) -#define ZEN_ON_SCOPE_EXIT(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__); +#define ZEN_ON_SCOPE_EXIT(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_EXIT >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__); +#define ZEN_ON_SCOPE_FAIL(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_FAIL >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__); +#define ZEN_ON_SCOPE_SUCCESS(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_SUCCESS>([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__); -#endif //ZEN_SCOPEGUARD_8971632487321434 +#endif //SCOPE_GUARD_H_8971632487321434 diff --git a/zen/serialize.h b/zen/serialize.h index ff2871b1..77cf657e 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SERIALIZE_H_INCLUDED_83940578357 -#define SERIALIZE_H_INCLUDED_83940578357 +#ifndef SERIALIZE_H_839405783574356 +#define SERIALIZE_H_839405783574356 #include <functional> #include <cstdint> @@ -250,4 +250,4 @@ C readContainer(BinInputStream& stream) //throw UnexpectedEndOfStreamError } } -#endif //SERIALIZE_H_INCLUDED_83940578357 +#endif //SERIALIZE_H_839405783574356 diff --git a/zen/shell_execute.h b/zen/shell_execute.h index 2adce179..9b8e00f9 100644 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef EXECUTE_HEADER_23482134578134134 -#define EXECUTE_HEADER_23482134578134134 +#ifndef SHELL_EXECUTE_H_23482134578134134 +#define SHELL_EXECUTE_H_23482134578134134 #include "file_error.h" @@ -94,9 +94,9 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError if (!argv.empty()) { filepath = argv[0]; - for (auto iter = argv.begin() + 1; iter != argv.end(); ++iter) - arguments += (iter != argv.begin() ? L" " : L"") + - (iter->empty() || std::any_of(iter->begin(), iter->end(), &isWhiteSpace<wchar_t>) ? L"\"" + *iter + L"\"" : *iter); + for (auto it = argv.begin() + 1; it != argv.end(); ++it) + arguments += (it != argv.begin() ? L" " : L"") + + (it->empty() || std::any_of(it->begin(), it->end(), &isWhiteSpace<wchar_t>) ? L"\"" + *it + L"\"" : *it); } auto fillExecInfo = [&](SHELLEXECUTEINFO& execInfo) @@ -130,4 +130,4 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError } } -#endif //EXECUTE_HEADER_23482134578134134 +#endif //SHELL_EXECUTE_H_23482134578134134 diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 1e38f1b0..b78dd5dd 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STL_TOOLS_HEADER_84567184321434 -#define STL_TOOLS_HEADER_84567184321434 +#ifndef STL_TOOLS_H_84567184321434 +#define STL_TOOLS_H_84567184321434 #include <set> #include <map> @@ -15,6 +15,7 @@ #include "type_tools.h" #include "build_info.h" + //enhancements for <algorithm> namespace zen { @@ -93,11 +94,11 @@ namespace impl template <class S, class Predicate> inline void set_or_map_erase_if(S& s, Predicate p) { - for (auto iter = s.begin(); iter != s.end();) - if (p(*iter)) - s.erase(iter++); + for (auto it = s.begin(); it != s.end();) + if (p(*it)) + s.erase(it++); else - ++iter; + ++it; } } @@ -125,14 +126,14 @@ void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c) { m.in template <class M, class K, class V> inline V& map_add_or_update(M& map, const K& key, const V& value) //efficient add or update without "default-constructible" requirement (Effective STL, item 24) { - auto iter = map.lower_bound(key); - if (iter != map.end() && !(map.key_comp()(key, iter->first))) + auto it = map.lower_bound(key); + if (it != map.end() && !(map.key_comp()(key, it->first))) { - iter->second = value; - return iter->second; + it->second = value; + return it->second; } else - return map.insert(iter, typename M::value_type(key, value))->second; + return map.insert(it, typename M::value_type(key, value))->second; } @@ -158,12 +159,12 @@ ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const template <class BidirectionalIterator, class T> inline BidirectionalIterator find_last(const BidirectionalIterator first, const BidirectionalIterator last, const T& value) { - for (BidirectionalIterator iter = last; iter != first;) //reverse iteration: 1. check 2. decrement 3. evaluate + for (BidirectionalIterator it = last; it != first;) //reverse iteration: 1. check 2. decrement 3. evaluate { - --iter; // + --it; // - if (*iter == value) - return iter; + if (*it == value) + return it; } return last; } @@ -173,7 +174,7 @@ template <class BidirectionalIterator1, class BidirectionalIterator2> inline BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1, const BidirectionalIterator2 first2, const BidirectionalIterator2 last2) { - const BidirectionalIterator1 iterNotFound = last1; + const BidirectionalIterator1 itNotFound = last1; //reverse iteration: 1. check 2. decrement 3. evaluate for (;;) @@ -184,7 +185,7 @@ BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, Bi for (;;) { if (it2 == first2) return it1; - if (it1 == first1) return iterNotFound; + if (it1 == first1) return itNotFound; --it1; --it2; @@ -231,4 +232,4 @@ size_t hashBytes(const unsigned char* ptr, size_t len) } } -#endif //STL_TOOLS_HEADER_84567184321434 +#endif //STL_TOOLS_H_84567184321434 diff --git a/zen/string_base.h b/zen/string_base.h index 224797e8..96d46fc4 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef Z_BASE_H_INCLUDED_08321745456 -#define Z_BASE_H_INCLUDED_08321745456 +#ifndef STRING_BASE_H_083217454562342526 +#define STRING_BASE_H_083217454562342526 #include <algorithm> #include <cassert> @@ -249,8 +249,8 @@ public: //std::string functions size_t length() const; size_t size () const { return length(); } - const Char* c_str() const { return rawStr; }; //C-string format with 0-termination - const Char* data() const { return rawStr; }; //internal representation, 0-termination not guaranteed + const Char* c_str() const { return rawStr; } //C-string format with 0-termination + const Char* data() const { return rawStr; } //internal representation, 0-termination not guaranteed const Char operator[](size_t pos) const; bool empty() const { return length() == 0; } void clear(); @@ -686,4 +686,4 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator+=(Char ch) } } -#endif //Z_BASE_H_INCLUDED_08321745456 +#endif //STRING_BASE_H_083217454562342526 diff --git a/zen/string_tools.h b/zen/string_tools.h index 8f83b9cd..fc9fe806 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STRING_TOOLS_HEADER_213458973046 -#define STRING_TOOLS_HEADER_213458973046 +#ifndef STRING_TOOLS_H_213458973046 +#define STRING_TOOLS_H_213458973046 #include <cctype> //isspace #include <cwctype> //iswspace @@ -42,8 +42,8 @@ template <class S, class T> S afterFirst (const S& str, const T& term, FailureRe template <class S, class T> S beforeFirst(const S& str, const T& term, FailureReturnVal rv); template <class S, class T> std::vector<S> split(const S& str, const T& delimiter); -template <class S> void trim ( S& str, bool fromLeft = true, bool fromRight = true); -template <class S> S trimCpy(const S& str, bool fromLeft = true, bool fromRight = true); +template <class S> void trim (S& str, bool fromLeft = true, bool fromRight = true); +template <class S> S trimCpy(S str, bool fromLeft = true, bool fromRight = true); template <class S, class T, class U> void replace ( S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true); template <class S, class T, class U> S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true); @@ -54,7 +54,7 @@ template <class Num, class S > Num stringTo(const S& str); template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using std::snprintf() //string to string conversion: converts string-like type into char-compatible target string class -template <class T, class S> T copyStringTo(const S& str); +template <class T, class S> T copyStringTo(S&& str); @@ -94,6 +94,7 @@ bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()! return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9'); } + template <> bool isAlpha(char ch) = delete; //probably not a good idea with UTF-8 anyway... template <> inline bool isAlpha(wchar_t ch) { return std::iswalpha(ch) != 0; } @@ -252,7 +253,7 @@ std::vector<S> split(const S& str, const T& delimiter) } -namespace implementation +namespace impl { ZEN_INIT_DETECT_MEMBER(append); @@ -295,8 +296,8 @@ S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll) for (;;) { - implementation::stringAppend(output, strPos, strMatch - strPos); - implementation::stringAppend(output, newBegin, newLen); + impl::stringAppend(output, strPos, strMatch - strPos); + impl::stringAppend(output, newBegin, newLen); strPos = strMatch + oldLen; @@ -308,7 +309,7 @@ S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll) if (strMatch == strEnd) break; } - implementation::stringAppend(output, strPos, strEnd - strPos); + impl::stringAppend(output, strPos, strEnd - strPos); return output; } @@ -346,16 +347,15 @@ void trim(S& str, bool fromLeft, bool fromRight) template <class S> inline -S trimCpy(const S& str, bool fromLeft, bool fromRight) +S trimCpy(S str, bool fromLeft, bool fromRight) { //implementing trimCpy() in terms of trim(), instead of the other way round, avoids memory allocations when trimming from right! - S tmp = str; - trim(tmp, fromLeft, fromRight); - return tmp; + trim(str, fromLeft, fromRight); + return std::move(str); //"str" is an l-value parameter => no copy elision! } -namespace implementation +namespace impl { template <class S, class T> struct CopyStringToString @@ -363,18 +363,19 @@ struct CopyStringToString T copy(const S& src) const { return T(strBegin(src), strLength(src)); } }; -template <class S> -struct CopyStringToString<S, S> //perf: we don't need a deep copy if string types match +template <class T> +struct CopyStringToString<T, T> //perf: we don't need a deep copy if string types match { - const S& copy(const S& src) const { return src; } + template <class S> + T copy(S&& str) const { return std::forward<S>(str); } }; } template <class T, class S> inline -T copyStringTo(const S& str) { return implementation::CopyStringToString<S, T>().copy(str); } +T copyStringTo(S&& str) { return impl::CopyStringToString<std::decay_t<S>, T>().copy(std::forward<S>(str)); } -namespace implementation +namespace impl { template <class Num> inline int saferPrintf(char* buffer, size_t bufferSize, const char* format, const Num& number) //there is no such thing as a "safe" printf ;) @@ -400,17 +401,18 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const template <class S, class T, class Num> inline S printNumber(const T& format, const Num& number) //format a single number using ::sprintf { - typedef typename GetCharType<S>::Type CharType; + static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, ""); + using CharType = typename GetCharType<S>::Type; const int BUFFER_SIZE = 128; - CharType buffer[BUFFER_SIZE]; - const int charsWritten = implementation::saferPrintf(buffer, BUFFER_SIZE, strBegin(format), number); + CharType buffer[BUFFER_SIZE]; //zero-initialize? + const int charsWritten = impl::saferPrintf(buffer, BUFFER_SIZE, strBegin(format), number); return charsWritten > 0 ? S(buffer, charsWritten) : S(); } -namespace implementation +namespace impl { enum NumberType { @@ -424,7 +426,7 @@ enum NumberType template <class S, class Num> inline S numberTo(const Num& number, Int2Type<NUM_TYPE_OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20) { - typedef typename GetCharType<S>::Type CharType; + using CharType = typename GetCharType<S>::Type; std::basic_ostringstream<CharType> ss; ss << number; @@ -453,7 +455,7 @@ template <class OutputIterator, class Num> inline void formatNegativeInteger(Num n, OutputIterator& it) { assert(n < 0); - typedef typename std::iterator_traits<OutputIterator>::value_type CharType; + using CharType = typename std::iterator_traits<OutputIterator>::value_type; do { const Num tmp = n / 10; @@ -469,7 +471,7 @@ template <class OutputIterator, class Num> inline void formatPositiveInteger(Num n, OutputIterator& it) { assert(n >= 0); - typedef typename std::iterator_traits<OutputIterator>::value_type CharType; + using CharType = typename std::iterator_traits<OutputIterator>::value_type; do { const Num tmp = n / 10; @@ -483,8 +485,9 @@ void formatPositiveInteger(Num n, OutputIterator& it) template <class S, class Num> inline S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) { - typedef typename GetCharType<S>::Type CharType; - CharType buffer[2 + sizeof(Num) * 241 / 100]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency + using CharType = typename GetCharType<S>::Type; + CharType buffer[2 + sizeof(Num) * 241 / 100]; //zero-initialize? + //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency //required chars (+ sign char): 1 + ceil(ln_10(256^sizeof(n) / 2 + 1)) -> divide by 2 for signed half-range; second +1 since one half starts with 1! // <= 1 + ceil(ln_10(256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.41) @@ -503,8 +506,8 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>) template <class S, class Num> inline S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) { - typedef typename GetCharType<S>::Type CharType; - CharType buffer[1 + sizeof(Num) * 241 / 100]; + using CharType = typename GetCharType<S>::Type; + CharType buffer[1 + sizeof(Num) * 241 / 100]; //zero-initialize? //required chars: ceil(ln_10(256^sizeof(n))) =~ ceil(sizeof(n) * 2.4082) <= 1 + floor(sizeof(n) * 2.41) auto it = std::end(buffer); @@ -519,7 +522,7 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>) template <class Num, class S> inline Num stringTo(const S& str, Int2Type<NUM_TYPE_OTHER>) //default string to number conversion using streams: convenient, but SLOW { - typedef typename GetCharType<S>::Type CharType; + using CharType = typename GetCharType<S>::Type; Num number = 0; std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType>>(str)) >> number; return number; @@ -538,8 +541,8 @@ Num stringTo(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>) template <class Num, class S> Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic { - typedef typename GetCharType<S>::Type CharType; - + using CharType = typename GetCharType<S>::Type; + const CharType* first = strBegin(str); const CharType* last = first + strLength(str); @@ -606,28 +609,28 @@ Num stringTo(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversi template <class S, class Num> inline S numberTo(const Num& number) { - typedef Int2Type< - IsSignedInt <Num>::value ? implementation::NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::value ? implementation::NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::value ? implementation::NUM_TYPE_FLOATING_POINT : - implementation::NUM_TYPE_OTHER> TypeTag; + using TypeTag = Int2Type< + IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT : + IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT : + IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT : + impl::NUM_TYPE_OTHER>; - return implementation::numberTo<S>(number, TypeTag()); + return impl::numberTo<S>(number, TypeTag()); } template <class Num, class S> inline Num stringTo(const S& str) { - typedef Int2Type< - IsSignedInt <Num>::value ? implementation::NUM_TYPE_SIGNED_INT : - IsUnsignedInt<Num>::value ? implementation::NUM_TYPE_UNSIGNED_INT : - IsFloat <Num>::value ? implementation::NUM_TYPE_FLOATING_POINT : - implementation::NUM_TYPE_OTHER> TypeTag; + using TypeTag = Int2Type< + IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT : + IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT : + IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT : + impl::NUM_TYPE_OTHER>; - return implementation::stringTo<Num>(str, TypeTag()); + return impl::stringTo<Num>(str, TypeTag()); } } -#endif //STRING_TOOLS_HEADER_213458973046 +#endif //STRING_TOOLS_H_213458973046 diff --git a/zen/string_traits.h b/zen/string_traits.h index 61fa2625..12701dc3 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -4,12 +4,13 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STRING_TRAITS_HEADER_813274321443234 -#define STRING_TRAITS_HEADER_813274321443234 +#ifndef STRING_TRAITS_H_813274321443234 +#define STRING_TRAITS_H_813274321443234 #include <cstring> //strlen #include "type_tools.h" + //uniform access to string-like types, both classes and character arrays namespace zen { @@ -42,12 +43,12 @@ public: StringRef(Iterator first, Iterator last) : len_(last - first), str_(first != last ? &*first : nullptr) {} //StringRef(const Char* str, size_t len) : str_(str), len_(len) {} -> needless constraint! Char* not available for empty range! - const Char* data() const { return str_; } //1. no null-termination! 2. may be nullptr! + Char* data () const { return str_; } //1. no null-termination! 2. may be nullptr! size_t length() const { return len_; } private: - size_t len_; - const Char* str_; + const size_t len_; + Char* str_; }; @@ -67,16 +68,14 @@ namespace implementation template<class S, class Char> //test if result of S::c_str() can convert to const Char* class HasConversion { - typedef char Yes[1]; - typedef char No [2]; + using Yes = char[1]; + using No = char[2]; static Yes& hasConversion(const Char*); static No& hasConversion(...); - static S& createInstance(); - public: - enum { value = sizeof(hasConversion(createInstance().c_str())) == sizeof(Yes) }; + enum { value = sizeof(hasConversion(std::declval<S>().c_str())) == sizeof(Yes) }; }; @@ -89,7 +88,7 @@ struct GetCharTypeImpl<S, true> : typename SelectIf<HasConversion<S, char >::value, char, NullType>::Type >::Type> { - //typedef typename S::value_type Type; + //using Type = typename S::value_type; /*DON'T use S::value_type: 1. support Glib::ustring: value_type is "unsigned int" but c_str() returns "const char*" 2. wxString, wxWidgets v2.9, has some questionable string design: wxString::c_str() returns a proxy (wxCStrData) which @@ -100,8 +99,10 @@ struct GetCharTypeImpl<S, true> : template <> struct GetCharTypeImpl<char, false> : ResultType<char > {}; template <> struct GetCharTypeImpl<wchar_t, false> : ResultType<wchar_t> {}; -template <> struct GetCharTypeImpl<StringRef<char >, false> : ResultType<char > {}; -template <> struct GetCharTypeImpl<StringRef<wchar_t>, false> : ResultType<wchar_t> {}; +template <> struct GetCharTypeImpl<StringRef<char >, false> : ResultType<char > {}; +template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> : ResultType<wchar_t> {}; +template <> struct GetCharTypeImpl<StringRef<const char >, false> : ResultType<char > {}; +template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> : ResultType<wchar_t> {}; ZEN_INIT_DETECT_MEMBER_TYPE(value_type); @@ -111,11 +112,11 @@ ZEN_INIT_DETECT_MEMBER(length); // template <class S> class StringTraits { - typedef typename RemoveRef <S >::Type NonRefType; - typedef typename RemoveConst <NonRefType >::Type NonConstType; - typedef typename RemoveArray <NonConstType>::Type NonArrayType; - typedef typename RemovePointer<NonArrayType>::Type NonPtrType; - typedef typename RemoveConst <NonPtrType >::Type UndecoratedType; //handle "const char* const" + using NonRefType = typename RemoveRef <S >::Type; + using NonConstType = typename RemoveConst <NonRefType >::Type; + using NonArrayType = typename RemoveArray <NonConstType>::Type; + using NonPtrType = typename RemovePointer<NonArrayType>::Type; + using UndecoratedType = typename RemoveConst <NonPtrType >::Type ; //handle "const char* const" public: enum @@ -125,7 +126,7 @@ public: HasMember_length <NonConstType>::value }; - typedef typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type CharType; + using CharType = typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type; enum { @@ -171,8 +172,11 @@ inline const char* strBegin(const char* str) { return str; } inline const wchar_t* strBegin(const wchar_t* str) { return str; } inline const char* strBegin(const char& ch) { return &ch; } inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; } -inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); } -inline const wchar_t* strBegin(const StringRef<wchar_t>& ref) { return ref.data(); } + +inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); } +inline const wchar_t* strBegin(const StringRef<wchar_t >& ref) { return ref.data(); } +inline const char* strBegin(const StringRef<const char >& ref) { return ref.data(); } +inline const wchar_t* strBegin(const StringRef<const wchar_t>& ref) { return ref.data(); } template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline @@ -185,8 +189,11 @@ inline size_t strLength(const char* str) { return cStringLength(str); } inline size_t strLength(const wchar_t* str) { return cStringLength(str); } inline size_t strLength(char) { return 1; } inline size_t strLength(wchar_t) { return 1; } -inline size_t strLength(const StringRef<char >& ref) { return ref.length(); } -inline size_t strLength(const StringRef<wchar_t>& ref) { return ref.length(); } + +inline size_t strLength(const StringRef<char >& ref) { return ref.length(); } +inline size_t strLength(const StringRef<wchar_t >& ref) { return ref.length(); } +inline size_t strLength(const StringRef<const char >& ref) { return ref.length(); } +inline size_t strLength(const StringRef<const wchar_t>& ref) { return ref.length(); } } @@ -206,4 +213,4 @@ size_t strLength(S&& str) } } -#endif //STRING_TRAITS_HEADER_813274321443234 +#endif //STRING_TRAITS_H_813274321443234 diff --git a/zen/symlink_target.h b/zen/symlink_target.h index b5ca8191..f50d1806 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef SYMLINK_80178347019835748321473214 -#define SYMLINK_80178347019835748321473214 +#ifndef SYMLINK_TARGET_H_801783470198357483 +#define SYMLINK_TARGET_H_801783470198357483 #include "scope_guard.h" #include "file_error.h" @@ -240,4 +240,4 @@ bool isSymlink(const WIN32_FIND_DATA& data) #endif } -#endif //SYMLINK_80178347019835748321473214 +#endif //SYMLINK_TARGET_H_801783470198357483 diff --git a/zen/thread.h b/zen/thread.h index b10dd342..bb6e7901 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -4,19 +4,23 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STD_THREAD_WRAP_H_7896323423432 -#define STD_THREAD_WRAP_H_7896323423432 +#ifndef THREAD_H_7896323423432235246427 +#define THREAD_H_7896323423432235246427 #include <thread> #include <future> -#include <zen/scope_guard.h> -#include <zen/type_traits.h> +#include "scope_guard.h" +#include "type_traits.h" +#include "optional.h" +#ifdef ZEN_WIN +#include "win.h" +#endif + namespace zen { class InterruptionStatus; - class InterruptibleThread { public: @@ -58,6 +62,9 @@ void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex> template <class Rep, class Period> void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //throw ThreadInterruption +#ifdef ZEN_WIN +void setCurrentThreadName(const char* threadName); +#endif //------------------------------------------------------------------------------------------ /* @@ -72,7 +79,7 @@ Example: //dir exising */ template <class Function> -auto runAsync(Function&& fun) -> std::future<decltype(fun())>; +auto runAsync(Function&& fun); //wait for all with a time limit: return true if *all* results are available! template<class InputIterator, class Duration> @@ -90,18 +97,18 @@ public: GetFirstResult(); template <class Fun> - void addJob(Fun&& f); //f must return a std::unique_ptr<T> containing a value if successful + void addJob(Fun&& f); //f must return a zen::Opt<T> containing a value if successful template <class Duration> bool timedWait(const Duration& duration) const; //true: "get()" is ready, false: time elapsed //return first value or none if all jobs failed; blocks until result is ready! - std::unique_ptr<T> get() const; //may be called only once! + Opt<T> get() const; //may be called only once! private: class AsyncResult; std::shared_ptr<AsyncResult> asyncResult_; - size_t jobsTotal_; + size_t jobsTotal_ = 0; }; //------------------------------------------------------------------------------------------ @@ -111,7 +118,7 @@ template <class T> class Protected { public: - Protected() : value_() {} + Protected() {} Protected(const T& value) : value_(value) {} template <class Function> @@ -126,7 +133,7 @@ private: Protected& operator=(const Protected&) = delete; std::mutex lockValue; - T value_; + T value_{}; }; @@ -142,7 +149,7 @@ private: namespace impl { template <class Function> inline -auto runAsync(Function&& fun, TrueType /*copy-constructible*/) -> std::future<decltype(fun())> +auto runAsync(Function&& fun, TrueType /*copy-constructible*/) { typedef decltype(fun()) ResultType; @@ -155,17 +162,17 @@ auto runAsync(Function&& fun, TrueType /*copy-constructible*/) -> std::future<de template <class Function> inline -auto runAsync(Function&& fun, FalseType /*copy-constructible*/) -> std::future<decltype(fun())> +auto runAsync(Function&& fun, FalseType /*copy-constructible*/) { //support move-only function objects! auto sharedFun = std::make_shared<Function>(std::forward<Function>(fun)); - return runAsync([sharedFun]() { return (*sharedFun)(); }, TrueType()); + return runAsync([sharedFun] { return (*sharedFun)(); }, TrueType()); } } template <class Function> inline -auto runAsync(Function&& fun) -> std::future<decltype(fun())> +auto runAsync(Function&& fun) { return impl::runAsync(std::forward<Function>(fun), StaticBool<std::is_copy_constructible<Function>::value>()); } @@ -187,7 +194,7 @@ class GetFirstResult<T>::AsyncResult { public: //context: worker threads - void reportFinished(std::unique_ptr<T>&& result) + void reportFinished(Opt<T>&& result) { { std::lock_guard<std::mutex> dummy(lockResult); @@ -206,7 +213,7 @@ public: return conditionJobDone.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); }); } - std::unique_ptr<T> getResult(size_t jobsTotal) + Opt<T> getResult(size_t jobsTotal) { std::unique_lock<std::mutex> dummy(lockResult); conditionJobDone.wait(dummy, [&] { return this->jobDone(jobsTotal); }); @@ -226,20 +233,20 @@ private: #endif std::mutex lockResult; - size_t jobsFinished = 0; // - std::unique_ptr<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal" + size_t jobsFinished = 0; // + Opt<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal" std::condition_variable conditionJobDone; }; template <class T> inline -GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()), jobsTotal_(0) {} +GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {} template <class T> template <class Fun> inline -void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::unique_ptr<T> containing a value on success +void GetFirstResult<T>::addJob(Fun&& f) //f must return a zen::Opt<T> containing a value on success { std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); }); ++jobsTotal_; @@ -253,7 +260,7 @@ bool GetFirstResult<T>::timedWait(const Duration& duration) const { return async template <class T> inline -std::unique_ptr<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); } +Opt<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); } //------------------------------------------------------------------------------------------ @@ -263,7 +270,7 @@ std::unique_ptr<T> GetFirstResult<T>::get() const { return asyncResult_->getResu #elif defined __GNUC__ || defined __clang__ #define ZEN_THREAD_LOCAL_SPECIFIER __thread #else - #error "game over" + #error "Game over!" #endif @@ -316,7 +323,7 @@ public: void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption { std::unique_lock<std::mutex> lock(lockSleep); - if (conditionSleepInterruption.wait_for(lock, relTime, [&] { return static_cast<bool>(this->interrupted); })) + if (conditionSleepInterruption.wait_for(lock, relTime, [this] { return static_cast<bool>(this->interrupted); })) throw ThreadInterruption(); } @@ -406,6 +413,39 @@ InterruptibleThread::InterruptibleThread(Function&& f) : intStatus_(std::make_sh inline void InterruptibleThread::interrupt() { intStatus_->interrupt(); } + + +#ifdef ZEN_WIN +//https://randomascii.wordpress.com/2015/10/26/thread-naming-in-windows-time-for-something-better/ + +#pragma pack(push,8) +struct THREADNAME_INFO +{ + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +}; +#pragma pack(pop) + + +inline +void setCurrentThreadName(const char* threadName) +{ +const DWORD MS_VC_EXCEPTION = 0x406D1388; + +THREADNAME_INFO info = {}; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = GetCurrentThreadId(); + + __try + { + ::RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info)); + } + __except(EXCEPTION_EXECUTE_HANDLER){} +} +#endif } -#endif //STD_THREAD_WRAP_H_7896323423432 +#endif //THREAD_H_7896323423432235246427 diff --git a/zen/tick_count.h b/zen/tick_count.h index b5667c6c..647876fb 100644 --- a/zen/tick_count.h +++ b/zen/tick_count.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZEN_TICK_COUNT_HEADER_3807326 -#define ZEN_TICK_COUNT_HEADER_3807326 +#ifndef TICK_COUNT_H_3807326223463457 +#define TICK_COUNT_H_3807326223463457 #include <cstdint> #include "type_traits.h" @@ -13,18 +13,12 @@ #ifdef ZEN_WIN #include "win.h" //includes "windows.h" - #elif defined ZEN_LINUX #include <time.h> //Posix ::clock_gettime() - #elif defined ZEN_MAC #include <mach/mach_time.h> #endif -//template <class T> inline -//T dist(T a, T b) -//{ -// return a > b ? a - b : b - a; -//} + namespace zen { @@ -55,7 +49,7 @@ public: typedef uint64_t NativeVal; #endif - TickVal() : val_() {} + TickVal() {} explicit TickVal(const NativeVal& val) : val_(val) {} inline friend @@ -92,7 +86,7 @@ public: bool isValid() const { return dist(*this, TickVal()) != 0; } private: - NativeVal val_; + NativeVal val_ {}; }; @@ -144,4 +138,4 @@ TickVal getTicks() //return !isValid() on error } } -#endif //ZEN_TICK_COUNT_HEADER_3807326 +#endif //TICK_COUNT_H_3807326223463457 @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZEN_TIME_HEADER_845709281432434 -#define ZEN_TIME_HEADER_845709281432434 +#ifndef TIME_H_8457092814324342453627 +#define TIME_H_8457092814324342453627 #include <ctime> #include "string_tools.h" @@ -13,16 +13,14 @@ namespace zen { -struct TimeComp //replaces "struct std::tm" and SYSTEMTIME +struct TimeComp //replaces std::tm and SYSTEMTIME { - TimeComp() : year(0), month(0), day(0), hour(0), minute(0), second(0) {} - - int year; // - - int month; //1-12 - int day; //1-31 - int hour; //0-23 - int minute; //0-59 - int second; //0-61 + int year = 0; // - + int month = 0; //1-12 + int day = 0; //1-31 + int hour = 0; //0-23 + int minute = 0; //0-59 + int second = 0; //0-60 (including leap second) }; TimeComp localTime (time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components @@ -32,9 +30,9 @@ time_t localToTimeT(const TimeComp& comp); //convert local time com /* format (current) date and time; example: - formatTime<std::wstring>(L"%Y*%m*%d"); -> "2011*10*29" - formatTime<std::wstring>(FORMAT_DATE); -> "2011-10-29" - formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34" + formatTime<std::wstring>(L"%Y|%m|%d"); -> "2011|10|29" + formatTime<std::wstring>(FORMAT_DATE); -> "2011-10-29" + formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34" */ template <class String, class String2> String formatTime(const String2& format, const TimeComp& comp = localTime()); //format as specified by "std::strftime", returns empty string on failure @@ -74,7 +72,7 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp); //simi namespace implementation { inline -struct std::tm toClibTimeComponents(const TimeComp& comp) +std::tm toClibTimeComponents(const TimeComp& comp) { assert(1 <= comp.month && comp.month <= 12 && 1 <= comp.day && comp.day <= 31 && @@ -82,19 +80,21 @@ struct std::tm toClibTimeComponents(const TimeComp& comp) 0 <= comp.minute && comp.minute <= 59 && 0 <= comp.second && comp.second <= 61); - struct std::tm ctc = {}; + std::tm ctc = {}; ctc.tm_year = comp.year - 1900; //years since 1900 ctc.tm_mon = comp.month - 1; //0-11 ctc.tm_mday = comp.day; //1-31 ctc.tm_hour = comp.hour; //0-23 ctc.tm_min = comp.minute; //0-59 - ctc.tm_sec = comp.second; //0-61 + ctc.tm_sec = comp.second; //0-60 (including leap second) ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available + //ctc.tm_wday + //ctc.tm_yday return ctc; } inline -TimeComp toZenTimeComponents(const struct ::tm& ctc) +TimeComp toZenTimeComponents(const std::tm& ctc) { TimeComp comp; comp.year = ctc.tm_year + 1900; @@ -157,21 +157,21 @@ struct GetFormat<FormatIsoDateTimeTag> //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14: // VS 2010: CRASH unless "_invalid_parameter_handler" is set: http://msdn.microsoft.com/en-us/library/ksazx244.aspx // GCC: returns 0, apparently no crash. Still, considering some clib maintainer's comments, we should expect the worst! inline -size_t strftimeWrap_impl(char* buffer, size_t bufferSize, const char* format, const struct std::tm* timeptr) +size_t strftimeWrap_impl(char* buffer, size_t bufferSize, const char* format, const std::tm* timeptr) { return std::strftime(buffer, bufferSize, format, timeptr); } inline -size_t strftimeWrap_impl(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const struct std::tm* timeptr) +size_t strftimeWrap_impl(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const std::tm* timeptr) { return std::wcsftime(buffer, bufferSize, format, timeptr); } /* inline -bool isValid(const struct std::tm& t) +bool isValid(const std::tm& t) { -> not enough! MSCRT has different limits than the C standard which even seem to change with different versions: _VALIDATE_RETURN((( timeptr->tm_sec >=0 ) && ( timeptr->tm_sec <= 59 ) ), EINVAL, FALSE) @@ -194,7 +194,7 @@ bool isValid(const struct std::tm& t) */ template <class CharType> inline -size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const struct std::tm* timeptr) +size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const std::tm* timeptr) { #if defined _MSC_VER && !defined NDEBUG //there's no way around: application init must register an invalid parameter handler that does nothing !!! @@ -215,7 +215,7 @@ template <class String, class String2> inline String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure { typedef typename GetCharType<String>::Type CharType; - struct std::tm ctc = toClibTimeComponents(comp); + std::tm ctc = toClibTimeComponents(comp); std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent @@ -236,10 +236,9 @@ String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag) inline TimeComp localTime(time_t utc) { - struct ::tm lt = {}; + std::tm lt = {}; - //use thread-safe variants of localtime()! -#ifdef ZEN_WIN +#ifdef ZEN_WIN //use thread-safe variants of std::localtime()! if (::localtime_s(<, &utc) != 0) #else if (::localtime_r(&utc, <) == nullptr) @@ -253,7 +252,7 @@ TimeComp localTime(time_t utc) inline time_t localToTimeT(const TimeComp& comp) //returns -1 on error { - struct std::tm ctc = implementation::toClibTimeComponents(comp); + std::tm ctc = implementation::toClibTimeComponents(comp); return std::mktime(&ctc); } @@ -279,36 +278,36 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur typedef typename GetCharType<String>::Type CharType; static_assert(IsSameType<CharType, typename GetCharType<String2>::Type>::value, ""); - const CharType* iterFmt = strBegin(format); - const CharType* const fmtLast = iterFmt + strLength(format); + const CharType* itFmt = strBegin(format); + const CharType* const fmtLast = itFmt + strLength(format); - const CharType* iterStr = strBegin(str); - const CharType* const strLast = iterStr + strLength(str); + const CharType* itStr = strBegin(str); + const CharType* const strLast = itStr + strLength(str); auto extractNumber = [&](int& result, size_t digitCount) -> bool { - if (strLast - iterStr < makeSigned(digitCount)) + if (strLast - itStr < makeSigned(digitCount)) return false; - if (std::any_of(iterStr, iterStr + digitCount, [](CharType c) { return !isDigit(c); })) + if (std::any_of(itStr, itStr + digitCount, [](CharType c) { return !isDigit(c); })) return false; - result = zen::stringTo<int>(StringRef<CharType>(iterStr, iterStr + digitCount)); - iterStr += digitCount; + result = zen::stringTo<int>(StringRef<const CharType>(itStr, itStr + digitCount)); + itStr += digitCount; return true; }; - for (; iterFmt != fmtLast; ++iterFmt) + for (; itFmt != fmtLast; ++itFmt) { - const CharType fmt = *iterFmt; + const CharType fmt = *itFmt; if (fmt == '%') { - ++iterFmt; - if (iterFmt == fmtLast) + ++itFmt; + if (itFmt == fmtLast) return false; - switch (*iterFmt) + switch (*itFmt) { case 'Y': if (!extractNumber(comp.year, 4)) @@ -340,19 +339,19 @@ bool parseTime(const String& format, const String2& str, TimeComp& comp) //retur } else if (isWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars { - while (iterStr != strLast && isWhiteSpace(*iterStr)) - ++iterStr; + while (itStr != strLast && isWhiteSpace(*itStr)) + ++itStr; } else { - if (iterStr == strLast || *iterStr != fmt) + if (itStr == strLast || *itStr != fmt) return false; - ++iterStr; + ++itStr; } } - return iterStr == strLast; + return itStr == strLast; } } -#endif //ZEN_TIME_HEADER_845709281432434 +#endif //TIME_H_8457092814324342453627 diff --git a/zen/type_tools.h b/zen/type_tools.h index 31384d4c..a1e628a1 100644 --- a/zen/type_tools.h +++ b/zen/type_tools.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TYPE_TOOLS_HEADER_45237590734254545 -#define TYPE_TOOLS_HEADER_45237590734254545 +#ifndef TYPE_TOOLS_H_45237590734254545 +#define TYPE_TOOLS_H_45237590734254545 #include "type_traits.h" @@ -100,4 +100,4 @@ template <class Predicate> inline LessDescending<Predicate> makeDescending(Predicate pred) { return pred; } } -#endif //TYPE_TOOLS_HEADER_45237590734254545 +#endif //TYPE_TOOLS_H_45237590734254545 diff --git a/zen/type_traits.h b/zen/type_traits.h index 3ec90f49..ac7f1a42 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -4,11 +4,12 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef TYPE_TRAITS_HEADER_3425628658765467 -#define TYPE_TRAITS_HEADER_3425628658765467 +#ifndef TYPE_TRAITS_H_3425628658765467 +#define TYPE_TRAITS_H_3425628658765467 #include <type_traits> //all we need is std::is_class!! + namespace zen { //################# TMP compile time return values: "inherit to return compile-time result" ############## @@ -21,8 +22,8 @@ struct StaticInt template <bool b> struct StaticBool : StaticInt<b> {}; -typedef StaticBool<true> TrueType; -typedef StaticBool<false> FalseType; +using TrueType = StaticBool<true>; +using FalseType = StaticBool<false>; template <class EnumType, EnumType val> struct StaticEnum @@ -33,15 +34,15 @@ struct StaticEnum template <class T> struct ResultType { - typedef T Type; + using Type = T; }; //Herb Sutter's signedness conversion helpers: http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/ template<class T> inline -typename std::make_signed<T>::type makeSigned(T t) { return static_cast<typename std::make_signed<T>::type>(t); } +typename std::make_signed<T>::type makeSigned(T t) { return static_cast<std::make_signed_t<T>>(t); } template<class T> inline -typename std::make_unsigned<T>::type makeUnsigned(T t) { return static_cast<typename std::make_unsigned<T>::type>(t); } +typename std::make_unsigned<T>::type makeUnsigned(T t) { return static_cast<std::make_unsigned_t<T>>(t); } //################# Built-in Types ######################## //Example: "IsSignedInt<int>::value" evaluates to "true" @@ -136,8 +137,8 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; struct HasMemberImpl_##NAME \ { \ private: \ - typedef char Yes[1]; \ - typedef char No [2]; \ + using Yes = char[1]; \ + using No = char[2]; \ \ template <typename U, U t> \ class Helper {}; \ @@ -165,8 +166,8 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; template<typename U> \ class HasMember_##NAME \ { \ - typedef char Yes[1]; \ - typedef char No [2]; \ + using Yes = char[1]; \ + using No = char[2]; \ \ template <typename T, T t> class Helper {}; \ \ @@ -182,8 +183,8 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; template<typename T> \ class HasMemberType_##TYPENAME \ { \ - typedef char Yes[1]; \ - typedef char No [2]; \ + using Yes = char[1]; \ + using No = char[2]; \ \ template <typename U> class Helper {}; \ \ @@ -194,4 +195,4 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; }; } -#endif //TYPE_TRAITS_HEADER_3425628658765467 +#endif //TYPE_TRAITS_H_3425628658765467 @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef STRING_UTF8_HEADER_01832479146991573473545 -#define STRING_UTF8_HEADER_01832479146991573473545 +#ifndef UTF_H_01832479146991573473545 +#define UTF_H_01832479146991573473545 #include <cstdint> #include <iterator> @@ -455,4 +455,4 @@ TargetString utfCvrtTo(const SourceString& str) } } -#endif //STRING_UTF8_HEADER_01832479146991573473545 +#endif //UTF_H_01832479146991573473545 diff --git a/zen/warn_static.h b/zen/warn_static.h index 3f268ec3..bba17cb7 100644 --- a/zen/warn_static.h +++ b/zen/warn_static.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef WARN_STATIC_HEADER_08724567834560832745 -#define WARN_STATIC_HEADER_08724567834560832745 +#ifndef WARN_STATIC_H_08724567834560832745 +#define WARN_STATIC_H_08724567834560832745 /* Portable Compile-Time Warning @@ -30,4 +30,4 @@ Usage: enum { STATIC_WARNING_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) }; #endif -#endif //WARN_STATIC_HEADER_08724567834560832745 +#endif //WARN_STATIC_H_08724567834560832745 @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef YAWFWH_YET_ANOTHER_WRAPPER_FOR_WINDOWS_H -#define YAWFWH_YET_ANOTHER_WRAPPER_FOR_WINDOWS_H +#ifndef WIN_H_8701570183560183247891346363457 +#define WIN_H_8701570183560183247891346363457 #ifndef _WINSOCKAPI_ //prevent inclusion of winsock.h in windows.h: obsoleted by and conflicting with winsock2.h #define _WINSOCKAPI_ @@ -30,4 +30,4 @@ #endif //------------------------------------------------------ -#endif //YAWFWH_YET_ANOTHER_WRAPPER_FOR_WINDOWS_H +#endif //WIN_H_8701570183560183247891346363457 diff --git a/zen/xml_io.h b/zen/xml_io.h index 16e01aff..4876ac55 100644 --- a/zen/xml_io.h +++ b/zen/xml_io.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef XMLBASE_H_INCLUDED -#define XMLBASE_H_INCLUDED +#ifndef XML_IO_H_8914759321263879 +#define XML_IO_H_8914759321263879 #include <zenxml/xml.h> #include "file_error.h" @@ -23,4 +23,4 @@ void checkForMappingErrors(const XmlIn& xmlInput, const Zstring& filepath); //th void saveXmlDocument(const XmlDoc& doc, const Zstring& filepath); //throw FileError } -#endif // XMLBASE_H_INCLUDED +#endif //XML_IO_H_8914759321263879 diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 3b29e664..59f15f19 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -6,14 +6,10 @@ #include "zstring.h" #include <stdexcept> -#include <unordered_map> #ifdef ZEN_WIN #include "dll.h" - #include "win_ver.h" - -#elif defined ZEN_MAC - #include <ctype.h> //toupper() + //#include "win_ver.h" #endif using namespace zen; @@ -52,7 +48,7 @@ const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<Compa } -int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) +int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen) { assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // @@ -79,7 +75,7 @@ int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen if (minSize == 0) //LCMapString does not allow input sizes of 0! return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); - auto copyToUpperCase = [&](const wchar_t* strIn, wchar_t* strOut) + auto copyToUpperCase = [minSize](const wchar_t* strIn, wchar_t* strOut) { //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString() if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale, @@ -118,43 +114,18 @@ int cmpFilePath(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen } -Zstring makeUpperCopy(const Zstring& str) -{ - const int len = static_cast<int>(str.size()); - - if (len == 0) //LCMapString does not allow input sizes of 0! - return str; - - Zstring output = str; - - //LOCALE_INVARIANT is NOT available with Windows 2000 -> ok - - //use Windows' upper case conversion: faster than ::CharUpper() - if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale, - LCMAP_UPPERCASE, //__in DWORD dwMapFlags, - str.c_str(), //__in LPCTSTR lpSrcStr, - len, //__in int cchSrc, - &*output.begin(), //__out LPTSTR lpDestStr, - len) == 0) //__in int cchDest - throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); - - return output; -} - - -#elif defined ZEN_MAC -Zstring makeUpperCopy(const Zstring& str) +void makeUpperInPlace(wchar_t* str, size_t strLen) { - const size_t len = str.size(); - - Zstring output; - output.resize(len); - - std::transform(str.begin(), str.end(), output.begin(), [](char c) { return static_cast<char>(::toupper(static_cast<unsigned char>(c))); }); //locale-dependent! - - //result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness! - //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves! - - return output; + //- use Windows' upper case conversion: faster than ::CharUpper() + //- LOCALE_INVARIANT is NOT available with Windows 2000 -> ok + //- MSDN: "The destination string can be the same as the source string only if LCMAP_UPPERCASE or LCMAP_LOWERCASE is set" + if (strLen != 0) //LCMapString does not allow input sizes of 0! + if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale, + LCMAP_UPPERCASE, //__in DWORD dwMapFlags, + str, //__in LPCTSTR lpSrcStr, + static_cast<int>(strLen), //__in int cchSrc, + str, //__out LPTSTR lpDestStr, + static_cast<int>(strLen)) == 0) //__in int cchDest + throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); } #endif diff --git a/zen/zstring.h b/zen/zstring.h index 462ea39c..3a431ea7 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -4,15 +4,11 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZSTRING_H_INCLUDED_73425873425789 -#define ZSTRING_H_INCLUDED_73425873425789 +#ifndef ZSTRING_H_73425873425789 +#define ZSTRING_H_73425873425789 #include "string_base.h" -#ifdef ZEN_LINUX - #include <cstring> //strncmp -#endif - #ifdef ZEN_WIN //Windows encodes Unicode as UTF-16 wchar_t typedef wchar_t Zchar; @@ -30,33 +26,33 @@ typedef zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, zen::AllocatorOptimalSpeed> Zstring; +int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen); +#if defined ZEN_LINUX || defined ZEN_MAC + int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen); +#endif + +template <class S, class T> inline +bool equalNoCase(const S& lhs, const T& rhs) { using namespace zen; return cmpStringNoCase(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; } + +template <class S> +S makeUpperCopy(S str); -//Compare filepaths: Windows does NOT distinguish between upper/lower-case, while Linux DOES -int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen); +//Compare filepaths: Windows/OS X does NOT distinguish between upper/lower-case, while Linux DOES +int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen); #if defined ZEN_LINUX || defined ZEN_MAC int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen); #endif +template <class S, class T> inline +bool equalFilePath(const S& lhs, const T& rhs) { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; } - - -struct LessFilePath //case-insensitive on Windows, case-sensitive on Linux +struct LessFilePath { template <class S, class T> bool operator()(const S& lhs, const T& rhs) const { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; } }; -struct EqualFilePath //case-insensitive on Windows, case-sensitive on Linux -{ - template <class S, class T> - bool operator()(const S& lhs, const T& rhs) const { using namespace zen; return cmpFilePath(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0; } -}; - - -#if defined ZEN_WIN || defined ZEN_MAC - Zstring makeUpperCopy(const Zstring& str); -#endif inline @@ -103,38 +99,98 @@ bool pathEndsWith(const S& str, const T& postfix) //################################# inline implementation ######################################## +#ifdef ZEN_WIN +void makeUpperInPlace(wchar_t* str, size_t strLen); -#if defined ZEN_LINUX || defined ZEN_MAC +#elif defined ZEN_LINUX || defined ZEN_MAC inline -int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) +void makeUpperInPlace(wchar_t* str, size_t strLen) +{ + std::for_each(str, str + strLen, [](wchar_t& c) { c = std::towupper(c); }); //locale-dependent! +} + + +inline +void makeUpperInPlace(char* str, size_t strLen) +{ + std::for_each(str, str + strLen, [](char& c) { c = std::toupper(static_cast<unsigned char>(c)); }); //locale-dependent! + //result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness! + //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves! +} + + +inline +int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen) +{ + assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! + assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // + + const int rv = ::wcsncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent! + if (rv != 0) + return rv; + return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); +} + + +inline +int cmpStringNoCase(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) { assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // -#if defined ZEN_LINUX - const int rv = std::strncmp(lhs, rhs, std::min(lhsLen, rhsLen)); -#elif defined ZEN_MAC const int rv = ::strncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent! -#endif if (rv != 0) return rv; return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); } +#endif + + +template <class S> inline +S makeUpperCopy(S str) +{ + const size_t len = str.length(); //we assert S is a string type! + if (len > 0) + makeUpperInPlace(&*str.begin(), len); + + return std::move(str); //"str" is an l-value parameter => no copy elision! +} + inline int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen) { +#if defined ZEN_WIN || defined ZEN_MAC + return cmpStringNoCase(lhs, lhsLen, rhs, rhsLen); + +#elif defined ZEN_LINUX assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // -#if defined ZEN_LINUX const int rv = std::wcsncmp(lhs, rhs, std::min(lhsLen, rhsLen)); -#elif defined ZEN_MAC - const int rv = ::wcsncasecmp(lhs, rhs, std::min(lhsLen, rhsLen)); //locale-dependent! + if (rv != 0) + return rv; + return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); #endif +} + + +#if defined ZEN_LINUX || defined ZEN_MAC +inline +int cmpFilePath(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen) +{ +#if defined ZEN_MAC + return cmpStringNoCase(lhs, lhsLen, rhs, rhsLen); + +#elif defined ZEN_LINUX + assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls! + assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); // + + const int rv = std::strncmp(lhs, rhs, std::min(lhsLen, rhsLen)); if (rv != 0) return rv; return static_cast<int>(lhsLen) - static_cast<int>(rhsLen); +#endif } #endif @@ -172,4 +228,4 @@ int cmpFilePath(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rh #error no target platform defined #endif -#endif //ZSTRING_H_INCLUDED_73425873425789 +#endif //ZSTRING_H_73425873425789 |