279 lines
7.5 KiB
C++
279 lines
7.5 KiB
C++
/**
|
|
* Copyright - See the COPYRIGHT that is included with this distribution.
|
|
* pvxs is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
*/
|
|
#ifndef UTILPVT_H
|
|
#define UTILPVT_H
|
|
|
|
#include "osiSockExt.h"
|
|
|
|
#ifdef _WIN32
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# include <windows.h>
|
|
# include <synchapi.h>
|
|
# include <errhandlingapi.h>
|
|
# include <ws2ipdef.h>
|
|
#else
|
|
# include <pthread.h>
|
|
#endif
|
|
|
|
#include <atomic>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <type_traits>
|
|
#include <limits>
|
|
|
|
#include <event2/util.h>
|
|
|
|
#include <compilerDependencies.h>
|
|
|
|
#include <pvxs/version.h>
|
|
#include <pvxs/util.h>
|
|
|
|
#ifndef EVUTIL_INVALID_SOCKET
|
|
# define EVUTIL_INVALID_SOCKET INVALID_SOCKET
|
|
#endif
|
|
|
|
#ifndef EPICS_ALWAYS_INLINE
|
|
# if __GNUC__
|
|
# define EPICS_ALWAYS_INLINE __inline__ __attribute__((always_inline))
|
|
# else
|
|
# define EPICS_ALWAYS_INLINE inline
|
|
# endif
|
|
#endif
|
|
|
|
#include <epicsThread.h>
|
|
|
|
namespace pvxs {namespace impl {
|
|
|
|
template<typename T>
|
|
struct promote_print { static T op(const T& v) { return v; }};
|
|
template<>
|
|
struct promote_print<int8_t> { static int op(const char& v) { return v; }};
|
|
template<>
|
|
struct promote_print<uint8_t> { static unsigned op(const char& v) { return v; }};
|
|
|
|
/* specialization of bad_alloc which notes the location from which
|
|
* the exception originates.
|
|
*/
|
|
struct PVXS_API loc_bad_alloc final : public std::bad_alloc
|
|
{
|
|
loc_bad_alloc(const char *file, int line);
|
|
virtual ~loc_bad_alloc();
|
|
|
|
virtual const char* what() const noexcept override final;
|
|
|
|
private:
|
|
char msg[64];
|
|
};
|
|
#define BAD_ALLOC() ::pvxs::impl::loc_bad_alloc(__FILE__, __LINE__)
|
|
|
|
//! in-line string builder (eg. for exception messages)
|
|
//! eg. @code throw std::runtime_error(SB()<<"Some message"<<42); @endcode
|
|
struct SB {
|
|
std::ostringstream strm;
|
|
SB() {}
|
|
operator std::string() const { return strm.str(); }
|
|
std::string str() const { return strm.str(); }
|
|
template<typename T>
|
|
SB& operator<<(const T& i) { strm<<i; return *this; }
|
|
};
|
|
|
|
PVXS_API
|
|
void strDiff(std::ostream& out,
|
|
const char *lhs,
|
|
const char *rhs);
|
|
|
|
void threadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC fn, void *arg=nullptr);
|
|
|
|
namespace idetail {
|
|
template <typename I>
|
|
struct Range {
|
|
I a, b;
|
|
|
|
struct iterator {
|
|
typedef std::forward_iterator_tag iterator_category;
|
|
typedef I value_type;
|
|
typedef ptrdiff_t difference_type;
|
|
typedef I* pointer;
|
|
typedef I& reference;
|
|
I val;
|
|
explicit constexpr iterator(I val) :val(val) {}
|
|
EPICS_ALWAYS_INLINE I operator*() const { return val; }
|
|
EPICS_ALWAYS_INLINE iterator& operator++() { val++; return *this; }
|
|
EPICS_ALWAYS_INLINE iterator operator++(int) { return iterator{val++}; }
|
|
EPICS_ALWAYS_INLINE bool operator==(const iterator& o) const { return val==o.val; }
|
|
EPICS_ALWAYS_INLINE bool operator!=(const iterator& o) const { return val!=o.val; }
|
|
};
|
|
|
|
EPICS_ALWAYS_INLINE iterator begin() const { return iterator{a}; }
|
|
EPICS_ALWAYS_INLINE iterator cbegin() const { return begin(); }
|
|
EPICS_ALWAYS_INLINE iterator end() const { return iterator{b}; }
|
|
EPICS_ALWAYS_INLINE iterator cend() const { return end(); }
|
|
};
|
|
} // namespace idetail
|
|
|
|
template<typename I>
|
|
constexpr idetail::Range<I> range(I end) { return idetail::Range<I>{I(0), end}; }
|
|
|
|
template<typename I>
|
|
constexpr idetail::Range<I> range(I begin, I end) { return idetail::Range<I>{begin, end}; }
|
|
|
|
template<typename T>
|
|
T parseTo(const std::string& s); // not implemented
|
|
|
|
template<>
|
|
PVXS_API
|
|
double parseTo<double>(const std::string& s);
|
|
template<>
|
|
PVXS_API
|
|
uint64_t parseTo<uint64_t>(const std::string& s);
|
|
template<>
|
|
PVXS_API
|
|
int64_t parseTo<int64_t>(const std::string& s);
|
|
|
|
#ifdef _WIN32
|
|
# define RWLOCK_TYPE SRWLOCK
|
|
# define RWLOCK_INIT(PLOCK) InitializeSRWLock(PLOCK)
|
|
# define RWLOCK_DTOR(PLOCK) do{(void)(PLOCK);}while(0)
|
|
# define RWLOCK_WLOCK(PLOCK) AcquireSRWLockExclusive(PLOCK)
|
|
# define RWLOCK_WUNLOCK(PLOCK) ReleaseSRWLockExclusive(PLOCK)
|
|
# define RWLOCK_RLOCK(PLOCK) AcquireSRWLockShared(PLOCK)
|
|
# define RWLOCK_RUNLOCK(PLOCK) ReleaseSRWLockShared(PLOCK)
|
|
#else
|
|
# define RWLOCK_TYPE pthread_rwlock_t
|
|
# define RWLOCK_INIT(PLOCK) pthread_rwlock_init(PLOCK, nullptr)
|
|
# define RWLOCK_DTOR(PLOCK) pthread_rwlock_destroy(PLOCK)
|
|
# define RWLOCK_WLOCK(PLOCK) pthread_rwlock_wrlock(PLOCK)
|
|
# define RWLOCK_WUNLOCK(PLOCK) pthread_rwlock_unlock(PLOCK)
|
|
# define RWLOCK_RLOCK(PLOCK) pthread_rwlock_rdlock(PLOCK)
|
|
# define RWLOCK_RUNLOCK(PLOCK) pthread_rwlock_unlock(PLOCK)
|
|
#endif
|
|
|
|
class RWLock
|
|
{
|
|
RWLOCK_TYPE lock;
|
|
public:
|
|
inline RWLock() { RWLOCK_INIT(&lock); }
|
|
inline ~RWLock() { RWLOCK_DTOR(&lock); }
|
|
|
|
RWLock(const RWLock&) = delete;
|
|
RWLock(RWLock&&) = delete;
|
|
RWLock& operator=(const RWLock&) = delete;
|
|
RWLock& operator=(RWLock&&) = delete;
|
|
|
|
struct UnlockReader {
|
|
inline void operator()(RWLock *plock) { RWLOCK_RUNLOCK(&plock->lock); }
|
|
};
|
|
inline std::unique_ptr<RWLock, UnlockReader> lockReader() {
|
|
RWLOCK_RLOCK(&lock);
|
|
return std::unique_ptr<RWLock, UnlockReader>{this};
|
|
}
|
|
|
|
struct UnlockWriter {
|
|
inline void operator()(RWLock *plock) { RWLOCK_WUNLOCK(&plock->lock); }
|
|
};
|
|
inline std::unique_ptr<RWLock, UnlockWriter> lockWriter() {
|
|
RWLOCK_WLOCK(&lock);
|
|
return std::unique_ptr<RWLock, UnlockWriter>{this};
|
|
}
|
|
};
|
|
|
|
#undef RWLOCK_TYPE
|
|
#undef RWLOCK_INIT
|
|
#undef RWLOCK_DTOR
|
|
#undef RWLOCK_WLOCK
|
|
#undef RWLOCK_WUNLOCK
|
|
#undef RWLOCK_RLOCK
|
|
#undef RWLOCK_RUNLOCK
|
|
|
|
PVXS_API
|
|
void osdGetRoles(const std::string& account, std::set<std::string>& roles);
|
|
|
|
void logger_shutdown();
|
|
|
|
// std::max() isn't constexpr until c++14 :(
|
|
constexpr size_t cmax(size_t A, size_t B) {
|
|
return A>B ? A : B;
|
|
}
|
|
|
|
// gcc 4.9 has aligned_storage but not aligned_union
|
|
#if GCC_VERSION && GCC_VERSION<VERSION_INT(4,10,0,0)
|
|
|
|
template<typename... Types>
|
|
struct max_sizeof {
|
|
static const size_t align = 0;
|
|
static const size_t size = 0;
|
|
};
|
|
|
|
template<typename Head, typename... Types>
|
|
struct max_sizeof<Head, Types...> {
|
|
static const size_t align = cmax(alignof(Head), max_sizeof<Types...>::align);
|
|
static const size_t size = cmax(sizeof(Head), max_sizeof<Types...>::size);
|
|
};
|
|
|
|
template <size_t Len, typename... Types>
|
|
struct aligned_union
|
|
{
|
|
using _info = max_sizeof<Types...>;
|
|
|
|
typedef typename std::aligned_storage<cmax(Len, _info::size), _info::align>::type type;
|
|
};
|
|
|
|
#else
|
|
|
|
template <size_t Len, typename... Types>
|
|
using aligned_union = std::aligned_union<Len, Types...>;
|
|
#endif
|
|
|
|
} // namespace impl
|
|
using namespace impl;
|
|
|
|
inline
|
|
timeval totv(double t)
|
|
{
|
|
timeval ret;
|
|
ret.tv_sec = t;
|
|
ret.tv_usec = (t - ret.tv_sec)*1e6;
|
|
return ret;
|
|
}
|
|
|
|
//! Scoped restore of std::ostream state (format flags, fill char, and field width)
|
|
struct Restore {
|
|
std::ostream& strm;
|
|
std::ios_base::fmtflags pflags;
|
|
std::ostream::char_type pfill;
|
|
std::streamsize pwidth;
|
|
Restore(std::ostream& strm)
|
|
:strm(strm)
|
|
,pflags(strm.flags())
|
|
,pfill(strm.fill())
|
|
,pwidth(strm.width())
|
|
{}
|
|
~Restore() {
|
|
strm.flags(pflags);
|
|
strm.fill(pfill);
|
|
strm.width(pwidth);
|
|
}
|
|
};
|
|
|
|
template<std::atomic<size_t>* Cnt>
|
|
struct InstCounter
|
|
{
|
|
InstCounter() {(*Cnt).fetch_add(1, std::memory_order_relaxed);}
|
|
~InstCounter() {(*Cnt).fetch_sub(1, std::memory_order_relaxed);}
|
|
};
|
|
|
|
#define INST_COUNTER(KLASS) InstCounter<&cnt_ ## KLASS> instances
|
|
|
|
#define CASE(KLASS) PVXS_API extern std::atomic<size_t> cnt_ ## KLASS
|
|
#include "instcounters.h"
|
|
#undef CASE
|
|
|
|
} // namespace pvxs
|
|
|
|
#endif // UTILPVT_H
|