You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

840 lines
20 KiB
C

4 years ago
/*! \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 <thread>
#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 <vector>
#include <string>
#include <sstream>
#include <memory>
#include <map>
#include <fstream>
#include <algorithm>
#include <ctime>
#include <cctype>
#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<typename T, typename _Iter>
T sum(_Iter s, _Iter e)
{
if (s == e)
{
return T();
}
T ret = *s;
s++;
while (s != e)
{
ret += *s;
s++;
}
return ret;
}
template<typename T, typename _Iter>
_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<typename T, typename _Iter>
_Iter findSumTopNPercentEle(_Iter s, _Iter e, float nPersent)
{
T sumVal = sum<T, _Iter>(s, e);
T threVal = sumVal*nPersent;
sumVal = 0;
while (s != e)
{
sumVal += *s;
if (sumVal > threVal)
{
s++;
return s;
}
s++;
}
return e;
}
template<typename T>
void clearAndResetVec(vector<T>* vec, int n)
{
if (vec) {
vec->clear();
vec->reserve(n);
}
}
template<typename T>
void genIncVec(vector<T>& 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<typename _It, typename _Ty>
_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<typename _Ty, typename _It>
void add(_It s, _It e, const _Ty& val)
{
while (s != e)
{
*s += val;
s++;
}
}
template<typename _Ty, typename _It>
bool allInRange(_It s, _It e, _Ty minVal, _Ty maxVal)
{
while (s != e)
{
if (*s < minVal || *s > maxVal)
{
return false;
}
++s;
}
return true;
}
template<typename _Ty, typename _It>
bool anyInRange(_It s, _It e, _Ty minVal, _Ty maxVal)
{
while (s != e)
{
if (*s >= minVal && *s <= maxVal)
{
return true;
}
++s;
}
return false;
}
template<typename _Ty, typename _It>
bool anyIn(_It s, _It e, _Ty v)
{
while (s != e)
{
if (*s == v)
{
return true;
}
++s;
}
return false;
}
template<typename _T>
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<typename _PairIter>
_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<typename _Ty0, typename _Ty1>
string joinStr(_Ty0 s0, _Ty1 s1)
{
stringstream ss;
ss << s0 << s1;
return ss.str();
}
template<typename _Ty0, typename _Ty1, typename _Ty2>
string joinStr(_Ty0 s0, _Ty1 s1, _Ty2 s2)
{
stringstream ss;
ss << s0 << s1 << s2;
return ss.str();
}
template<typename _Ty0, typename _Ty1, typename _Ty2, typename _Ty3>
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<string> splitString(const string& str, const string& sep);
string mergeString(const vector<string>& 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<type>(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<type>(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<typename T, typename TPtr,
typename std::enable_if<std::is_base_of<std::shared_ptr<T>, 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<T>();
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<string> getAllEntries() {
CyclopsSharedLockGuard guard(&mLock);
vector<string> ret;
for (auto it = mLookupTable.begin(); it != mLookupTable.end(); ++it) {
ret.push_back(it->first);
}
return ret;
}
protected:
ObjectFactory() {}
virtual ~ObjectFactory() {}
std::map<string, TPtr> 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<T, TPtr>::getInstance().getObject(name);}\
TPtr T::getInstance(const std::string& name) { return getInstance(name.c_str());}\
bool T::deleteInstance(const char* name) { return ObjectFactory<T, TPtr>::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<T, TPtr>::getInstance().updateObject(name, obj); }\
bool T::updateInstance(const std::string& name, TPtr obj) { return updateInstance(name.c_str(), obj); }
template<typename T>
inline T ensureRng(T val, T minVal, T maxVal) {
return std::min<T>(maxVal, std::max<T>(minVal, val));
}
template<typename T>
inline bool inRng(T val, T minVal, T maxVal) {
return val >= minVal && val <= maxVal;
}
template<typename T>
inline void rangeVector(std::vector<T>& 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 <functional>
template <typename T>
vector<size_t> sort_permutation(const vector<T>& vec, std::function<bool(const T&, const T&)> comparefunc, size_t n = 0, size_t startIdx = 0)
{
size_t vecSize = n > 0 ? n : vec.size();
vector<size_t> 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 <typename T>
vector<size_t> sort_permutation(vector<T>& vec, std::function<bool(T&, T&)> comparefunc, size_t n = 0, size_t startIdx = 0)
{
size_t vecSize = n > 0 ? n : vec.size();
vector<size_t> 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 <typename T>
vector<T> apply_permutation(const vector<T>& vec, const vector<size_t>& p)
{
vector<T> sorted_vec(vec.size());
std::transform(p.begin(), p.end(), sorted_vec.begin(),
[&](size_t i) { return vec[i]; });
return sorted_vec;
}
template <typename T>
vector<size_t> sort_permutation_partial(const vector<T>& vec,
std::function<bool(const T&, const T&)> comparefunc, size_t n)
{
size_t vecSize = vec.size();
vector<size_t> 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 <typename T>
vector<T> apply_permutation_partial(const vector<T>& vec, const vector<size_t>& p, size_t n)
{
vector<T> sorted_vec(n);
std::transform(p.begin(), p.begin() + n, sorted_vec.begin(),
[&](size_t i) { return vec[i]; });
return sorted_vec;
}
#endif
template<typename T>
T binarySearch(const T& startVal, const T& endVal, std::function<bool(const T& val)> searchFunc,
std::function<bool(const T& rng, const T& val)> 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<std::string> getAllFilesInFolder(const std::string& folderPath,
const std::vector<std::string>& validExtension);
inline std::vector<std::string> 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<typename T>
class ObjectVectorIterator
{
public:
// Construct the iterator, to make things easier, the iterator will take owner ship of the provide vector.
ObjectVectorIterator(std::vector<T>& 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<T> mVec;
size_t mCurIndex;
};
#if !defined(LITTLE_CPP11)
#include <atomic>
class AsyncResult
{
public:
typedef std::shared_ptr<AsyncResult> Ptr;
typedef std::function<void(AsyncResult*)> 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<int, std::string> mIdNames;
};
#endif
#endif // __StdUtils_h_