#ifndef UTIL_H #define UTIL_H #include #include #include #include #include #include #include #define CONCAT_IMPL(a, b) a##b #define CONCAT(a, b) CONCAT_IMPL(a, b) #define _ CONCAT(unused_, __LINE__) #define SET_TERMINATE \ std::set_terminate([]() noexcept \ { \ EBMessageBox(What(), L"Fatal Error", MB_ICONERROR); \ _Exit(1); \ }) /* Scope guard. */ template struct Finally { F f; inline Finally(F f) noexcept : f(f) {} inline ~Finally() { f(); } }; #define FINALLY Finally _ = [=]() /* Unique is similar to unique_ptr, but it is designed for pointers * and handles of type T, where T is not an RAII class. Further, the * managed object is assumed to be in an invalid state until checked * with Good or Bad. Upon destruction, the object, if valid, is passed * to a given destructor function F. */ template struct Unique { union { T v; }; bool ok = false; Unique() noexcept {} Unique(T v) noexcept : v(v) {} Unique& operator =(T v_) noexcept { Destroy(); v = v_; ok = false; return *this; } Unique(Unique&& other) noexcept : v(std::move(other.v)), ok(other.ok) { other.ok = false; } Unique& operator =(Unique&& other) noexcept { Destroy(); v = std::move(other.v); ok = other.ok; other.ok = false; return *this; } bool Good(T u) noexcept { return !(ok = v == u); } bool Bad(T u) noexcept { return !(ok = v != u); } template class UniqueOk; template UniqueOk ThrowIf(T u) noexcept { if (v == u) throw E; return std::move(*this); } ~Unique() { Destroy(); } private: void Destroy() { if (ok) { if constexpr (F) F(v); v.~T(); } } }; /* UniqueOk contains a Unique that has already been validated. */ template struct UniqueOk { T v; UniqueOk(Unique&& u) : v(std::move(u.v)) { assert(u.ok, "UniqueOk may not be constructed from non-ok Unique"); u.ok = false; } UniqueOk& operator =(Unique&& u) { assert(u.ok, "UniqueOk may not be constructed from non-ok Unique"); F(v); v = std::move(u.v); u.ok = false; return *this; } ~UniqueOk() { F(v); } }; /* Buf is a span-like structure of a buffer and its size. */ template struct Buf { T* data; size_t c; Buf(T* data, size_t c) noexcept : data(data), c(c) {} Buf(std::basic_string& s) noexcept : data(s.data()), c(s.capacity()) {} template Buf(T (&data)[N]) noexcept : data(data), c(N) {} operator T*() const noexcept { return data; } T& operator *() const noexcept { return *data; } T& operator [](size_t i) const noexcept { return data[i]; } Buf operator +(size_t i) const noexcept { return {data+i, c-i}; } //T operator -(size_t i) { return {data-i, c+i}; } }; inline int Cmp(const int a, const int b) { if (a == b) return 0; if (a > b) return 1; return -1; } inline size_t Min(size_t a, size_t b) { return a < b? a: b; } template inline size_t Len(T (&)[N]) { return N-1; } /* Format wide string. */ template inline std::enable_if_t...>, int> Swprintf(Buf buf, const wchar_t* const fmt, T... xs) { return _snwprintf_s(buf, buf.c, _TRUNCATE, fmt, xs...); } /* Format static narrow string. */ template inline std::enable_if_t...>, int> Sprintf(Buf buf, const char* const fmt, T... xs) { return _snprintf_s(buf, buf.c, _TRUNCATE, fmt, xs...); } /* Copy to static wide string buffer. */ inline wchar_t* Wcscpy(Buf dst, const wchar_t* const src) { const size_t len = Min(dst.c, wcslen(src)+1); memcpy(dst, src, len*sizeof(wchar_t)); dst[len-1] = 0; return dst; } /* Copy to static narrow string buffer. */ inline char* Strcpy(Buf dst, const char* const src) { const size_t len = Min(dst.c, strlen(src)+1); memcpy(dst, src, len); dst[len-1] = 0; return dst; } #endif