diff options
Diffstat (limited to 'compat')
48 files changed, 1430 insertions, 844 deletions
diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt index b58b54e9..dc81436d 100644 --- a/compat/CMakeLists.txt +++ b/compat/CMakeLists.txt @@ -1,14 +1,9 @@ otr_module(compat NO-COMPAT BIN) if(NOT WIN32 AND NOT APPLE) - target_link_libraries(opentrack-compat rt) + target_link_libraries(${self} rt) endif() if(WIN32) - target_link_libraries(opentrack-compat winmm strmiids) -endif() - -if(CMAKE_COMPILER_IS_GNUCXX) - set_property(SOURCE nan.cpp APPEND_STRING PROPERTY - COMPILE_FLAGS "-fno-lto -fno-fast-math -fno-finite-math-only -O0 ") + target_link_libraries(${self} strmiids) endif() diff --git a/compat/activation-context.cpp b/compat/activation-context.cpp new file mode 100644 index 00000000..8d34243d --- /dev/null +++ b/compat/activation-context.cpp @@ -0,0 +1,51 @@ +#ifdef _WIN32 + +#include "activation-context.hpp" +#include "compat/library-path.hpp" + +#include <QString> +#include <QFile> +#include <QDebug> + +#include <windows.h> + +static_assert(sizeof(std::uintptr_t) == sizeof(ULONG_PTR)); + +activation_context::activation_context(const QString& module_name, int resid) +{ + static const QString prefix = OPENTRACK_BASE_PATH + OPENTRACK_LIBRARY_PATH + OPENTRACK_LIBRARY_PREFIX; + QString path = prefix + module_name; + QByteArray name = QFile::encodeName(path); + + ACTCTXA actx = {}; + actx.cbSize = sizeof(actx); + actx.lpResourceName = MAKEINTRESOURCEA(resid); + actx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID; + actx.lpSource = name.constData(); + + handle = CreateActCtxA(&actx); + + if (handle != INVALID_HANDLE_VALUE) + { + if (!ActivateActCtx(handle, (ULONG_PTR*)&cookie)) + { + qDebug() << "win32: can't set activation context" << GetLastError(); + ReleaseActCtx(handle); + handle = INVALID_HANDLE_VALUE; + } + else + ok = true; + } else { + qDebug() << "win32: can't create activation context" << GetLastError(); + } +} + +activation_context::~activation_context() +{ + if (handle != INVALID_HANDLE_VALUE) + { + DeactivateActCtx(0, cookie); + ReleaseActCtx(handle); + } +} +#endif diff --git a/compat/activation-context.hpp b/compat/activation-context.hpp new file mode 100644 index 00000000..a3b0429e --- /dev/null +++ b/compat/activation-context.hpp @@ -0,0 +1,26 @@ +#pragma once + +#ifdef _WIN32 + +#include "export.hpp" + +#include <cstdint> +#include <QString> + +class OTR_COMPAT_EXPORT activation_context +{ +public: + explicit activation_context(const QString& module_name, int resid); + ~activation_context(); + + explicit operator bool() const { return ok; } + +private: + std::uintptr_t cookie = 0; + void* handle = (void*)-1; + bool ok = false; +}; + +#else +# error "tried to use win32-only activation context" +#endif diff --git a/compat/arch.hpp b/compat/arch.hpp new file mode 100644 index 00000000..3120116a --- /dev/null +++ b/compat/arch.hpp @@ -0,0 +1,50 @@ +#pragma once + +// fix MSVC arch check macros + +// this file is too simple to fall under copyright, and +// can be copied, modified, and redistributed freely with +// no conditions. there's no warranty. -sh 20181226 + +#if defined _MSC_VER +# ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wreserved-id-macro" +# pragma clang diagnostic ignored "-Wunused-macros" +# endif + +# if defined _M_AMD64 +# undef __x86_64__ +# define __x86_64__ 1 +# elif defined _M_IX86 +# undef __i386__ +# define __i386__ 1 +# endif + +# if defined __AVX__ || defined __x86_64__ || \ + defined _M_IX86 && _M_IX86_FP >= 2 +# undef __SSE__ +# undef __SSE2__ +# undef __SSE3__ +# define __SSE__ 1 +# define __SSE2__ 1 +# define __SSE3__ 1 // assume SSE3 in the _M_IX86_FP >= 2 case +# endif + +# ifdef __clang__ +# pragma clang diagnostic pop +# endif +#endif + +#if defined __SSE3__ +# define OTR_ARCH_DENORM_DAZ +# include <pmmintrin.h> +#elif defined __SSE2__ +# define OTR_ARCH_DENORM_FTZ +# include <emmintrin.h> +#endif + +#if defined __SSE2__ +# define OTR_ARCH_FPU_MASK +# include <xmmintrin.h> +#endif diff --git a/compat/base-path.cpp b/compat/base-path.cpp index 3285bac8..2a26dcff 100644 --- a/compat/base-path.cpp +++ b/compat/base-path.cpp @@ -1,10 +1,12 @@ +#undef NDEBUG +#include <cassert> + #include "base-path.hpp" #include <QCoreApplication> -OTR_COMPAT_EXPORT -never_inline const QString& application_base_path() { - static QString const& const_path = QCoreApplication::applicationDirPath(); - return const_path; + assert(qApp && "logic error"); + static QString path = QCoreApplication::applicationDirPath(); + return path; } diff --git a/compat/base-path.hpp b/compat/base-path.hpp index 8e2884fd..7a9d4115 100644 --- a/compat/base-path.hpp +++ b/compat/base-path.hpp @@ -1,12 +1,11 @@ #pragma once -#include "macros.hpp" #include "export.hpp" #include <QString> OTR_COMPAT_EXPORT -never_inline const QString& application_base_path(); #define OPENTRACK_BASE_PATH (application_base_path()) + diff --git a/compat/camera-names.cpp b/compat/camera-names.cpp index 22dcdd8f..b9511037 100644 --- a/compat/camera-names.cpp +++ b/compat/camera-names.cpp @@ -1,95 +1,130 @@ #include "camera-names.hpp" +#include <algorithm> +#include <iterator> + #ifdef _WIN32 +# include <cwchar> # define NO_DSHOW_STRSAFE # include <dshow.h> -# include <cwchar> -#elif defined(__unix) || defined(__linux) || defined(__APPLE__) +#elif defined(__unix) || defined(__linux__) || defined(__APPLE__) # include <unistd.h> #endif -#ifdef __linux +#ifdef __APPLE__ +# include <QCameraInfo> +#endif + +#ifdef __linux__ # include <fcntl.h> # include <sys/ioctl.h> # include <linux/videodev2.h> # include <cerrno> +# include <cstring> #endif #include <QDebug> -OTR_COMPAT_EXPORT int camera_name_to_index(const QString &name) +int camera_name_to_index(const QString &name) { auto list = get_camera_names(); - int ret = list.indexOf(name); - if (ret < 0) - ret = 0; + auto it = std::find_if(list.cbegin(), list.cend(), [&name](const auto& tuple) { + const auto& [str, idx] = tuple; + return str == name; + }); + if (it != list.cend()) + { + const auto& [ str, idx ] = *it; + return idx; + } + + return -1; +} + +#ifdef _WIN32 +# include <QRegularExpression> + +static QString prop_to_qstring(IPropertyBag* pPropBag, const wchar_t* name) +{ + QString ret{}; + VARIANT var; + VariantInit(&var); + HRESULT hr = pPropBag->Read(name, &var, nullptr); + if (SUCCEEDED(hr)) + ret = QString{(const QChar*)var.bstrVal, int(std::wcslen(var.bstrVal))}; + VariantClear(&var); return ret; } -OTR_COMPAT_EXPORT QList<QString> get_camera_names() +static QString device_path_from_qstring(const QString& str) +{ + // language=RegExp prefix=R"/( suffix=)/" + static const QRegularExpression regexp{R"/(#vid_([0-9a-f]{4})&pid_([0-9a-f]{4})&mi_([0-9a-f]{2})#([^#]+))/", + QRegularExpression::CaseInsensitiveOption}; + auto match = regexp.match(str); + if (!match.hasMatch()) + return {}; + QString id = match.captured(4); + id.replace('&', '_'); + return id; +} + +#endif + +std::vector<std::tuple<QString, int>> get_camera_names() { - QList<QString> ret; -#if defined(_WIN32) + std::vector<std::tuple<QString, int>> ret; +#ifdef _WIN32 // Create the System Device Enumerator. HRESULT hr; CoInitialize(nullptr); - ICreateDevEnum *pSysDevEnum = NULL; - hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum); + ICreateDevEnum *pSysDevEnum = nullptr; + hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum); if (FAILED(hr)) { qDebug() << "failed CLSID_SystemDeviceEnum" << hr; return ret; } // Obtain a class enumerator for the video compressor category. - IEnumMoniker *pEnumCat = NULL; + IEnumMoniker *pEnumCat = nullptr; hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0); if (hr == S_OK) { // Enumerate the monikers. - IMoniker *pMoniker = NULL; + IMoniker *pMoniker = nullptr; ULONG cFetched; while (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) { IPropertyBag *pPropBag; - hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); - if (SUCCEEDED(hr)) { + hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void **)&pPropBag); + if (SUCCEEDED(hr)) { // To retrieve the filter's friendly name, do the following: - VARIANT varName; - VariantInit(&varName); - hr = pPropBag->Read(L"FriendlyName", &varName, 0); if (SUCCEEDED(hr)) { - // Display the name in your UI somehow. - QString str((QChar*)varName.bstrVal, int(std::wcslen(varName.bstrVal))); - ret.append(str); + QString str = prop_to_qstring(pPropBag, L"FriendlyName"); + QString path = device_path_from_qstring(prop_to_qstring(pPropBag, L"DevicePath")); + if (!path.isNull()) + str += QStringLiteral(" [%1]").arg(path); + ret.push_back({ str, (int)ret.size() }); } - VariantClear(&varName); - - ////// To create an instance of the filter, do the following: - ////IBaseFilter *pFilter; - ////hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, - //// (void**)&pFilter); - // Now add the filter to the graph. - //Remember to release pFilter later. pPropBag->Release(); } pMoniker->Release(); } pEnumCat->Release(); } +#if 0 else qDebug() << "failed CLSID_VideoInputDeviceCategory" << hr; +#endif pSysDevEnum->Release(); #endif -#ifdef __linux + +#ifdef __linux__ for (int i = 0; i < 16; i++) { - char buf[128]; - sprintf(buf, "/dev/video%d", i); - if (access(buf, F_OK) == 0) - ret.append(buf); - else - continue; + char buf[32]; + snprintf(buf, sizeof(buf), "/dev/video%d", i); if (access(buf, R_OK | W_OK) == 0) { int fd = open(buf, O_RDONLY); @@ -102,10 +137,16 @@ OTR_COMPAT_EXPORT QList<QString> get_camera_names() close(fd); continue; } - ret[ret.size()-1] = reinterpret_cast<const char*>(video_cap.card); + ret.push_back({ QString((const char*)video_cap.card), i}); close(fd); } } #endif +#ifdef __APPLE__ + QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); + for (const QCameraInfo &cameraInfo : cameras) + ret.push_back({ cameraInfo.description(), ret.size() }); +#endif + return ret; } diff --git a/compat/camera-names.hpp b/compat/camera-names.hpp index 97184c8c..3585edfe 100644 --- a/compat/camera-names.hpp +++ b/compat/camera-names.hpp @@ -8,11 +8,12 @@ #pragma once -#include <QList> +#include <vector> #include <QString> +# include <tuple> #include "export.hpp" -OTR_COMPAT_EXPORT QList<QString> get_camera_names(); +OTR_COMPAT_EXPORT std::vector<std::tuple<QString, int>> get_camera_names(); OTR_COMPAT_EXPORT int camera_name_to_index(const QString &name); diff --git a/compat/check-visible.cpp b/compat/check-visible.cpp index 84ddd6d9..621d941a 100644 --- a/compat/check-visible.cpp +++ b/compat/check-visible.cpp @@ -1,71 +1,98 @@ #include "check-visible.hpp" +#include <QMutex> +#include <QWidget> +#include <QDebug> + +static QMutex lock; +static bool visible = true; + #if defined _WIN32 #include "timer.hpp" +#include "macros.h" -#include <QMutexLocker> - -#include <windows.h> +static Timer timer; -constexpr int visible_timeout = 5000; +constexpr int visible_timeout = 1000; constexpr int invisible_timeout = 250; -static Timer timer; -static QMutex mtx; -static bool visible = true; +#include <windows.h> -never_inline OTR_COMPAT_EXPORT void set_is_visible(const QWidget& w, bool force) { - QMutexLocker l(&mtx); + QMutexLocker l(&lock); - if (!force && timer.elapsed_ms() < (visible ? visible_timeout : invisible_timeout)) + if (w.isHidden() || w.windowState() & Qt::WindowMinimized) + { + visible = false; return; + } - timer.start(); + { + int ndisplays = GetSystemMetrics(SM_CMONITORS); + if (ndisplays > 1) + { + visible = true; + return; + } + } + + HWND hwnd = (HWND)w.winId(); - const HWND id = (HWND) w.winId(); - const QPoint pt = w.mapToGlobal({ 0, 0 }); + if (!force && timer.elapsed_ms() < (visible ? visible_timeout : invisible_timeout)) + return; - const int W = w.width(), H = w.height(); + timer.start(); - const QPoint points[] = + if (RECT r; GetWindowRect(hwnd, &r)) { - pt, - pt + QPoint(W - 1, 0), - pt + QPoint(0, H - 1), - pt + QPoint(W - 1, H - 1), - pt + QPoint(W / 2, H / 2), - }; - - for (const QPoint& pt : points) + const int x = r.left, y = r.top; + const int w = r.right - x, h = r.bottom - y; + + const POINT xs[] { + { x + w - 1, y + 1 }, + { x + 1, y + h - 1 }, + { x + w - 1, y + h - 1 }, + { x + 1, y + 1 }, + { x + w/2, y + h/2 }, + }; + + visible = false; + + for (const POINT& pt : xs) + if (WindowFromPoint(pt) == hwnd) + { + + visible = true; + break; + } + } + else { - visible = WindowFromPoint({ pt.x(), pt.y() }) == id; - if (visible) - break; + eval_once(qDebug() << "check-visible: GetWindowRect failed"); + visible = true; } } -never_inline OTR_COMPAT_EXPORT -bool check_is_visible() -{ - QMutexLocker l(&mtx); - - return visible; -} - #else -never_inline OTR_COMPAT_EXPORT -void set_is_visible(const QWidget&, bool) +void set_is_visible(const QWidget& w, bool) { + QMutexLocker l(&lock); + visible = !(w.isHidden() || w.windowState() & Qt::WindowMinimized); } -never_inline OTR_COMPAT_EXPORT +#endif + bool check_is_visible() { - return true; + QMutexLocker l(&lock); + return visible; } -#endif +void force_is_visible(bool value) +{ + QMutexLocker l(&lock); + visible = value; +} diff --git a/compat/check-visible.hpp b/compat/check-visible.hpp index a0211982..6669b84d 100644 --- a/compat/check-visible.hpp +++ b/compat/check-visible.hpp @@ -1,9 +1,15 @@ #pragma once #include "export.hpp" -#include "macros.hpp" +#include "macros.h" -#include <QWidget> +class QWidget; -never_inline OTR_COMPAT_EXPORT void set_is_visible(QWidget const& w, bool force = false); -never_inline OTR_COMPAT_EXPORT bool check_is_visible(); +never_inline OTR_COMPAT_EXPORT +void set_is_visible(QWidget const& w, bool force = false); + +OTR_COMPAT_EXPORT +bool check_is_visible(); + +OTR_COMPAT_EXPORT +void force_is_visible(bool value); diff --git a/compat/copyable-mutex.cpp b/compat/copyable-mutex.cpp deleted file mode 100644 index 7c112c5e..00000000 --- a/compat/copyable-mutex.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "copyable-mutex.hpp" - -mutex& mutex::operator=(const mutex& datum) -{ - inner = nullptr; - inner = std::make_unique<QMutex>(datum->isRecursive() - ? QMutex::Recursive - : QMutex::NonRecursive); - return *this; -} - -mutex::mutex(const mutex& datum) -{ - *this = datum; -} - -mutex::mutex(mutex::mode m) : - inner(std::make_unique<QMutex>(static_cast<QMutex::RecursionMode>(int(m)))) -{ -} - -QMutex* mutex::operator&() const -{ - return *this; -} - -QMutex* mutex::operator->() const -{ - return *this; -} - -mutex::operator QMutex*() const -{ - return const_cast<QMutex*>(inner.get()); -} - diff --git a/compat/copyable-mutex.hpp b/compat/copyable-mutex.hpp deleted file mode 100644 index af82876d..00000000 --- a/compat/copyable-mutex.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include <memory> - -#include <QMutex> - -#include "export.hpp" - -class OTR_COMPAT_EXPORT mutex -{ - std::unique_ptr<QMutex> inner; - -public: - enum mode - { - recursive = QMutex::Recursive, - normal = QMutex::NonRecursive, - }; - - mutex& operator=(const mutex& datum); - mutex(const mutex& datum); - mutex(mode m = normal); - - QMutex* operator&() const; - operator QMutex*() const; - QMutex* operator->() const; -}; diff --git a/compat/correlation-calibrator.cpp b/compat/correlation-calibrator.cpp index 949f10ae..07ef7d3d 100644 --- a/compat/correlation-calibrator.cpp +++ b/compat/correlation-calibrator.cpp @@ -2,6 +2,7 @@ #include "variance.hpp" #include "compat/math.hpp" #include "compat/meta.hpp" +#include "compat/macros.h" #include <cmath> #include <iterator> @@ -11,16 +12,9 @@ #define DEBUG_PRINT #ifdef DEBUG_PRINT # include <cstdio> -# include <cwchar> - using std::fwprintf; - using std::fflush; #endif -using namespace correlation_calibrator_impl; - -correlation_calibrator::correlation_calibrator() -{ -} +namespace correlation_calibrator_impl { static constexpr unsigned nbuckets[6] = { @@ -44,26 +38,26 @@ static constexpr double spacing[6] = roll_spacing_in_degrees, }; -static constexpr wchar_t const* const names[6] { - L"x", L"y", L"z", - L"yaw", L"pitch", L"roll", +static constexpr char const* const names[6] { + "x", "y", "z", + "yaw", "pitch", "roll", }; -bool correlation_calibrator::check_buckets(const vec6 &data) +bool correlation_calibrator::check_buckets(const vec6& data) { bool ret = false; unsigned pos[6]; for (unsigned k = 0; k < 6; k++) { - const double val = clamp(data[k], min[k], max[k]); - pos[k] = (val-min[k])/spacing[k]; + const double val = std::clamp(data[k], min[k], max[k]); + pos[k] = unsigned((val-min[k])/spacing[k]); if (pos[k] >= nbuckets[k]) { - once_only(qDebug() << "idx" << k + eval_once(qDebug() << "idx" << k << "bucket" << (int)pos[k] - << "outside bounds" << nbuckets[k]);; + << "outside bounds" << nbuckets[k]); return false; } @@ -133,18 +127,18 @@ mat66 correlation_calibrator::get_coefficients() const cs = cs * (1./(data.size() - 1)); #if defined DEBUG_PRINT - fwprintf(stderr, L"v:change-of h:due-to\n"); - fwprintf(stderr, L"%10s ", L""); - for (unsigned k = 0; k < 6; k++) - fwprintf(stderr, L"%10s", names[k]); - fwprintf(stderr, L"\n"); + fprintf(stderr, "v:change-of h:due-to\n"); + fprintf(stderr, "%10s ", ""); + for (char const* k : names) + fprintf(stderr, "%10s", k); + fprintf(stderr, "\n"); for (unsigned i = 0; i < 6; i++) { - fwprintf(stderr, L"%10s ", names[i]); + fprintf(stderr, "%10s ", names[i]); for (unsigned k = 0; k < 6; k++) - fwprintf(stderr, L"%10.3f", cs(i, k)); - fwprintf(stderr, L"\n"); + fprintf(stderr, "%10.3f", cs(i, k)); + fprintf(stderr, "\n"); } fflush(stderr); #endif @@ -162,3 +156,5 @@ unsigned correlation_calibrator::sample_count() const { return data.size(); } + +} // ns correlation_calibrator_impl diff --git a/compat/correlation-calibrator.hpp b/compat/correlation-calibrator.hpp index b44a2312..44aee537 100644 --- a/compat/correlation-calibrator.hpp +++ b/compat/correlation-calibrator.hpp @@ -9,7 +9,7 @@ namespace correlation_calibrator_impl { -static constexpr inline double min[6] = { +static constexpr double min[6] = { -50, -50, 250, @@ -19,7 +19,7 @@ static constexpr inline double min[6] = { -180, }; -static constexpr inline double max[6] = { +static constexpr double max[6] = { 50, 50, 250, @@ -29,18 +29,18 @@ static constexpr inline double max[6] = { 180, }; -static constexpr inline double yaw_spacing_in_degrees = 1.5; -static constexpr inline double pitch_spacing_in_degrees = 1; -static constexpr inline double roll_spacing_in_degrees = 1; +static constexpr double yaw_spacing_in_degrees = 1.5; +static constexpr double pitch_spacing_in_degrees = 1; +static constexpr double roll_spacing_in_degrees = 1; -static constexpr inline unsigned yaw_nbuckets = 1+ 360./yaw_spacing_in_degrees; -static constexpr inline unsigned pitch_nbuckets = 1+ 360./pitch_spacing_in_degrees; -static constexpr inline unsigned roll_nbuckets = 1+ 360./roll_spacing_in_degrees; +static constexpr unsigned yaw_nbuckets = unsigned(1+ 360./yaw_spacing_in_degrees); +static constexpr unsigned pitch_nbuckets = unsigned(1+ 360./pitch_spacing_in_degrees); +static constexpr unsigned roll_nbuckets = unsigned(1+ 360./roll_spacing_in_degrees); -static constexpr inline double translation_spacing = .25; -static constexpr inline unsigned x_nbuckets = 1+ (max[0]-min[0])/translation_spacing; -static constexpr inline unsigned y_nbuckets = 1+ (max[1]-min[1])/translation_spacing; -static constexpr inline unsigned z_nbuckets = 1+ (max[2]-min[2])/translation_spacing; +static constexpr double translation_spacing = .25; +static constexpr unsigned x_nbuckets = unsigned(1+ (max[0]-min[0])/translation_spacing); +static constexpr unsigned y_nbuckets = unsigned(1+ (max[1]-min[1])/translation_spacing); +static constexpr unsigned z_nbuckets = unsigned(1+ (max[2]-min[2])/translation_spacing); using vec6 = Mat<double, 6, 1>; using mat66 = Mat<double, 6, 6>; @@ -63,14 +63,14 @@ class OTR_COMPAT_EXPORT correlation_calibrator final bool check_buckets(const vec6& data); public: - correlation_calibrator(); + correlation_calibrator() = default; void input(const vec6& data); mat66 get_coefficients() const; unsigned sample_count() const; - static constexpr inline unsigned min_samples = 25; + static constexpr unsigned min_samples = 25; }; } // ns correlation_calibrator_impl -using correlation_calibrator_impl::correlation_calibrator; +using correlation_calibrator = correlation_calibrator_impl::correlation_calibrator; diff --git a/compat/enum-operators.hpp b/compat/enum-operators.hpp new file mode 100644 index 00000000..e4f81a39 --- /dev/null +++ b/compat/enum-operators.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include <type_traits> + +#define OTR_FLAGS_OP2(type, op) \ + constexpr inline type operator op (type a, type b) \ + { \ + using t_ = std::underlying_type_t<type>; \ + return type(t_((a)) op t_((b))); \ + } // end + +#define OTR_FLAGS_DELETE_SHIFT(type, op) \ + template<typename u> \ + type operator op (type, u) = delete // end + +#define OTR_FLAGS_OP1(type, op) \ + constexpr inline type operator op (type x) \ + { \ + using t_ = std::underlying_type_t<type>; \ + return type(op t_((x))); \ + } // end + +#define OTR_FLAGS_ASSIGN_OP(type, op) \ + constexpr inline type& operator op ## = (type& lhs, type rhs) \ + { \ + using t_ = std::underlying_type_t<decltype(rhs)>; \ + lhs = type(t_((lhs)) op t_((rhs))); \ + return lhs; \ + } //end + +#define OTR_FLAGS_DELETE_SHIFT_ASSIGN(type, op) \ + type operator op ## = (type& lhs, type rhs) = delete //end + +#define DEFINE_ENUM_OPERATORS(type) \ + OTR_FLAGS_OP2(type, |) \ + OTR_FLAGS_OP2(type, &) \ + OTR_FLAGS_OP2(type, ^) \ + OTR_FLAGS_OP1(type, ~) \ + OTR_FLAGS_DELETE_SHIFT(type, <<); \ + OTR_FLAGS_DELETE_SHIFT(type, >>); \ + OTR_FLAGS_ASSIGN_OP(type, |) \ + OTR_FLAGS_ASSIGN_OP(type, &) \ + OTR_FLAGS_ASSIGN_OP(type, ^) \ + OTR_FLAGS_DELETE_SHIFT_ASSIGN(type, <<); \ + OTR_FLAGS_DELETE_SHIFT_ASSIGN(type, >>) // end diff --git a/compat/euler.cpp b/compat/euler.cpp index 39d2b116..5d6fb25b 100644 --- a/compat/euler.cpp +++ b/compat/euler.cpp @@ -2,38 +2,53 @@ #include "math-imports.hpp" #include <cmath> -// rotation order is XYZ - namespace euler { -euler_t OTR_COMPAT_EXPORT rmat_to_euler(const rmat& R) +Pose_ rmat_to_euler(const rmat& R) { - double alpha, beta, gamma; - - beta = atan2( -R(2,0), sqrt(R(2,1)*R(2,1) + R(2,2)*R(2,2)) ); - alpha = atan2( R(1,0), R(0,0)); - gamma = atan2( R(2,1), R(2,2)); - - return { alpha, -beta, gamma }; + const double cy = sqrt(R(2,2)*R(2, 2) + R(2, 1)*R(2, 1)); + const bool large_enough = cy > 1e-10; + if (large_enough) + return { + atan2(-R(1, 0), R(0, 0)), + atan2(R(2, 0), cy), + atan2(-R(2, 1), R(2, 2)) + }; + else + return { + atan2(R(0, 1), R(1, 1)), + atan2(R(2, 0), cy), + 0 + }; } -rmat OTR_COMPAT_EXPORT euler_to_rmat(const euler_t& e) +// tait-bryan angles, not euler +rmat euler_to_rmat(const Pose_& input) { - const double X = e(2); - const double Y = -e(1); - const double Z = e(0); + const double H = -input(0); + const double P = -input(1); + const double B = -input(2); - const double cx = cos(X); - const double sx = sin(X); - const double cy = cos(Y); - const double sy = sin(Y); - const double cz = cos(Z); - const double sz = sin(Z); + const auto c1 = cos(H); + const auto s1 = sin(H); + const auto c2 = cos(P); + const auto s2 = sin(P); + const auto c3 = cos(B); + const auto s3 = sin(B); return { - cy*cz, -cy*sz, sy, - cz*sx*sy + cx*sz, cx*cz - sx*sy*sz, -cy*sx, - -cx*cz*sy + sx*sz, cz*sx + cx*sy*sz, cx*cy, + // z + c1*c2, + c1*s2*s3 - c3*s1, + s1*s3 + c1*c3*s2, + // y + c2*s1, + c1*c3 + s1*s2*s3, + c3*s1*s2 - c1*s3, + // x + -s2, + c2*s3, + c2*c3 }; } diff --git a/compat/euler.hpp b/compat/euler.hpp index d4217a70..b2f10ac0 100644 --- a/compat/euler.hpp +++ b/compat/euler.hpp @@ -17,9 +17,9 @@ template<int h_, int w_> using dmat = Mat<double, h_, w_>; using dvec3 = Mat<double, 3, 1>; using rmat = dmat<3, 3>; -using euler_t = dmat<3, 1>; +using Pose_ = dmat<3, 1>; -rmat OTR_COMPAT_EXPORT euler_to_rmat(const euler_t& input); -euler_t OTR_COMPAT_EXPORT rmat_to_euler(const rmat& R); +rmat OTR_COMPAT_EXPORT euler_to_rmat(const Pose_& input); +Pose_ OTR_COMPAT_EXPORT rmat_to_euler(const rmat& R); } // end ns euler diff --git a/compat/hamilton-tools.cpp b/compat/hamilton-tools.cpp new file mode 100644 index 00000000..eb0ef984 --- /dev/null +++ b/compat/hamilton-tools.cpp @@ -0,0 +1,109 @@ +#include "hamilton-tools.h" +#include <cmath> + +tQuat QuatFromAngleAxe(const double angle, const tVector& axe) +{ + double a = TO_RAD * 0.5 * angle; + double d = sin(a) / VectorLength(axe); + return ( tQuat ( + axe.v[0] * d, + axe.v[1] * d, + axe.v[2] * d, + cos(a) + ) + ); +} + +tQuat QuatMultiply(const tQuat& qL, const tQuat& qR) +{ + tQuat Q; + Q.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z - qL.z*qR.y; + Q.y = qL.w*qR.y + qL.y*qR.w + qL.z*qR.x - qL.x*qR.z; + Q.z = qL.w*qR.z + qL.z*qR.w + qL.x*qR.y - qL.y*qR.x; + Q.w = qL.w*qR.w - qL.x*qR.x - qL.y*qR.y - qL.z*qR.z; + return(Q); +} + +tQuat QuatFromYPR(const double YPR[]) +{ + tQuat Q, Qp, Qy; + Q = QuatFromAngleAxe( -YPR[2], {0, 0, 1} ); //Roll, Z axe + Qp = QuatFromAngleAxe( -YPR[1], {1, 0, 0} ); //Pitch, X axe + Qy = QuatFromAngleAxe( -YPR[0], {0, 1, 0} ); //Yaw, Y axe + + Q = QuatMultiply(Qp, Q); + return(QuatMultiply(Qy, Q)); +} + +void Normalize(tQuat& Q) +{ + double m = sqrt(Q.x*Q.x + Q.y*Q.y + Q.z*Q.z + Q.w*Q.w); + if (m > EPSILON) + { + m = 1 / m; + Q.x = Q.x * m; + Q.y = Q.y * m; + Q.z = Q.z * m; + Q.w = Q.w * m; + } + else Q = tQuat(0, 0, 0, 1); +} + +tQuat Slerp(const tQuat& S, const tQuat& D, const double alpha) +{ + // calc cosine of half angle + double cosin = S.x*D.x + S.y*D.y + S.z*D.z + S.w*D.w; + + // select nearest rotation direction + tQuat Q; + if (cosin < 0) + { + cosin = - cosin; + Q.x = - D.x; + Q.y = - D.y; + Q.z = - D.z; + Q.w = - D.w; + } + else Q = D; + + // calculate coefficients + double scale0, scale1; + if ((1.0 - cosin) > EPSILON) + { + double omega = acos(cosin); + double sinus = 1 / sin(omega); + scale0 = sin((1.0 - alpha) * omega) * sinus; + scale1 = sin(alpha * omega)* sinus; + } + else + { + scale0 = 1.0 - alpha; + scale1 = alpha; + } + + Q.x = scale0 * S.x + scale1 * Q.x; + Q.y = scale0 * S.y + scale1 * Q.y; + Q.z = scale0 * S.z + scale1 * Q.z; + Q.w = scale0 * S.w + scale1 * Q.w; + + Normalize(Q); + + return( Q ); +} + +void QuatToYPR(const tQuat& Q, double YPR[]) +{ + const double xx = Q.x * Q.x; + const double xy = Q.x * Q.y; + const double xz = Q.x * Q.z; + const double xw = Q.x * Q.w; + const double yy = Q.y * Q.y; + const double yz = Q.y * Q.z; + const double yw = Q.y * Q.w; + const double zz = Q.z * Q.z; + const double zw = Q.z * Q.w; + + YPR[0] = TO_DEG * ( -atan2( 2 * ( xz + yw ), 1 - 2 * ( xx + yy ) )); + YPR[1] = TO_DEG * ( asin ( 2 * ( yz - xw ) )); + YPR[2] = TO_DEG * ( -atan2( 2 * ( xy + zw ), 1 - 2 * ( xx + zz ) )); +} diff --git a/compat/hamilton-tools.h b/compat/hamilton-tools.h new file mode 100644 index 00000000..885e9f79 --- /dev/null +++ b/compat/hamilton-tools.h @@ -0,0 +1,76 @@ +#pragma once + +#include <algorithm> +#include "export.hpp" +#include "compat/math.hpp" + +constexpr double TO_RAD = (M_PI / 180); +constexpr double TO_DEG = (180 / M_PI); +constexpr double EPSILON = 1e-30; + +struct tVector +{ + double v[3]; + tVector(double X = 0, double Y = 0, double Z = 0) {v[0]=X; v[1]=Y; v[2]=Z;} + tVector(const double V[]) {v[0]=V[0]; v[1]=V[1]; v[2]=V[2];} + + void operator=(const tVector& other) + { + std::copy(other.v, other.v + 3, v); + } + inline const tVector operator+(const tVector& other) const + { + return tVector(v[0] + other.v[0], v[1] + other.v[1], v[2] + other.v[2]); + } + void operator+=(const tVector& other) + { + v[0] += other.v[0]; + v[1] += other.v[1]; + v[2] += other.v[2]; + } + const tVector operator-(const tVector& other) const + { + return tVector(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]); + } + void operator-=(const tVector& other) + { + v[0] -= other.v[0]; + v[1] -= other.v[1]; + v[2] -= other.v[2]; + } + const tVector operator*(double scalar) const + { + return tVector(v[0] * scalar, v[1] * scalar, v[2] * scalar); + } + void operator*=(double scalar) + { + v[0] *= scalar; + v[1] *= scalar; + v[2] *= scalar; + } + const tVector operator/(double scalar) const + { + return *this * (1.0 / scalar); + } + void operator/= (double scalar) + { + *this *= 1.0 / scalar; + } +}; + +struct tQuat +{ + double x, y, z, w; + tQuat(double X = 0, double Y = 0, double Z = 0, double W = 1) + {x = X; y = Y; z = Z; w = W;} +}; + +inline double VectorLength(const tVector& v) { return sqrt(v.v[0] * v.v[0] + v.v[1] * v.v[1] + v.v[2] * v.v[2]); } +inline double VectorDistance(const tVector& v1, const tVector& v2) { return VectorLength(v2 - v1); } +inline tVector Lerp(const tVector& s, const tVector& d, const double alpha) { return s + (d - s) * alpha; } +tQuat OTR_COMPAT_EXPORT QuatFromYPR(const double YPR[]); +tQuat OTR_COMPAT_EXPORT QuatMultiply(const tQuat& qL, const tQuat& qR); +inline tQuat QuatDivide(const tQuat& qL, const tQuat& qR) { return QuatMultiply(qL, tQuat(-qR.x, -qR.y, -qR.z, qR.w)); } +inline double AngleBetween(const tQuat& S, const tQuat& D) { return TO_DEG * 2 * acos(fabs(S.x * D.x + S.y * D.y + S.z * D.z + S.w * D.w)); } +tQuat OTR_COMPAT_EXPORT Slerp(const tQuat& S, const tQuat& D, const double alpha); +void OTR_COMPAT_EXPORT QuatToYPR(const tQuat& Q, double YPR[]); diff --git a/compat/lang/de_DE.ts b/compat/lang/de_DE.ts new file mode 100644 index 00000000..1552582e --- /dev/null +++ b/compat/lang/de_DE.ts @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="de_DE"> +</TS> diff --git a/compat/lang/zh_CN.ts b/compat/lang/zh_CN.ts index 6401616d..e5ca8aa9 100644 --- a/compat/lang/zh_CN.ts +++ b/compat/lang/zh_CN.ts @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> -<TS version="2.1"> +<TS version="2.1" language="zh_CN"> </TS> diff --git a/compat/library-path.hpp b/compat/library-path.hpp index e14cf5e0..49e2c414 100644 --- a/compat/library-path.hpp +++ b/compat/library-path.hpp @@ -1,4 +1,4 @@ #pragma once // from build directory -#include "__opentrack-library-path.h" +#include "opentrack-library-path.hxx" diff --git a/compat/linkage-macros.hpp b/compat/linkage-macros.hpp index 9e71ac8c..f4d0104b 100644 --- a/compat/linkage-macros.hpp +++ b/compat/linkage-macros.hpp @@ -1,15 +1,26 @@ #pragma once -#if defined _MSC_VER -# define OTR_GENERIC_EXPORT __declspec(dllexport) -# define OTR_GENERIC_IMPORT __declspec(dllimport) -#elif defined _WIN32 && !defined(__WINE__) -# define OTR_GENERIC_EXPORT __attribute__((dllexport, visibility ("default"))) -# define OTR_GENERIC_IMPORT __attribute__((dllimport)) -#else -# define OTR_GENERIC_EXPORT __attribute__ ((visibility ("default"))) -# define OTR_GENERIC_IMPORT +#ifndef OTR_GENERIC_EXPORT +# if defined _MSC_VER +# define OTR_GENERIC_EXPORT __declspec(dllexport) +# define OTR_GENERIC_IMPORT __declspec(dllimport) +# elif defined _WIN32 && !defined __WINE__ +# define OTR_GENERIC_EXPORT __attribute__((dllexport, visibility ("default"))) +# define OTR_GENERIC_IMPORT __attribute__((dllimport)) +# else +# define OTR_GENERIC_EXPORT __attribute__((visibility ("default"))) +# define OTR_GENERIC_IMPORT +# endif +#endif + +#if defined __APPLE__ || defined __MINGW32__ +# define OTR_NO_TMPL_INST // link failure on both targets #endif -#define OTR_TEMPLATE_EXPORT template class OTR_GENERIC_EXPORT -#define OTR_TEMPLATE_IMPORT extern template class OTR_GENERIC_IMPORT +#if defined OTR_NO_TMPL_INST +# define OTR_TEMPLATE_IMPORT(x) +# define OTR_TEMPLATE_EXPORT(x) +#else +# define OTR_TEMPLATE_IMPORT(x) extern template class OTR_GENERIC_IMPORT x; +# define OTR_TEMPLATE_EXPORT(x) template class OTR_GENERIC_EXPORT x; +#endif diff --git a/compat/macros.h b/compat/macros.h new file mode 100644 index 00000000..7de7fb86 --- /dev/null +++ b/compat/macros.h @@ -0,0 +1,57 @@ +#pragma once + +#if defined _MSC_VER +# define never_inline __declspec(noinline) +#else +# define never_inline __attribute__((noinline)) +#endif + +#if defined _MSC_VER +# define force_inline __forceinline +#else +# define force_inline __attribute__((always_inline)) inline +#endif + +#if !defined likely +# if defined __GNUC__ +# define likely(x) __builtin_expect(!!(x),1) +# define unlikely(x) __builtin_expect(!!(x),0) +# else +# define likely(x) (x) +# define unlikely(x) (x) +# endif +#endif + +#if defined _MSC_VER +# define function_name __FUNCSIG__ +#else +# define function_name __PRETTY_FUNCTION__ +#endif + +#ifdef _MSC_VER +# define unreachable() do { __assume(0); *(volatile int*)nullptr = 0; } while (0) /* NOLINT(clang-analyzer-core.NullDereference) */ +#else +# define unreachable() do { __builtin_unreachable(); *(volatile int*)nullptr = 0; } while (0) /* NOLINT(clang-analyzer-core.NullDereference) */ +#endif + +#ifdef __cplusplus +# define progn(...) ([&]() -> decltype(auto) { __VA_ARGS__ }()) +# define eval_once2(expr, ctr) eval_once3(expr, ctr) +# define eval_once3(expr, ctr) ([&] { [[maybe_unused]] static const char init_ ## ctr = ((void)(expr), 0); }()) +# ifdef QT_NO_DEBUG_OUTPUT +# define eval_once(expr) void() +# else +# define eval_once(expr) eval_once2(expr, __COUNTER__) +# endif + +#define OTR_DISABLE_MOVE_COPY(type) \ + type(const type&) = delete; \ + type(type&&) = delete; \ + type& operator=(const type&) = delete; \ + type& operator=(type&&) = delete +#endif + +#ifdef _MSC_VER +# define strncasecmp _strnicmp +# define strcasecmp _stricmp +#endif diff --git a/compat/macros.hpp b/compat/macros.hpp deleted file mode 100644 index c8fbca20..00000000 --- a/compat/macros.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#if !defined __WINE__ -# include <QCoreApplication> -# define otr_tr(...) (QCoreApplication::translate(OTR_MODULE_NAME, __VA_ARGS__)) -# define _(...) (otr_tr(__VA_ARGS__)) -#endif - -#if defined _MSC_VER -# -# define MEMORY_BARRIER _ReadWriteBarrier() -#else -# define MEMORY_BARRIER asm volatile("" ::: "memory") -#endif - -#if defined _MSC_VER -# define never_inline __declspec(noinline) -#elif defined __GNUG__ -# define never_inline __attribute__((noinline)) -#else -# define never_inline -#endif - -#if defined __cplusplus -# define restrict_ptr __restrict -#endif - -#if defined _MSC_VER -# define force_inline __forceinline -#else -# define force_inline __attribute__((always_inline, gnu_inline)) inline -#endif - -#ifdef Q_CREATOR_RUN -# define warn_result_unused -#elif defined _MSC_VER -# define warn_result_unused _Check_return_ -#else -# define warn_result_unused __attribute__((warn_unused_result)) -#endif - -#if defined __GNUC__ -# define likely(x) __builtin_expect(!!(x),1) -# define unlikely(x) __builtin_expect(!!(x),0) -#else -# define likely(x) (x) -# define unlikely(x) (x) -#endif - -#if defined _MSC_VER -# define OTR_FUNNAME (__FUNCSIG__) -#else -# define OTR_FUNNAME (__PRETTY_FUNCTION__) -#endif diff --git a/compat/math.hpp b/compat/math.hpp index 5d80dace..c5981cc2 100644 --- a/compat/math.hpp +++ b/compat/math.hpp @@ -1,74 +1,16 @@ #pragma once -#include "macros.hpp" - #include <cmath> #include <type_traits> -namespace util_detail { - -template<typename n> -inline auto clamp_float(n val, n min_, n max_) -{ - return std::fmin(std::fmax(val, min_), max_); -} - -template<typename t, typename n> -struct clamp final -{ - static inline auto clamp_(const n& val, const n& min_, const n& max_) - { - if (unlikely(val > max_)) - return max_; - if (unlikely(val < min_)) - return min_; - return val; - } -}; - -template<typename t> -struct clamp<float, t> -{ - static inline auto clamp_(float val, float min_, float max_) - { - return clamp_float(val, min_, max_); - } -}; - -template<typename t> -struct clamp<double, t> -{ - static inline auto clamp_(double val, double min_, double max_) - { - return clamp_float(val, min_, max_); - } -}; - -} // ns util_detail - -template<typename t, typename u, typename w> -inline auto clamp(const t& val, const u& min, const w& max) -{ - using tp = decltype(val + min + max); - return ::util_detail::clamp<std::decay_t<tp>, tp>::clamp_(val, min, max); -} - template<typename t> -inline int iround(const t& val) +inline auto iround(t val) -> std::enable_if_t<std::is_floating_point_v<std::decay_t<t>>, int> { - return int(std::round(val)); + return (int)std::round(val); } -template<typename t> -inline unsigned uround(const t& val) -{ - return std::round(std::fmax(t(0), val)); -} - -#include "macros.hpp" - -template <typename T> -static force_inline constexpr auto signum(T x) +template <typename t> +constexpr int signum(const t& x) { - return x < T(0) ? -1 : 1; + return x < t{0} ? -1 : 1; } diff --git a/compat/meta.hpp b/compat/meta.hpp index 80eca89a..49686996 100644 --- a/compat/meta.hpp +++ b/compat/meta.hpp @@ -7,26 +7,12 @@ * copyright notice and this permission notice appear in all copies. */ -#include <type_traits> +namespace meta::detail { -template<typename t> -using cv_qualified = std::conditional_t<std::is_fundamental_v<std::decay_t<t>>, std::decay_t<t>, const t&>; + template<typename... xs> + struct tuple; -#define progn(...) (([&]() { __VA_ARGS__ })()) -#define prog1(x, ...) (([&]() { auto _ret1324 = (x); do { __VA_ARGS__; } while (0); return _ret1324; })()) - -#define once_only(...) do { static bool once__ = false; if (!once__) { once__ = true; __VA_ARGS__; } } while(false) - -#if 0 - -#include <tuple> -#include <cstddef> - -namespace meta { - -namespace detail { - - template<typename x, typename y> + template<typename... xs> struct reverse_; template<typename x0, typename... xs, template<typename...> class x, typename... ys> @@ -41,51 +27,97 @@ namespace detail { using type = x<ys...>; }; - template<template<typename...> class inst, typename x> - struct lift_; + template<template<typename...> class, typename, typename...> struct lift_; - template<template<typename...> class inst, template<typename...> class x, typename... xs> - struct lift_<inst, x<xs...>> + template<template<typename...> class to, template<typename...> class from, typename... xs> + struct lift_<to, from<xs...>> { - using type = inst<xs...>; + using type = to<xs...>; }; - template<typename N, N max, N pos, typename... xs> - struct index_sequence_ + template<typename> struct append_helper; + + template<typename, typename> struct cons_; + + template<typename x, template<typename...> class t, typename... xs> + struct cons_<x, t<xs...>> { - using part = std::integral_constant<N, pos>; - using type = typename index_sequence_<N, max, pos+1, xs..., part>::type; + using type = t<x, xs...>; }; - template<typename N, N max, typename... xs> - struct index_sequence_<N, max, max, xs...> + template<typename> struct append2; + + template<template<typename...> class t, typename... xs> + struct append2<t<xs...>> { - using type = std::tuple<xs...>; + template<typename> struct append1; + + template<template<typename...> class u, typename... ys> + struct append1<u<ys...>> + { + using type = t<xs..., ys...>; + }; }; -} // ns detail + template<typename, typename...> struct list__; + + template<typename rest, typename... first> + struct list__ + { + template<typename> struct list1; + + template<template<typename...> class t, typename... xs> + struct list1<t<xs...>> + { + using type = t<first..., xs...>; + }; + + using type = typename list1<rest>::type; + }; + + template<typename xs, typename ys> + struct append_ + { + using t1 = append2<xs>; + using type = typename t1::template append1<ys>::type; + }; + +} // ns meta::detail + +namespace meta { + template<typename... xs> + using tuple_ = detail::tuple<xs...>; + + template<typename... xs> + using reverse = typename detail::reverse_<detail::tuple<xs...>, detail::tuple<>>::type; + + // the to/from order is awkward but mimics function composition + template<template<typename...> class to, typename from> + using lift = typename detail::lift_<to, from>::type; + + template<template<typename...> class to, typename from> + constexpr inline auto lift_v = detail::lift_<to, from>::type::value; -template<typename... xs> -using reverse = typename detail::reverse_<std::tuple<xs...>, std::tuple<>>::type; + template<typename x, typename... xs> + using first = x; -template<template<typename...> class inst, class x> -using lift = typename detail::lift_<inst, x>::type; + template<typename x, typename... xs> + using rest = detail::tuple<xs...>; -template<typename x, typename... xs> -using first = x; + template<typename... xs> + using butlast = reverse<rest<reverse<xs...>>>; -template<typename x, typename... xs> -using rest = std::tuple<xs...>; + template<typename... xs> + using last = lift<first, reverse<xs...>>; -template<typename... xs> -using butlast = reverse<rest<reverse<xs...>>>; + template<typename x, typename rest> + using cons = typename detail::cons_<x, rest>::type; -template<typename... xs> -using last = lift<first, reverse<xs...>>; + template<typename xs, typename ys> + using append = typename detail::append_<xs, ys>; -template<std::size_t max> -using index_sequence = typename detail::index_sequence_<std::size_t, max, std::size_t(0)>::type; + template<typename rest, typename... xs> + using list_ = typename detail::list__<rest, xs...>; } // ns meta -#endif diff --git a/compat/mutex.cpp b/compat/mutex.cpp new file mode 100644 index 00000000..664677ea --- /dev/null +++ b/compat/mutex.cpp @@ -0,0 +1,33 @@ +#include "mutex.hpp" +#include <cstdlib> + +mutex& mutex::operator=(const mutex& rhs) +{ + if (rhs->isRecursive() != inner.isRecursive()) + std::abort(); + + return *this; +} + +mutex::mutex(const mutex& datum) : mutex{datum.inner.isRecursive() ? Recursive : NonRecursive} +{ +} + +mutex::mutex(RecursionMode m) : inner{m} +{ +} + +QMutex* mutex::operator&() const noexcept +{ + return &inner; +} + +mutex::operator QMutex*() const noexcept +{ + return &inner; +} + +QMutex* mutex::operator->() const noexcept +{ + return &inner; +} diff --git a/compat/mutex.hpp b/compat/mutex.hpp new file mode 100644 index 00000000..54758a08 --- /dev/null +++ b/compat/mutex.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <QMutex> + +#include "export.hpp" + +class OTR_COMPAT_EXPORT mutex +{ + mutable QMutex inner; + +public: + using RecursionMode = QMutex::RecursionMode; + static constexpr RecursionMode Recursive = RecursionMode::Recursive; + static constexpr RecursionMode NonRecursive = RecursionMode::NonRecursive; + + mutex& operator=(const mutex& datum); + mutex(const mutex& datum); + explicit mutex(RecursionMode m); + mutex() : mutex{NonRecursive} {} + + QMutex* operator&() const noexcept; + explicit operator QMutex*() const noexcept; + QMutex* operator->() const noexcept; +}; diff --git a/compat/process-list.hpp b/compat/process-list.hpp index 10613791..39e12603 100644 --- a/compat/process-list.hpp +++ b/compat/process-list.hpp @@ -52,7 +52,7 @@ static QStringList get_all_executable_names() template<typename = void> static QStringList get_all_executable_names() { - QStringList ret; + QStringList ret; ret.reserve(2048); std::vector<int> vec; while (true) @@ -103,7 +103,7 @@ static QStringList get_all_executable_names() if (idx != -1) { QString tmp = cmdline[0].mid(idx+1); - if (cmdline.size() > 1 && (tmp == "wine.bin" || tmp == "wine")) + if (cmdline.size() > 1 && (tmp == QStringLiteral("wine.bin") || tmp == QStringLiteral("wine"))) { idx = cmdline[1].lastIndexOf('/'); if (idx == -1) @@ -129,14 +129,68 @@ static QStringList get_all_executable_names() } } -#elif defined __linux +#elif defined __linux__ + +#include <cerrno> + +#ifdef OTR_HAS_LIBPROC2 +#include <libproc2/pids.h> +template<typename = void> +QStringList get_all_executable_names() +{ + QStringList ret; ret.reserve(2048); + enum pids_item items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_CMDLINE_V }; + + enum rel_items { rel_pid, rel_cmd, rel_cmdline }; + struct pids_info *info = NULL; + struct pids_stack *stack; + QString tmp; tmp.reserve(255); + + procps_pids_new(&info, items, 3); + // procps-ng version 4.0.5 removed an unused argument in PIDS_VAL() macro. + // cf. https://gitlab.com/procps-ng/procps/-/commit/967fdcfb06e20aad0f3 + + // Although the emitted machine code is identical, backward API + // compatibility was silently broken in the patch with no upgrade path + // (e.g. deprecating PIDS_VAL() while introducing PIDS_VAL2()). + + // Unfortunately, procps-ng doesn't include a #define for identifying its + // version. For these reasons the code below depends on undocumented ABI + // compatibility between procps-ng versions.. -sh 20241226 + +#define OPENTRACK_PIDS_VAL(i, type, stack) stack->head[i].result.type + + while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) + { + char **p_cmdline = OPENTRACK_PIDS_VAL(rel_cmdline, strv, stack); + + // note, wine sets argv[0] so no parsing like in OSX case + if (p_cmdline && p_cmdline[0] && p_cmdline[0][0] && + !(p_cmdline[0][0] == '-' && !p_cmdline[0][1])) + { + tmp = QString{p_cmdline[0]}; + const int idx = std::max(tmp.lastIndexOf('\\'), tmp.lastIndexOf('/')); + if (idx != -1) + tmp = tmp.mid(idx+1); + //qDebug() << "procps" << tmp; + ret.append(tmp); + } + } + //qDebug() << "-- procps end"; + + procps_pids_unref(&info); + + return ret; +} +#else #include <proc/readproc.h> #include <cerrno> + template<typename = void> -static QStringList get_all_executable_names() +QStringList get_all_executable_names() { - QStringList ret; + QStringList ret; ret.reserve(2048); proc_t** procs = readproctab(PROC_FILLCOM); if (procs == nullptr) { @@ -159,6 +213,7 @@ static QStringList get_all_executable_names() free(procs); return ret; } +#endif #else template<typename = void> diff --git a/compat/qhash.hpp b/compat/qhash.hpp new file mode 100644 index 00000000..5286e97e --- /dev/null +++ b/compat/qhash.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <functional> + +#include <QtGlobal> +#include <QString> +#include <QHashFunctions> + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + +#include <cstdlib> + +namespace std { +template<> struct hash<QString> +{ + using argument_type = QString; + using result_type = std::size_t; + + std::size_t operator()(const QString& value) const noexcept + { + return (std::size_t) qHash(value); + } +}; +} + +#endif diff --git a/compat/qt-dpi.hpp b/compat/qt-dpi.hpp new file mode 100644 index 00000000..fc17a062 --- /dev/null +++ b/compat/qt-dpi.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <algorithm> +#include <QWidget> + +static inline double screen_dpi(const QPaintDevice* widget) +{ + const auto& x = *widget; +#ifdef _WIN32 + return std::max(x.devicePixelRatioF(), 1.); +#else + return std::max(std::max(x.logicalDpiX()/(double)x.physicalDpiX(), x.devicePixelRatioF()), 1.); +#endif +} + +template<typename self> +struct screen_dpi_mixin +{ +protected: + double screen_dpi() const + { + return ::screen_dpi(static_cast<const self*>(this)); + } +}; diff --git a/compat/qt-signal.cpp b/compat/qt-signal.cpp new file mode 100644 index 00000000..8c23866b --- /dev/null +++ b/compat/qt-signal.cpp @@ -0,0 +1,7 @@ +#define OTR_GENERATE_SIGNAL3(type) void sig_##type::operator()(const type& x) const { notify(x); } +#include "qt-signal.hpp" +namespace _qt_sig_impl { + +sig_void::sig_void(QObject* parent) : QObject(parent) {} + +} diff --git a/compat/qt-signal.hpp b/compat/qt-signal.hpp new file mode 100644 index 00000000..f74de642 --- /dev/null +++ b/compat/qt-signal.hpp @@ -0,0 +1,72 @@ +#pragma once + +// this is to avoid dealing with QMetaObject for the time being -sh 20190203 + +#include "export.hpp" +namespace options { class slider_value; } +#include <QObject> +#include <QList> + +namespace _qt_sig_impl { + +template<typename t> struct sig; + +class OTR_COMPAT_EXPORT sig_void final : public QObject +{ + Q_OBJECT + +public: + template<typename t, typename F> + sig_void(t* datum, F&& f, Qt::ConnectionType conntype = Qt::AutoConnection) : QObject(datum) + { + connect(this, &sig_void::notify, datum, f, conntype); + } + explicit sig_void(QObject* parent = nullptr); + void operator()() const { notify(); } + +signals: + void notify() const; +}; + +template<> struct sig<void> { using t = sig_void; }; + +#ifndef OTR_GENERATE_SIGNAL3 +# define OTR_GENERATE_SIGNAL3(x) +#endif +#define OTR_GENERATE_SIGNAL2(type) \ + class OTR_COMPAT_EXPORT sig_##type final : public QObject \ + { \ + Q_OBJECT \ + public: \ + explicit sig_##type(QObject* parent = nullptr) : QObject(parent) {} \ + void operator()(const type& x) const; \ + Q_SIGNALS: \ + void notify(const type& x) const; \ + }; \ + OTR_GENERATE_SIGNAL3(type) + +# define OTR_GENERATE_SIGNAL(type) \ + OTR_GENERATE_SIGNAL2(type); \ + using qlist##type = QList<type>; \ + OTR_GENERATE_SIGNAL2(qlist##type); \ + template<> struct sig<type> { using t = sig_##type; }; \ + template<> struct sig<qlist##type> { using t = qlist##type; } + +using slider_value = options::slider_value; + +OTR_GENERATE_SIGNAL(int); +OTR_GENERATE_SIGNAL(double); +OTR_GENERATE_SIGNAL(float); +OTR_GENERATE_SIGNAL(bool); +OTR_GENERATE_SIGNAL(QString); +OTR_GENERATE_SIGNAL(slider_value); +OTR_GENERATE_SIGNAL(QPointF); +OTR_GENERATE_SIGNAL(QVariant); + +} // namespace _qt_sig_impl + +#undef OTR_GENERATE_SIGNAL2 +#undef OTR_GENERATE_SIGNAL + +template<typename t> using qt_signal = typename _qt_sig_impl::sig<t>::t; + diff --git a/compat/run-in-thread.hpp b/compat/run-in-thread.hpp index b425532e..c552c600 100644 --- a/compat/run-in-thread.hpp +++ b/compat/run-in-thread.hpp @@ -7,9 +7,6 @@ * copyright notice and this permission notice appear in all copies. */ -#include "macros.hpp" - -#include <cassert> #include <thread> #include <condition_variable> #include <utility> @@ -17,99 +14,56 @@ #include <QObject> #include <QThread> -namespace qt_impl_detail { - -template<typename t> -struct run_in_thread_traits +namespace impl_run_in_thread { +struct semaphore final { - using type = t; - using ret_type = t; - static inline void assign(t& lvalue, const t& rvalue) { lvalue = rvalue; } - static inline t pass(const t& val) { return val; } - template<typename F> static inline t call(F&& fun) { return std::move(fun()); } -}; + using lock_guard = std::unique_lock<std::mutex>; + std::mutex mtx; + std::condition_variable cvar; + bool flag = false; -template<typename u> -struct run_in_thread_traits<u&&> -{ - using t = typename std::remove_reference<u>::type; - using type = t; - using ret_type = u; - static inline void assign(t& lvalue, t&& rvalue) { lvalue = rvalue; } - static inline t&& pass(t&& val) { return val; } - template<typename F> static inline t&& call(F&& fun) { return std::move(fun()); } -}; + semaphore() = default; -template<> -struct run_in_thread_traits<void> -{ - using type = unsigned char; - using ret_type = void; - static inline void assign(unsigned char&, unsigned char&&) {} - static inline void pass(type&&) {} - template<typename F> static type call(F&& fun) { fun(); return type(0); } -}; + void wait() + { + lock_guard guard(mtx); + while (!flag) + cvar.wait(guard); + } + void notify() + { + lock_guard guard(mtx); + flag = true; + cvar.notify_one(); + } +}; } template<typename F> -auto never_inline -run_in_thread_sync(QObject* obj, F&& fun) - -> typename qt_impl_detail::run_in_thread_traits<decltype(fun())>::ret_type +void run_in_thread_sync(QObject* obj, F&& fun) { - using lock_guard = std::unique_lock<std::mutex>; + if (obj->thread() == QThread::currentThread()) + return (void)fun(); - using traits = qt_impl_detail::run_in_thread_traits<decltype(fun())>; - - typename traits::type ret; - - struct semaphore final - { - std::mutex mtx; - std::condition_variable cvar; - bool flag; - - semaphore() : flag(false) {} - - void wait() - { - lock_guard guard(mtx); - while (!flag) - cvar.wait(guard); - } - - void notify() - { - lock_guard guard(mtx); - flag = true; - cvar.notify_one(); - } - }; - - semaphore sem; + impl_run_in_thread::semaphore sem; { QObject src; - QObject::connect(&src, - &QObject::destroyed, - obj, - [&]() { - traits::assign(ret, traits::call(fun)); - sem.notify(); - }, - Qt::AutoConnection); + QObject::connect(&src, &QObject::destroyed, + obj, [&] { fun(); sem.notify(); }, + Qt::QueuedConnection); } sem.wait(); - return traits::pass(std::move(ret)); } template<typename F> void run_in_thread_async(QObject* obj, F&& fun) { + if (obj->thread() == QThread::currentThread()) + return (void)fun(); + QObject src; - QThread* t(obj->thread()); - assert(t); - src.moveToThread(t); - QObject::connect(&src, &QObject::destroyed, obj, std::move(fun), Qt::AutoConnection); + QObject::connect(&src, &QObject::destroyed, obj, std::forward<F>(fun), Qt::QueuedConnection); } diff --git a/compat/shm.cpp b/compat/shm.cpp index 4bd89d35..f59469dc 100644 --- a/compat/shm.cpp +++ b/compat/shm.cpp @@ -10,130 +10,36 @@ #if defined _WIN32 #include <cstring> -#include <stdio.h> +#include <cstdio> #include <accctrl.h> #include <aclapi.h> -#if !defined __WINE__ +#ifdef QT_CORE_LIB # include <QDebug> +# define warn(str, ...) (qDebug() << "shm:" str ": " << __VA_ARGS__) +#else +# define warn(str, ...) (void)0 #endif -struct secattr final -{ - bool success; - SECURITY_DESCRIPTOR* pSD; - SECURITY_ATTRIBUTES attrs; - PSID pEveryoneSID; - PACL pACL; - - void cleanup(); - secattr(DWORD perms); - ~secattr(); -}; - -void secattr::cleanup() -{ - if (pEveryoneSID) - FreeSid(pEveryoneSID); - if (pACL) - LocalFree(pACL); - if (pSD) - LocalFree(pSD); - success = false; - pSD = nullptr; - pEveryoneSID = nullptr; - pACL = nullptr; -} - -secattr::secattr(DWORD perms) : success(true), pSD(nullptr), pEveryoneSID(nullptr), pACL(nullptr) -{ - SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; - EXPLICIT_ACCESS ea; - - if(!AllocateAndInitializeSid(&SIDAuthWorld, 1, - SECURITY_WORLD_RID, - 0, 0, 0, 0, 0, 0, 0, - &pEveryoneSID)) - { - fprintf(stderr, "AllocateAndInitializeSid: %d\n", (int) GetLastError()); - goto cleanup; - } - - memset(&ea, 0, sizeof(ea)); - - ea.grfAccessPermissions = perms; - ea.grfAccessMode = SET_ACCESS; - ea.grfInheritance = NO_INHERITANCE; - ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; - ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; - ea.Trustee.ptstrName = (LPTSTR) pEveryoneSID; - - if (SetEntriesInAcl(1, &ea, NULL, &pACL) != ERROR_SUCCESS) - { - fprintf(stderr, "SetEntriesInAcl: %d\n", (int) GetLastError()); - goto cleanup; - } - - pSD = (SECURITY_DESCRIPTOR*) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); - if (pSD == nullptr) - { - fprintf(stderr, "LocalAlloc: %d\n", (int) GetLastError()); - goto cleanup; - } - - if (!InitializeSecurityDescriptor(pSD, - SECURITY_DESCRIPTOR_REVISION)) - { - fprintf(stderr, "InitializeSecurityDescriptor: %d\n", (int) GetLastError()); - goto cleanup; - } - - if (!SetSecurityDescriptorDacl(pSD, - TRUE, - pACL, - FALSE)) - { - fprintf(stderr, "SetSecurityDescriptorDacl: %d\n", (int) GetLastError()); - goto cleanup; - } - - attrs.bInheritHandle = false; - attrs.lpSecurityDescriptor = pSD; - attrs.nLength = sizeof(SECURITY_ATTRIBUTES); - - return; -cleanup: - cleanup(); -} - -secattr::~secattr() -{ - cleanup(); -} - shm_wrapper::shm_wrapper(const char* shm_name, const char* mutex_name, int map_size) { - secattr sa(GENERIC_ALL|SYNCHRONIZE); - if (mutex_name == nullptr) mutex = nullptr; else { - mutex = CreateMutexA(sa.success ? &sa.attrs : nullptr, false, mutex_name); + mutex = CreateMutexA(nullptr, false, mutex_name); if (!mutex) { - #if !defined __WINE__ - qDebug() << "CreateMutexA:" << (int) GetLastError(); - #endif + warn("CreateMutexA", (int) GetLastError()); return; } } mapped_file = CreateFileMappingA( INVALID_HANDLE_VALUE, - sa.success ? &sa.attrs : nullptr, + nullptr, PAGE_READWRITE, 0, map_size, @@ -141,9 +47,7 @@ shm_wrapper::shm_wrapper(const char* shm_name, const char* mutex_name, int map_s if (!mapped_file) { -#if !defined __WINE__ - qDebug() << "CreateFileMappingA:", (int) GetLastError(); -#endif + warn("CreateFileMappingA", (int) GetLastError()); return; } @@ -155,19 +59,15 @@ shm_wrapper::shm_wrapper(const char* shm_name, const char* mutex_name, int map_s map_size); if (!mem) - { -#if !defined __WINE__ - qDebug() << "MapViewOfFile:" << (int) GetLastError(); -#endif - } + warn("MapViewOfFile:", (int) GetLastError()); } shm_wrapper::~shm_wrapper() { - if(!UnmapViewOfFile(mem)) + if (mem && !UnmapViewOfFile(mem)) goto fail; - if (!CloseHandle(mapped_file)) + if (mapped_file && !CloseHandle(mapped_file)) goto fail; if (mutex && !CloseHandle(mutex)) @@ -176,10 +76,7 @@ shm_wrapper::~shm_wrapper() return; fail: - (void)0; -#if !defined __WINE__ - qDebug() << "failed to close mapping"; -#endif + warn("failed to close mapping", (int) GetLastError()); } bool shm_wrapper::lock() @@ -209,7 +106,7 @@ shm_wrapper::shm_wrapper(const char *shm_name, const char* /*mutex_name*/, int m strcat(filename, shm_name); fd = shm_open(filename, O_RDWR | O_CREAT, 0600); (void) ftruncate(fd, map_size); - mem = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t)0); + mem = mmap(nullptr, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t)0); } shm_wrapper::~shm_wrapper() @@ -234,7 +131,7 @@ bool shm_wrapper::success() #ifndef _WIN32 return mem != (void*) -1; #else - return mem != NULL; + return mem != nullptr; #endif } diff --git a/compat/shm.h b/compat/shm.h index 5ea6c80a..5036b335 100644 --- a/compat/shm.h +++ b/compat/shm.h @@ -19,8 +19,8 @@ #include <sys/types.h> #endif -#include "macros.hpp" #include "export.hpp" +#include "macros.h" class OTR_COMPAT_EXPORT shm_wrapper final { @@ -28,14 +28,17 @@ class OTR_COMPAT_EXPORT shm_wrapper final #if defined(_WIN32) HANDLE mutex, mapped_file; #else - int fd, size; + int fd; + unsigned size; #endif public: - never_inline shm_wrapper(const char *shm_name, const char *mutex_name, int map_size); - never_inline ~shm_wrapper(); - never_inline bool lock(); - never_inline bool unlock(); - never_inline bool success(); + shm_wrapper(const char *shm_name, const char *mutex_name, int map_size); + ~shm_wrapper(); + bool lock(); + bool unlock(); + bool success(); inline void* ptr() { return mem; } + + OTR_DISABLE_MOVE_COPY(shm_wrapper); }; diff --git a/compat/simple-mat.hpp b/compat/simple-mat.hpp index c8c9f48b..e697f76a 100644 --- a/compat/simple-mat.hpp +++ b/compat/simple-mat.hpp @@ -8,13 +8,12 @@ #pragma once -#include "export.hpp" - +#include <cstddef> #include <type_traits> #include <utility> #include <cmath> -namespace simple_mat_detail { +namespace simple_mat { // last param to fool SFINAE into overloading template<int i, int j, int> struct equals @@ -51,81 +50,83 @@ namespace simple_mat_detail { enum { Q = a == 1 ? 3 : 1 }; }; - template<typename num, int h, int w, typename...ts> + template<typename, int h, int w, typename...ts> struct is_arglist_correct { enum { value = h * w == sizeof...(ts) }; }; -template<typename num, int h_, int w_> +template<typename num, int H, int W> class Mat { - static_assert(h_ > 0 && w_ > 0, "must have positive mat dimensions"); - num data[h_][w_]; + static_assert(H > 0 && W > 0, "must have positive mat dimensions"); + num data[H][W]; public: - template<int Q = w_> std::enable_if_t<equals<Q, 1, 0>::value, num> - constexpr inline operator()(unsigned i) const& { return data[i][0]; } + // parameters W and H are rebound so that SFINAE occurs + // removing them causes a compile-time error -sh 20150811 + + template<typename t, int Q = W> std::enable_if_t<equals<Q, 1, 0>::value, num> + constexpr inline operator()(t i) const& { return data[(unsigned)i][0]; } - template<int P = h_> std::enable_if_t<equals<P, 1, 1>::value, num> - constexpr inline operator()(unsigned i) const& { return data[0][i]; } + template<typename t, int P = H> std::enable_if_t<equals<P, 1, 1>::value, num> + constexpr inline operator()(t i) const& { return data[0][(unsigned)i]; } - template<int Q = w_> std::enable_if_t<equals<Q, 1, 2>::value, num&> - constexpr inline operator()(unsigned i) & { return data[i][0]; } + template<typename t, int Q = W> std::enable_if_t<equals<Q, 1, 2>::value, num&> + constexpr inline operator()(t i) & { return data[(unsigned)i][0]; } - template<int P = h_> std::enable_if_t<equals<P, 1, 3>::value, num&> - constexpr inline operator()(unsigned i) & { return data[0][i]; } + template<typename t, int P = H> std::enable_if_t<equals<P, 1, 3>::value, num&> + constexpr inline operator()(t i) & { return data[0][(unsigned)i]; } -#define OPENTRACK_ASSERT_SWIZZLE static_assert(P == h_ && Q == w_, "") +#define OTR_MAT_ASSERT_SWIZZLE static_assert(P == H && Q == W) // const variants - template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 1, 4>::value, num> - constexpr inline x() const& { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); } + template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 1, 4>::value, num> + constexpr inline x() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(0); } - template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 2, 4>::value, num> - constexpr inline y() const& { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); } + template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 2, 4>::value, num> + constexpr inline y() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(1); } - template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 3, 4>::value, num> - constexpr inline z() const& { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); } + template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 3, 4>::value, num> + constexpr inline z() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(2); } - template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 4, 4>::value, num> - constexpr inline w() const& { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); } + template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 4, 4>::value, num> + constexpr inline w() const& { OTR_MAT_ASSERT_SWIZZLE; return operator()(3); } // mutable variants - template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 1, 4>::value, num&> - constexpr inline x() & { OPENTRACK_ASSERT_SWIZZLE; return operator()(0); } + template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 1, 4>::value, num&> + constexpr inline x() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(0); } - template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 2, 4>::value, num&> - constexpr inline y() & { OPENTRACK_ASSERT_SWIZZLE; return operator()(1); } + template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 2, 4>::value, num&> + constexpr inline y() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(1); } - template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 3, 4>::value, num&> - constexpr inline z() & { OPENTRACK_ASSERT_SWIZZLE; return operator()(2); } + template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 3, 4>::value, num&> + constexpr inline z() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(2); } - template<int P = h_, int Q = w_> std::enable_if_t<maybe_add_swizzle<P, Q, 4, 4>::value, num&> - constexpr inline w() & { OPENTRACK_ASSERT_SWIZZLE; return operator()(3); } + template<int P = H, int Q = W> std::enable_if_t<maybe_add_swizzle<P, Q, 4, 4>::value, num&> + constexpr inline w() & { OTR_MAT_ASSERT_SWIZZLE; return operator()(3); } - // parameters w_ and h_ are rebound so that SFINAE occurs - // removing them causes a compile-time error -sh 20150811 - - template<int P = h_, int Q = w_> - std::enable_if_t<is_vector<P, Q>::value, num> - norm() const + template<int P = H, int Q = W> + constexpr auto norm_squared() const -> std::enable_if_t<is_vector<P, Q>::value, num> { - static_assert(P == h_ && Q == w_, ""); + static_assert(P == H && Q == W); const num val = dot(*this); + constexpr num eps = num(1e-4); - if (val < num(1e-4)) + if (val < eps) return num(0); else - return std::sqrt(val); + return val; } - template<int R, int S, int P = h_, int Q = w_> + inline auto norm() const { return num(std::sqrt(norm_squared())); } + + template<int R, int S, int P = H, int Q = W> std::enable_if_t<is_vector_pair<R, S, P, Q>::value, num> constexpr dot(const Mat<num, R, S>& p2) const { - static_assert(P == h_ && Q == w_, ""); + static_assert(P == H && Q == W); num ret = 0; constexpr int len = vector_len<R, S>::value; @@ -134,166 +135,201 @@ public: return ret; } - template<int R, int S, int P = h_, int Q = w_> + template<int R, int S, int P = H, int Q = W> std::enable_if_t<is_dim3<P, Q, R, S>::value, Mat<num, is_dim3<P, Q, R, S>::P, is_dim3<P, Q, R, S>::Q>> constexpr cross(const Mat<num, R, S>& b) const { - static_assert(P == h_ && Q == w_, ""); - auto& a = *this; + static_assert(P == H && Q == W); + const auto& a = *this; return Mat<num, R, S>(a.y()*b.z() - a.z()*b.y(), a.z()*b.x() - a.x()*b.z(), a.x()*b.y() - a.y()*b.x()); } - constexpr Mat<num, h_, w_> operator+(const Mat<num, h_, w_>& other) const + constexpr Mat<num, H, W> operator+(const Mat<num, H, W>& other) const { - Mat<num, h_, w_> ret; - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) + Mat<num, H, W> ret; + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) ret(j, i) = data[j][i] + other.data[j][i]; return ret; } - constexpr Mat<num, h_, w_> operator-(const Mat<num, h_, w_>& other) const + constexpr Mat<num, H, W> operator-(const Mat<num, H, W>& other) const { - Mat<num, h_, w_> ret; - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) + Mat<num, H, W> ret; + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) ret(j, i) = data[j][i] - other.data[j][i]; return ret; } - constexpr Mat<num, h_, w_> operator+(const num other) const + constexpr Mat<num, H, W> operator+(const num other) const { - Mat<num, h_, w_> ret; - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) + Mat<num, H, W> ret; + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) ret(j, i) = data[j][i] + other; return ret; } - constexpr Mat<num, h_, w_> operator-(const num other) const + constexpr Mat<num, H, W> operator-(const num other) const { - Mat<num, h_, w_> ret; - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) + Mat<num, H, W> ret; + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) ret(j, i) = data[j][i] - other; return ret; } template<int p> - constexpr Mat<num, h_, p> operator*(const Mat<num, w_, p>& other) const + constexpr Mat<num, H, p> operator*(const Mat<num, W, p>& other) const { - Mat<num, h_, p> ret; - for (int k = 0; k < h_; k++) + Mat<num, H, p> ret; + for (int k = 0; k < H; k++) for (int i = 0; i < p; i++) { ret(k, i) = 0; - for (int j = 0; j < w_; j++) + for (int j = 0; j < W; j++) ret(k, i) += data[k][j] * other(j, i); } return ret; } - constexpr Mat<num, h_, w_> mult_elementwise(const Mat<num, h_, w_>& other) const& + constexpr Mat<num, H, W> mult_elementwise(const Mat<num, H, W>& other) const& { - Mat<num, h_, w_> ret; + Mat<num, H, W> ret; - for (unsigned j = 0; j < h_; j++) - for (unsigned i = 0; i < w_; i++) + for (unsigned j = 0; j < H; j++) + for (unsigned i = 0; i < W; i++) ret(j, i) = data[j][i] * other.data[j][i]; return ret; } template<typename t, typename u> - constexpr inline num operator()(t j, u i) const& { return data[(int) j][(int) i]; } + constexpr inline num operator()(t j, u i) const& { return data[(unsigned)j][(unsigned)i]; } template<typename t, typename u> - constexpr inline num& operator()(t j, u i) & { return data[(int) j][(int) i]; } + constexpr inline num& operator()(t j, u i) & { return data[(unsigned)j][(unsigned)i]; } -#ifdef __GNUG__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmissing-braces" +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wmissing-braces" #endif - template<typename... ts, int h__ = h_, int w__ = w_, - typename = std::enable_if_t<is_arglist_correct<num, h__, w__, ts...>::value>> + template<typename... ts, int h2 = H, int w2 = W, + typename = std::enable_if_t<is_arglist_correct<num, h2, w2, ts...>::value>> constexpr Mat(const ts... xs) : data{static_cast<num>(xs)...} { - static_assert(h__ == h_ && w__ == w_, ""); + static_assert(h2 == H && w2 == W); } -#ifdef __GNUG__ -# pragma GCC diagnostic pop +#ifdef __clang__ +# pragma clang diagnostic pop #endif constexpr Mat() { - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) data[j][i] = num(0); } - Mat(const num* mem) + constexpr Mat(const num* mem) { - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) - data[j][i] = mem[i*h_+j]; + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) + data[j][i] = mem[i*H+j]; } - operator num*() { return reinterpret_cast<num*>(data); } - operator const num*() const { return reinterpret_cast<const num*>(data); } + constexpr Mat(const Mat<num, H, W>& x) + { + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) + data[j][i] = x(j, i); + } + + constexpr operator num*() & { return (num*)data; } + constexpr operator const num*() const& { return (const num*)data; } // XXX add more operators as needed, third-party dependencies mostly // not needed merely for matrix algebra -sh 20141030 - template<int h__ = h_> - static std::enable_if_t<h_ == w_, Mat<num, h__, h__>> eye() + template<int H_ = H> + static constexpr std::enable_if_t<H == W, Mat<num, H_, H_>> eye() { - static_assert(h_ == h__, ""); + static_assert(H == H_); - Mat<num, h_, h_> ret; - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) + Mat<num, H, H> ret; + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) ret.data[j][i] = 0; - for (int i = 0; i < h_; i++) + for (int i = 0; i < H; i++) ret.data[i][i] = 1; return ret; } - constexpr Mat<num, w_, h_> t() const + constexpr Mat<num, W, H> t() const { - Mat<num, w_, h_> ret; + Mat<num, W, H> ret; - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) ret(i, j) = data[j][i]; return ret; } + + constexpr Mat<num, H, W>& operator=(const Mat<num, H, W>& rhs) + { + for (unsigned j = 0; j < H; j++) + for (unsigned i = 0; i < W; i++) + data[j][i] = rhs(j, i); + + return *this; + } }; +template<unsigned k, typename num, int h, int w> +constexpr num get(const Mat<num, h, w>& m) { return m(k); } + +template<unsigned k, typename num, int h, int w> +constexpr num& get(Mat<num, h, w>& m) { return m(k); } + +} // ns simple_mat + +template<typename num, int h, int w> +using Mat = simple_mat::Mat<num, h, w>; + template<typename num, int h, int w> constexpr Mat<num, h, w> operator*(num scalar, const Mat<num, h, w>& mat) { return mat * scalar; } -template<typename num, int h_, int w_> -constexpr Mat<num, h_, w_> operator*(const Mat<num, h_, w_>& self, num other) +template<typename num, int H, int W> +constexpr Mat<num, H, W> operator*(const Mat<num, H, W>& self, num other) { - Mat<num, h_, w_> ret; - for (int j = 0; j < h_; j++) - for (int i = 0; i < w_; i++) + Mat<num, H, W> ret; + for (int j = 0; j < H; j++) + for (int i = 0; i < W; i++) ret(j, i) = self(j, i) * other; return ret; } -} // ns simple_mat_detail +namespace std { + template<typename num, int H, int W> + struct tuple_size<Mat<num, H, W>> : + std::integral_constant<std::size_t, H == 1 || W == 1 ? W * H : 0> + {}; -template<typename num, int h, int w> -using Mat = simple_mat_detail::Mat<num, h, w>; + template<std::size_t k, typename num, int h, int w> + struct tuple_element<k, Mat<num, h, w>> + { + using type = std::remove_const_t<std::remove_reference_t<num>>; + }; +} // ns std diff --git a/compat/sleep.cpp b/compat/sleep.cpp new file mode 100644 index 00000000..e64e6254 --- /dev/null +++ b/compat/sleep.cpp @@ -0,0 +1,23 @@ +#include "sleep.hpp" + +#ifdef _WIN32 +# include <windows.h> +#else +# include <unistd.h> +#endif + +namespace portable +{ + void sleep(int milliseconds) + { + if (milliseconds > 0) + { +#ifdef _WIN32 + + Sleep((unsigned)milliseconds); +#else + usleep(unsigned(milliseconds) * 1000U); // takes microseconds +#endif + } + } +} diff --git a/compat/sleep.hpp b/compat/sleep.hpp index 4cd7a397..9419b36b 100644 --- a/compat/sleep.hpp +++ b/compat/sleep.hpp @@ -1,24 +1,7 @@ #pragma once -#ifdef _WIN32 -# include <windows.h> -#else -# include <unistd.h> -#endif +#include "export.hpp" -namespace portable -{ -#ifdef _WIN32 - inline void sleep(int milliseconds) - { - if (milliseconds > 0) - Sleep(milliseconds); - } -#else - inline void sleep(int milliseconds) - { - if (milliseconds > 0) - usleep(unsigned(milliseconds) * 1000U); // takes microseconds - } -#endif +namespace portable { + OTR_COMPAT_EXPORT void sleep(int milliseconds); } diff --git a/compat/sysexits.hpp b/compat/sysexits.hpp new file mode 100644 index 00000000..6747fc88 --- /dev/null +++ b/compat/sysexits.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include <cstdlib> // for EXIT_SUCCESS, EXIT_FAILRUE + +#ifndef _WIN32 +# include <sysexits.h> +#else +// this conforms to BSD sysexits(3) +// reference the manual page on FreeBSD or Linux for semantics +# define EX_OK 0 +# define EX_USAGE 64 +# define EX_DATAERR 65 +# define EX_NOINPUT 66 +# define EX_NOUSER 67 +# define EX_NOHOST 68 +# define EX_UNAVAILABLE 69 +# define EX_SOFTWARE 70 +# define EX_OSERR 71 +# define EX_OSFILE 72 +# define EX_CANTCREAT 73 +# define EX_IOERR 74 +# define EX_TEMPFAIL 75 +# define EX_PROTOCOL 76 +# define EX_NOPERM 77 +# define EX_CONFIG 78 +#endif + + diff --git a/compat/thread-name.cpp b/compat/thread-name.cpp new file mode 100644 index 00000000..08a7d628 --- /dev/null +++ b/compat/thread-name.cpp @@ -0,0 +1,87 @@ +#include "thread-name.hpp" +#ifdef _WIN32 +# include <QDebug> +# include <windows.h> +#else +# include <QThread> +#endif + +namespace portable { + +#ifdef _WIN32 + +#ifdef _MSC_VER +struct THREADNAME_INFO +{ + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + HANDLE dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero +}; + +static inline +void set_curthread_name_old_(const char* name) +{ + HANDLE curthread = GetCurrentThread(); + THREADNAME_INFO info; // NOLINT(cppcoreguidelines-pro-type-member-init) + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = curthread; + info.dwFlags = 0; + __try + { + static_assert(sizeof(info) % sizeof(unsigned) == 0); + unsigned sz = sizeof(info)/sizeof(unsigned); + RaiseException(0x406D1388, 0, sz, (const ULONG_PTR*)&info); + } + __except (EXCEPTION_CONTINUE_EXECUTION) + { + } +} + +static inline +void set_curthread_name_old(const QString& name_) +{ + QByteArray str = name_.toLocal8Bit(); + const char* name = str.constData(); + + set_curthread_name_old_(name); +} +#else + +static inline void set_curthread_name_old(const QString&) {} + +#endif + +using SetThreadDescr_type = HRESULT (__stdcall *)(HANDLE, const wchar_t*); + +static SetThreadDescr_type get_funptr() +{ + HMODULE module; + if (GetModuleHandleExA(0, "kernel32.dll", &module)) + return (SetThreadDescr_type)GetProcAddress(module, "SetThreadDescription"); + else + return nullptr; +} + +void set_curthread_name(const QString& name) +{ + static_assert(sizeof(wchar_t) == sizeof(decltype(*QString().utf16()))); + static const SetThreadDescr_type fn = get_funptr(); + + if (fn) + fn(GetCurrentThread(), (const wchar_t*)name.utf16()); + else + set_curthread_name_old(name); +} + +#else + +void set_curthread_name(const QString& name) +{ + QThread::currentThread()->setObjectName(name); +} + +#endif + +} // ns portable diff --git a/compat/thread-name.hpp b/compat/thread-name.hpp new file mode 100644 index 00000000..21256003 --- /dev/null +++ b/compat/thread-name.hpp @@ -0,0 +1,6 @@ +#pragma once +#include "export.hpp" +#include <QString> +namespace portable { + OTR_COMPAT_EXPORT void set_curthread_name(const QString& name); +} diff --git a/compat/time.hpp b/compat/time.hpp index d34e4e8c..0d171038 100644 --- a/compat/time.hpp +++ b/compat/time.hpp @@ -7,17 +7,9 @@ namespace time_units { template<typename repr, typename ratio = std::ratio<1>> using duration = std::chrono::duration<repr, ratio>; -template<typename t, typename u> -static inline constexpr auto time_cast(const u& in) -{ - return std::chrono::duration_cast<t>(in); -} - -using secs = duration<double>; -using secs_ = duration<long>; -using ms = duration<double, std::milli>; -using us = duration<double, std::micro>; -using us_ = duration<long long, std::micro>; -using ns = duration<long long, std::nano>; +using secs = duration<float>; +using ms = duration<float, std::milli>; +using us = duration<float, std::micro>; +using ns = duration<float, std::nano>; } // ns time_units diff --git a/compat/timer.cpp b/compat/timer.cpp index 162ba16f..fdea185a 100644 --- a/compat/timer.cpp +++ b/compat/timer.cpp @@ -6,10 +6,12 @@ * notice appear in all copies. */ -#include <cassert> +#undef NDEBUG #include "timer.hpp" +#include <cassert> #include <cmath> +#include <QDebug> Timer::Timer() { @@ -21,40 +23,25 @@ void Timer::start() gettime(&state); } -// nanoseconds - -long long Timer::elapsed_nsecs() const -{ - timespec cur{}; - gettime(&cur); - return conv_nsecs(cur); -} - -long long Timer::conv_nsecs(const struct timespec& cur) const +struct timespec Timer::get_delta() const { - return (cur.tv_sec - state.tv_sec) * 1000000000LL + (cur.tv_nsec - state.tv_nsec); -} - -// microseconds - -double Timer::elapsed_usecs() const -{ - timespec cur{}; - gettime(&cur); - const long long nsecs = conv_nsecs(cur); - return nsecs * 1e-3; + struct timespec ts; // NOLINT + gettime(&ts); + return { ts.tv_sec - state.tv_sec, ts.tv_nsec - state.tv_nsec }; } // milliseconds double Timer::elapsed_ms() const { - return elapsed_usecs() / 1000.; + struct timespec delta = get_delta(); + return delta.tv_sec * 1000 + delta.tv_nsec * 1e-6; } double Timer::elapsed_seconds() const { - return double(elapsed_nsecs() * 1e-9L); + struct timespec delta = get_delta(); + return delta.tv_sec + delta.tv_nsec * 1e-9; } // -- @@ -64,29 +51,24 @@ double Timer::elapsed_seconds() const #if defined (_WIN32) # include <windows.h> -static LARGE_INTEGER otr_get_clock_frequency() +static auto otr_get_clock_frequency() { LARGE_INTEGER freq{}; - const BOOL ret = QueryPerformanceFrequency(&freq); + BOOL ret = QueryPerformanceFrequency(&freq); assert(ret && "QueryPerformanceFrequency failed"); - return freq; + return freq.QuadPart; } -static void otr_clock_gettime(timespec* ts) +void Timer::gettime(timespec* state) { - static const LARGE_INTEGER freq = otr_get_clock_frequency(); - + static const auto freq = otr_get_clock_frequency(); LARGE_INTEGER d; + BOOL ret = QueryPerformanceCounter(&d); + assert(ret && "QueryPerformanceCounter failed"); - (void) QueryPerformanceCounter(&d); - - using ll = long long; - const ll part = ll(std::roundl((d.QuadPart * 1000000000.L) / ll(freq.QuadPart))); - using t_s = decltype(ts->tv_sec); - using t_ns = decltype(ts->tv_nsec); - - ts->tv_sec = t_s(part / 1000000000); - ts->tv_nsec = t_ns(part % 1000000000); + constexpr int nsec = 1'000'000 * 1000; + state->tv_sec = (time_t)(d.QuadPart/freq); + state->tv_nsec = (decltype(state->tv_nsec))(d.QuadPart % freq * nsec / freq); } #elif defined __MACH__ @@ -96,32 +78,30 @@ static void otr_clock_gettime(timespec* ts) static mach_timebase_info_data_t otr_get_mach_frequency() { mach_timebase_info_data_t timebase_info; - (void) mach_timebase_info(&timebase_info); + kern_return_t status = mach_timebase_info(&timebase_info); + assert(status == KERN_SUCCESS && "mach_timebase_info failed"); return timebase_info; } -static void otr_clock_gettime(timespec* ts) +void Timer::gettime(timespec* ts) { static const mach_timebase_info_data_t timebase_info = otr_get_mach_frequency(); uint64_t state, nsec; state = mach_absolute_time(); nsec = state * timebase_info.numer / timebase_info.denom; - ts->tv_sec = nsec / 1000000000L; - ts->tv_nsec = nsec % 1000000000L; + ts->tv_sec = nsec / 1000000000UL; + ts->tv_nsec = nsec % 1000000000UL; +} + +#else + +void Timer::gettime(timespec* ts) +{ + int error = clock_gettime(CLOCK_MONOTONIC, ts); + assert(error == 0 && "clock_gettime failed"); } #endif // common -void Timer::gettime(timespec* state) -{ -#if defined(_WIN32) || defined(__MACH__) - otr_clock_gettime(state); -#elif defined CLOCK_MONOTONIC - const int res = clock_gettime(CLOCK_MONOTONIC, state); - assert(res == 0 && "must support CLOCK_MONOTONIC"); -#else -# error "timer query method not known" -#endif -} diff --git a/compat/timer.hpp b/compat/timer.hpp index 03b537ac..47072226 100644 --- a/compat/timer.hpp +++ b/compat/timer.hpp @@ -10,43 +10,19 @@ #include "export.hpp" #include "time.hpp" - #include <ctime> -class OTR_COMPAT_EXPORT Timer final +struct OTR_COMPAT_EXPORT Timer final { - struct timespec state; - long long conv_nsecs(const struct timespec& cur) const; - - static void gettime(struct timespec* state); - - using ns = time_units::ns; -public: Timer(); void start(); - template<typename t> - t elapsed() const - { - using namespace time_units; - return time_cast<t>(ns(elapsed_nsecs())); - } - - template<typename t> - bool is_elapsed(const t& time_value) - { - using namespace time_units; - - if (unlikely(elapsed<ns>() >= time_value)) - { - start(); - return true; - } - return false; - } - - long long elapsed_nsecs() const; - double elapsed_usecs() const; double elapsed_ms() const; double elapsed_seconds() const; + +private: + struct timespec state {}; + static void gettime(struct timespec* state); + struct timespec get_delta() const; + using ns = time_units::ns; }; diff --git a/compat/tr.hpp b/compat/tr.hpp new file mode 100644 index 00000000..c5a561e7 --- /dev/null +++ b/compat/tr.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include <QObject> + +// The class does nothing except provide a fake assignment operator for QObject +// It's meant to be used inside classes that need i18n support but are returned by value. + +struct TR : QObject +{ + TR() = default; + TR(const TR&) : QObject(nullptr) {} + + TR& operator=(const TR&) { return *this; } +}; diff --git a/compat/variance.hpp b/compat/variance.hpp index 7a83154c..8abefd33 100644 --- a/compat/variance.hpp +++ b/compat/variance.hpp @@ -1,7 +1,6 @@ #pragma once #include <cmath> -#include <cinttypes> // no copyright information other than the attribution below. @@ -18,19 +17,18 @@ // Comparison of Several Algorithms for Computing Sample Means and Variances. // Journal of the American Statistical Association, Vol. 69, No. 348, 859-866. -class variance +struct variance final { - double m_old, m_new, s_old, s_new; - std::uintptr_t cnt; - -public: - using size_type = std::uintptr_t; + using size_type = unsigned; - variance() : cnt(0) {} + variance& operator=(variance const&) = default; + void clear() { *this = {}; } - void clear() { *this = variance(); } + constexpr size_type count() const { return cnt; } - size_type count() { return cnt; } + constexpr double avg() const { return cnt > 0 ? m_new : 0; } + constexpr double Var() const { return cnt > 1 ? s_new/(cnt - 1) : 0; } + double stddev() const { return std::sqrt(Var()); } void input(double x) { @@ -51,7 +49,7 @@ public: } } - double avg() const { return cnt > 0 ? m_new : 0; } - double Var() const { return cnt > 1 ? s_new/(cnt - 1) : 0; } - double stddev() const { return std::sqrt(Var()); } +private: + double m_old, m_new, s_old, s_new; + size_type cnt = 0; }; |