/*! \file StdUtils.h \brief useful functions working with std functions. Created: 2015/06/22, author: Jin Bingwen. */ #ifndef __StdUtils_h_ #define __StdUtils_h_ #if (defined(_MSC_VER) && _MSC_VER <= 1600) #define LITTLE_CPP11 1 #else #include #endif #if (defined _WINDOWS) || (defined WIN32) #define USE_WIN_API 1 #endif #if defined(__GNUC__) || defined(__clang__) #define DEPRECATED __attribute__((deprecated)) #elif defined(_MSC_VER) #define DEPRECATED __declspec(deprecated) #else #pragma message("WARNING: You need to implement DEPRECATED for this compiler") #define DEPRECATED #endif #if _WIN64 || __x86_64__ || __ppc64__ #define CYCLOPS_64 1 #endif #include "CyclopsVersion.h" #include "CyclopsLock.h" #include #include #include #include #include #include #include #include #include #include "Asserte.h" using std::string; using std::vector; using std::stringstream; #if defined(LITTLE_CPP11) #define GET_SIGN(x) (x < 0 ? true : false) #else #define GET_SIGN(x) std::signbit(x) #endif inline unsigned int bitCount(unsigned int u) { unsigned int uCount; uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111); return ((uCount + (uCount >> 3)) & 030707070707) % 63; } template T sum(_Iter s, _Iter e) { if (s == e) { return T(); } T ret = *s; s++; while (s != e) { ret += *s; s++; } return ret; } template _Iter findTopNPercentEle(_Iter s, _Iter e, float nPersent) { auto maxVal = std::max_element(s, e); T threVal = (*maxVal)*(1.0f - nPersent); while (s != e) { if (*s < threVal) { return s; } ++s; } return s; } template _Iter findSumTopNPercentEle(_Iter s, _Iter e, float nPersent) { T sumVal = sum(s, e); T threVal = sumVal*nPersent; sumVal = 0; while (s != e) { sumVal += *s; if (sumVal > threVal) { s++; return s; } s++; } return e; } template void clearAndResetVec(vector* vec, int n) { if (vec) { vec->clear(); vec->reserve(n); } } template void genIncVec(vector& vec, T start, int count, T step) { for (int i = 0; i < count; ++i) { vec.push_back(start); start += step; } } class SortEle { public: SortEle() : mSortVal(0), pEle(NULL) {} SortEle(double val, const void* pData) : mSortVal(val), pEle(pData) {} double mSortVal; const void* pEle; bool operator< (const SortEle& i) { return mSortVal < i.mSortVal; } bool operator>(const SortEle& i) { return mSortVal > i.mSortVal; } SortEle operator+ (const SortEle& i) { return SortEle(mSortVal + i.mSortVal, pEle); } void operator+= (const SortEle& i) { mSortVal += i.mSortVal; } SortEle operator- (const SortEle& i) { return SortEle(mSortVal - i.mSortVal, pEle); } operator float() { return (float)mSortVal; } operator double() { return mSortVal; } SortEle operator* (float f) { return SortEle(mSortVal*f, pEle); } void operator= (float f) { mSortVal = f; } }; template _It findRange(_It s, _It e, const _Ty& val) { if (s == e) { return s; } _It mi = (e - s) / 2 + s; if (val < *mi) { return findRange(s, mi, val); } else if (val > *mi) { return findRange(mi + 1, e, val); } else { return mi; } } template void add(_It s, _It e, const _Ty& val) { while (s != e) { *s += val; s++; } } template bool allInRange(_It s, _It e, _Ty minVal, _Ty maxVal) { while (s != e) { if (*s < minVal || *s > maxVal) { return false; } ++s; } return true; } template bool anyInRange(_It s, _It e, _Ty minVal, _Ty maxVal) { while (s != e) { if (*s >= minVal && *s <= maxVal) { return true; } ++s; } return false; } template bool anyIn(_It s, _It e, _Ty v) { while (s != e) { if (*s == v) { return true; } ++s; } return false; } template bool loadAValueFromFile(string filePath, _T& ret) { std::fstream fs; fs.open(filePath, std::fstream::in); if (!fs.is_open()) { return false; } fs >> ret; fs.close(); } // search range is [si, ei), not include ei template _PairIter max_first_element(_PairIter si, _PairIter ei) { if (si == ei) { return ei; } // exclude ei _PairIter ret = --ei; ei++; auto maxVal = si->first; si++; while (si != ei) { if (maxVal < si->first) { maxVal = si->first; ret = si; } ++si; } return ret; } template string joinStr(_Ty0 s0, _Ty1 s1) { stringstream ss; ss << s0 << s1; return ss.str(); } template string joinStr(_Ty0 s0, _Ty1 s1, _Ty2 s2) { stringstream ss; ss << s0 << s1 << s2; return ss.str(); } template string joinStr(_Ty0 s0, _Ty1 s1, _Ty2 s2, _Ty3 s3) { stringstream ss; ss << s0 << s1 << s2 << s3; return ss.str(); } string toLower(const string& str); string toUpper(const string& str); vector splitString(const string& str, const string& sep); string mergeString(const vector& strVec, const string& sep); #define _DECLARE_PARAMETER_MEM(type, name)\ protected:\ type m##name; #define _DECLARE_PARAMETER_GETFUN(type, name)\ public:\ type get##name() const { return m##name; } #define _DECLARE_PARAMETER_SETFUN(type, name)\ public:\ void set##name(type val) { m##name = val; } #define _DECLARE_PARAMETER_SETFUN2(type, name, val1, val2)\ public:\ void set##name(type val) {\ _ASSERTE(val >= val1 && val <= val2);\ if (val >= val1 && val <= val2) m##name = val; } #define _DECLARE_PARAMETER_SETENUM(type, name)\ public:\ void set##name(type val) { m##name = val; }\ void set##name(int val) {\ set##name(static_cast(val)); } #define _DECLARE_PARAMETER_SETENUM2(type, name, val1, val2)\ public:\ void set##name(type val) {\ _ASSERTE(val >= val1 && val <= val2);\ if (val >= val1 && val <= val2) m##name = val; }\ void set##name(int val) {\ set##name(static_cast(val)); } #define _DECLARE_PARAMETER_SETPAIR(type, name)\ public:\ void set##name(type val1, type val2) {\ if (val1 > val2) { m##name##Start = val2; m##name##End = val1; }\ else { m##name##Start = val1; m##name##End = val2; }\ } #define _DECLARE_PARAMETER_SETPAIR2(type, name, val1, val2)\ public:\ void set##name(type value1, type value2) {\ _ASSERTE(value1 >= val1 && value1 <= val2 && value2 >= val1 && value2 <= val2);\ if (value1 >= val1 && value1 <= val2 && value2 >= val1 && value2 <= val2) {\ if (value1 > value2) { m##name##Start = value2; m##name##End = value1; }\ else { m##name##Start = value1; m##name##End = value2; }\ }\ } #define DECLARE_PARAMETER(type, name)\ _DECLARE_PARAMETER_MEM(type, name)\ _DECLARE_PARAMETER_GETFUN(type, name)\ _DECLARE_PARAMETER_SETFUN(type, name) #define DECLARE_PARAMETER2(type, name, val1, val2)\ _DECLARE_PARAMETER_MEM(type, name)\ _DECLARE_PARAMETER_GETFUN(type, name)\ _DECLARE_PARAMETER_SETFUN2(type, name, val1 , val2) #define DECLARE_PARAMETER_SET(type, name)\ _DECLARE_PARAMETER_MEM(type, name)\ _DECLARE_PARAMETER_SETFUN(type, name) #define DECLARE_PARAMETER_SET2(type, name, val1, val2)\ _DECLARE_PARAMETER_MEM(type, name)\ _DECLARE_PARAMETER_SETFUN2(type, name, val1, val2) #define DECLARE_PARAMETER_GET(type, name)\ _DECLARE_PARAMETER_MEM(type, name)\ _DECLARE_PARAMETER_GETFUN(type, name) #define DECLARE_PARAMETER_ENUM(type, name)\ _DECLARE_PARAMETER_MEM(type, name)\ _DECLARE_PARAMETER_GETFUN(type, name)\ _DECLARE_PARAMETER_SETENUM(type, name) #define DECLARE_PARAMETER_ENUM2(type, name, val1, val2)\ _DECLARE_PARAMETER_MEM(type, name)\ _DECLARE_PARAMETER_GETFUN(type, name)\ _DECLARE_PARAMETER_SETENUM2(type, name, val1, val2) #define DECLARE_PARAMETER_PAIR(type, name)\ _DECLARE_PARAMETER_MEM(type, name##Start)\ _DECLARE_PARAMETER_MEM(type, name##End)\ _DECLARE_PARAMETER_GETFUN(type, name##Start)\ _DECLARE_PARAMETER_GETFUN(type, name##End)\ _DECLARE_PARAMETER_SETPAIR(type, name) #define DECLARE_PARAMETER_PAIR2(type, name, val1, val2)\ _DECLARE_PARAMETER_MEM(type, name##Start)\ _DECLARE_PARAMETER_MEM(type, name##End)\ _DECLARE_PARAMETER_GETFUN(type, name##Start)\ _DECLARE_PARAMETER_GETFUN(type, name##End)\ _DECLARE_PARAMETER_SETPAIR2(type, name, val1, val2) // Declare the provide class as a singleton. // Get instance via Class::getInstance(). // // Note: according to C++11 standard, static object initialization will // be made only by one thread, other threads will wait till it complete. // start from VS2014, this macro is thread-safe. #define DECLARE_SINGLETON(type, ...)\ public:\ static type& getInstance() {\ static type inst(__VA_ARGS__);\ return inst;\ } // Declare the provide class as a singleton. // Get instance via Class::getInstance(). // // Note: according to C++11 standard, static object initialization will // be made only by one thread, other threads will wait till it complete. // start from VS2014, this macro is thread-safe. #define DECLARE_SINGLETON_NOPARA(type)\ public:\ static type& getInstance() {\ static type inst;\ return inst;\ } // A simple version of object factory that use object name as key and hold object instances. // It's thread-safe. // Note: factory own the object instance, aka. own the object instance's memory, which means it will // deallocate the memory when it self is destroyed (when the who application is shutdown). // You don't need to delete the object instance yourself, and even worse, it will cause the double-delete crash. // Use DECL_GET_INSTANCE to define getInstance() functions inside your class, and also IMPL_GET_INSTANCE in cpp. template, TPtr>::value>::type* = nullptr> class ObjectFactory { DECLARE_SINGLETON_NOPARA(ObjectFactory) public: TPtr getObject(const char* name) { CyclopsLockGuard guard(&mLock); auto it = mLookupTable.find(name); if (it == mLookupTable.end()) { // create new TPtr ptr = std::make_shared(); it = mLookupTable.insert(std::make_pair(name, ptr)).first; } return it->second; } TPtr getObject(const std::string& name) { return getObject(name.c_str()); } bool deleteObject(const char* name) { CyclopsLockGuard guard(&mLock); auto it = mLookupTable.find(name); if (it == mLookupTable.end()) return false; mLookupTable.erase(it); return true; } bool deleteObject(const std::string& name) { return deleteObject(name.c_str()); } bool updateObject(const char* name, TPtr obj) { CyclopsLockGuard guard(&mLock); mLookupTable[name] = obj; return true; } bool updateObject(const std::string& name, TPtr obj) { return updateObject(name.c_str(), obj); } bool isEmpty() { CyclopsSharedLockGuard guard(&mLock); return mLookupTable.empty(); } vector getAllEntries() { CyclopsSharedLockGuard guard(&mLock); vector ret; for (auto it = mLookupTable.begin(); it != mLookupTable.end(); ++it) { ret.push_back(it->first); } return ret; } protected: ObjectFactory() {} virtual ~ObjectFactory() {} std::map mLookupTable; CyclopsLock mLock; }; #define DECL_GET_INSTANCE(TPtr)\ public:\ static TPtr getInstance(const char* name);\ static TPtr getInstance(const std::string& name);\ static bool deleteInstance(const char* name);\ static bool deleteInstance(const std::string& name);\ static bool updateInstance(const char* name, TPtr nobj);\ static bool updateInstance(const std::string& name, TPtr nobj); #define IMPL_GET_INSTANCE(T, TPtr)\ TPtr T::getInstance(const char* name) { return ObjectFactory::getInstance().getObject(name);}\ TPtr T::getInstance(const std::string& name) { return getInstance(name.c_str());}\ bool T::deleteInstance(const char* name) { return ObjectFactory::getInstance().deleteObject(name); }\ bool T::deleteInstance(const std::string& name) { return deleteInstance(name.c_str()); }\ bool T::updateInstance(const char* name, TPtr obj) { return ObjectFactory::getInstance().updateObject(name, obj); }\ bool T::updateInstance(const std::string& name, TPtr obj) { return updateInstance(name.c_str(), obj); } template inline T ensureRng(T val, T minVal, T maxVal) { return std::min(maxVal, std::max(minVal, val)); } template inline bool inRng(T val, T minVal, T maxVal) { return val >= minVal && val <= maxVal; } template inline void rangeVector(std::vector& vec, size_t n, size_t startIdx = 0) { vec.reserve(n); size_t endIdx = startIdx + n; for (size_t i = startIdx; i < endIdx; ++i) vec.push_back((T)i); } #if !defined(LITTLE_CPP11) #include template vector sort_permutation(const vector& vec, std::function comparefunc, size_t n = 0, size_t startIdx = 0) { size_t vecSize = n > 0 ? n : vec.size(); vector p; if (vecSize == 0) return p; rangeVector(p, vecSize, startIdx); std::sort(p.begin(), p.end(), [&](size_t i, size_t j) { return comparefunc(vec[i], vec[j]); }); return p; } template vector sort_permutation(vector& vec, std::function comparefunc, size_t n = 0, size_t startIdx = 0) { size_t vecSize = n > 0 ? n : vec.size(); vector p; if (vecSize == 0) return p; rangeVector(p, vecSize, startIdx); std::sort(p.begin(), p.end(), [&](size_t i, size_t j) { return comparefunc(vec[i], vec[j]); }); return p; } template vector apply_permutation(const vector& vec, const vector& p) { vector sorted_vec(vec.size()); std::transform(p.begin(), p.end(), sorted_vec.begin(), [&](size_t i) { return vec[i]; }); return sorted_vec; } template vector sort_permutation_partial(const vector& vec, std::function comparefunc, size_t n) { size_t vecSize = vec.size(); vector p(vecSize); for (int i = 0; i < vecSize; ++i) p[i] = i; std::partial_sort(p.begin(), p.begin() + n, p.end(), [&](size_t i, size_t j) { return comparefunc(vec[i], vec[j]); }); return p; } template vector apply_permutation_partial(const vector& vec, const vector& p, size_t n) { vector sorted_vec(n); std::transform(p.begin(), p.begin() + n, sorted_vec.begin(), [&](size_t i) { return vec[i]; }); return sorted_vec; } #endif template T binarySearch(const T& startVal, const T& endVal, std::function searchFunc, std::function stopFunc) { // startVal -> i -> endVal T i = startVal, _startVal = startVal, _endVal = endVal; do { bool ret = searchFunc(i); T rng; if (_startVal == i || ret) { // converge towards endVal _startVal = i; i = (i + _endVal) / 2.; rng = _startVal - i; } else { // converge towards startVal _endVal = i; i = (i + _startVal) / 2.; rng = i - _endVal; } if (stopFunc(rng, i)) { break; } } while (true); return i; } #if !defined(LITTLE_CPP11) CYCLOPS_UTILS_SPEC std::string getCurTimeAsFileName(); std::vector getAllFilesInFolder(const std::string& folderPath, const std::vector& validExtension); inline std::vector getAllImagesInFolder(const std::string& folderPath) { return getAllFilesInFolder(folderPath, { ".png", ".bmp", ".jpg", ".jpeg" }); } std::string getFolderName(const std::string& folderPath); std::string getFileName(const std::string& filePath, bool withExtension = true); std::string getFileExtension(const std::string& filePath); inline bool isImagePath(const std::string& filePath) { string ext = getFileExtension(filePath); return ext == ".png" || ext == ".bmp" || ext == ".jpg" || ext == ".jpeg"; } std::string makePathPreferred(const std::string& p); bool isSamePath(const std::string& p1, const std::string& p2); #endif template class ObjectVectorIterator { public: // Construct the iterator, to make things easier, the iterator will take owner ship of the provide vector. ObjectVectorIterator(std::vector& takeVec) { mVec = std::move(takeVec); toFront(); } ObjectVectorIterator(const ObjectVectorIterator& other) { mVec = std::move(other.mVec); mCurIndex = other.mCurIndex; } // Returns true if there is at least one item ahead of the iterator inline bool hasNext() const { return (!mVec.empty()) && ((mCurIndex == -1) || (mCurIndex < (mVec.size() - 1))); } inline bool hasPrevious() const { return (mCurIndex >= 1) && (mCurIndex <= mVec.size()); } inline T& next() { return mVec.at(++mCurIndex); } inline T& previous() { return mVec.at(--mCurIndex); } // Moves the iterator to the front of the container (before the first item). inline void toFront() { mCurIndex = -1; } // Moves the iterator to the back of the container (after the last item). inline void toBack() { mCurIndex = mVec.size(); } // Moves the iterator to the middle of the container inline void jumpTo(int idx) { mCurIndex = ensureRng(idx - 1, -1, mVec.size()); } // Return total size inline size_t size() const { return mVec.size(); } // Get current index inline int currentIndex() const { return mCurIndex; } private: std::vector mVec; size_t mCurIndex; }; #if !defined(LITTLE_CPP11) #include class AsyncResult { public: typedef std::shared_ptr Ptr; typedef std::function AsyncFunc; enum { AsyncNotStart = 9999, AsyncRunning = 8888, }; AsyncResult(AsyncFunc func, int total = 0) : mFunc(func), mLock(), mSucceed(AsyncNotStart), mStopEarly(false), mTotal(0), mProgress(0) {} ~AsyncResult() { if (mThread.joinable()) mThread.detach(); } virtual void start() { if (!mThread.joinable() && mSucceed == AsyncNotStart && mTotal == 0) { CyclopsLockGuard write_gaurd(&mLock); mSucceed = AsyncRunning; mThread = std::thread(mFunc, this); // start if not start yet } } virtual float getStatus() { if (!mThread.joinable() || (mSucceed != AsyncNotStart && mSucceed != AsyncRunning)) return 1; // thread is finished or not started if (mSucceed == AsyncNotStart) { return 0; // not started, or not running } else if (mSucceed == AsyncRunning) { // running, calculate process percentage if (mTotal <= 0) return 0; int c = count(); return c == mTotal ? 0.99 // waiting for the succeed flag : (float)(c) / mTotal; } // either succeed or fail return 1; } virtual int count() { return mProgress; } void waitForDone() { if (mThread.joinable() && mSucceed == AsyncRunning) { mThread.join(); } } void stop() { mStopEarly = true; } bool isStopped() const { return mStopEarly; } bool succeeded() const { return mSucceed > 0; } int errorCode() const { return mSucceed; } void setProgress(int val) { mProgress = val; } std::string getNameById(int id) { CyclopsSharedLockGuard gaurd(&mLock); auto it = mIdNames.find(id); if (it != mIdNames.end()) { return it->second; } return std::string(); } int getIdByName(const std::string& name) { CyclopsSharedLockGuard gaurd(&mLock); for (auto it = mIdNames.begin(); it != mIdNames.end(); ++it) { if (it->second == name) { return it->first; } } return -1; } protected: // for AsyncFunc to set succeed code when finished // AsyncNotStart and AsyncRunning is reserved for status control // > 0 for succeed, -1 for failure and error code virtual void setSucceed(int val) { mSucceed = val; _ASSERTE(count() <= mTotal); } virtual void registerTargetName(int id, const std::string& name) { CyclopsLockGuard write_gaurd(&mLock); mIdNames[id] = name; } protected: std::thread mThread; std::atomic_int mSucceed; std::atomic_bool mStopEarly; CyclopsLock mLock; std::atomic_int mTotal; // total number of tests std::atomic_int mProgress; // simple implementation of async result AsyncFunc mFunc; std::map mIdNames; }; #endif #endif // __StdUtils_h_