mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
98 lines
2.4 KiB
C++
98 lines
2.4 KiB
C++
/* SPDX-License-Identifier: MIT */
|
|
|
|
#ifndef HELPERS_H
|
|
#define HELPERS_H
|
|
|
|
// Ideally, we'd use `__has_attribute` and `__has_builtin`, but these were only introduced in GCC 9
|
|
#ifdef __GNUC__ // GCC or compatible
|
|
#define format_(archetype, str_index, first_arg) \
|
|
__attribute__((format(archetype, str_index, first_arg)))
|
|
#define attr_(...) __attribute__((__VA_ARGS__))
|
|
// In release builds, define "unreachable" as such, but trap in debug builds
|
|
#ifdef NDEBUG
|
|
#define unreachable_ __builtin_unreachable
|
|
#else
|
|
#define unreachable_ __builtin_trap
|
|
#endif
|
|
#else
|
|
// Unsupported, but no need to throw a fit
|
|
#define format_(archetype, str_index, first_arg)
|
|
#define attr_(...)
|
|
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
|
// Note that executing this is undefined behavior (declared [[noreturn]], but does return)
|
|
[[noreturn]] static inline void unreachable_() {
|
|
}
|
|
#endif
|
|
|
|
// Use builtins whenever possible, and shim them otherwise
|
|
#ifdef __GNUC__ // GCC or compatible
|
|
#define ctz __builtin_ctz
|
|
#define clz __builtin_clz
|
|
|
|
#elif defined(_MSC_VER)
|
|
#include <assert.h>
|
|
#include <intrin.h>
|
|
#pragma intrinsic(_BitScanReverse, _BitScanForward)
|
|
static inline int ctz(unsigned int x) {
|
|
unsigned long cnt;
|
|
|
|
assert(x != 0);
|
|
_BitScanForward(&cnt, x);
|
|
return cnt;
|
|
}
|
|
static inline int clz(unsigned int x) {
|
|
unsigned long cnt;
|
|
|
|
assert(x != 0);
|
|
_BitScanReverse(&cnt, x);
|
|
return 31 - cnt;
|
|
}
|
|
|
|
#else
|
|
#include <limits.h>
|
|
static inline int ctz(unsigned int x) {
|
|
int cnt = 0;
|
|
|
|
while (!(x & 1)) {
|
|
x >>= 1;
|
|
cnt++;
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
static inline int clz(unsigned int x) {
|
|
int cnt = 0;
|
|
|
|
while (x <= UINT_MAX / 2) {
|
|
x <<= 1;
|
|
cnt++;
|
|
}
|
|
return cnt;
|
|
}
|
|
#endif
|
|
|
|
// Macros for stringification
|
|
#define STR(x) #x
|
|
#define EXPAND_AND_STR(x) STR(x)
|
|
|
|
// Macros for concatenation
|
|
#define CAT(x, y) x##y
|
|
#define EXPAND_AND_CAT(x, y) CAT(x, y)
|
|
|
|
// Obtaining the size of an array; `arr` must be an expression, not a type!
|
|
// (Having two instances of `arr` is OK because the contents of `sizeof` are not evaluated.)
|
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof *(arr))
|
|
|
|
// For lack of <ranges>, this adds some more brevity
|
|
#define RANGE(s) std::begin(s), std::end(s)
|
|
|
|
// For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))`
|
|
template<typename T>
|
|
struct Defer {
|
|
T deferred;
|
|
Defer(T func) : deferred(func) {}
|
|
~Defer() { deferred(); }
|
|
};
|
|
|
|
#endif // HELPERS_H
|