Files
rgbds/include/helpers.hpp
2024-03-04 14:22:49 -05:00

94 lines
2.3 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)
// 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)
// Convenience feature for visiting variants.
template<typename... Ts>
struct Visitor : Ts... {
using Ts::operator()...;
};
template<typename... Ts>
Visitor(Ts...) -> Visitor<Ts...>;
#endif // HELPERS_H