diff --git a/include/helpers.hpp b/include/helpers.hpp index 67e0eb88..55b40640 100644 --- a/include/helpers.hpp +++ b/include/helpers.hpp @@ -93,15 +93,14 @@ static inline int clz(unsigned int x) { #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 , this adds some more brevity #define RANGE(s) std::begin(s), std::end(s) -// MSVC does not inline `strlen()` or `.length()` of a constant string, so we use `sizeof` -#define QUOTEDSTRLEN(s) (sizeof(s) - 1) +// MSVC does not inline `strlen()` or `.length()` of a constant string +template +static constexpr int literal_strlen(char const (&)[N]) { + return N - 1; +} // For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))` template diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp index bd11dd34..9bcbcd1c 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -500,27 +500,27 @@ BufferedContent::~BufferedContent() { } void BufferedContent::advance() { - assume(offset < ARRAY_SIZE(buf)); + assume(offset < std::size(buf)); offset++; - if (offset == ARRAY_SIZE(buf)) + if (offset == std::size(buf)) offset = 0; // Wrap around if necessary assume(size > 0); size--; } void BufferedContent::refill() { - size_t target = ARRAY_SIZE(buf) - size; // Aim: making the buf full + size_t target = std::size(buf) - size; // Aim: making the buf full // Compute the index we'll start writing to - size_t startIndex = (offset + size) % ARRAY_SIZE(buf); + size_t startIndex = (offset + size) % std::size(buf); // If the range to fill passes over the buffer wrapping point, we need two reads - if (startIndex + target > ARRAY_SIZE(buf)) { - size_t nbExpectedChars = ARRAY_SIZE(buf) - startIndex; + if (startIndex + target > std::size(buf)) { + size_t nbExpectedChars = std::size(buf) - startIndex; size_t nbReadChars = readMore(startIndex, nbExpectedChars); startIndex += nbReadChars; - if (startIndex == ARRAY_SIZE(buf)) + if (startIndex == std::size(buf)) startIndex = 0; // If the read was incomplete, don't perform a second read @@ -534,7 +534,7 @@ void BufferedContent::refill() { size_t BufferedContent::readMore(size_t startIndex, size_t nbChars) { // This buffer overflow made me lose WEEKS of my life. Never again. - assume(startIndex + nbChars <= ARRAY_SIZE(buf)); + assume(startIndex + nbChars <= std::size(buf)); ssize_t nbReadChars = read(fd, &buf[startIndex], nbChars); if (nbReadChars == -1) @@ -720,7 +720,7 @@ int LexerState::peekChar() { auto &cbuf = content.get(); if (cbuf.size == 0) cbuf.refill(); - assume(cbuf.offset < ARRAY_SIZE(cbuf.buf)); + assume(cbuf.offset < std::size(cbuf.buf)); if (cbuf.size > 0) return static_cast(cbuf.buf[cbuf.offset]); } @@ -748,11 +748,11 @@ int LexerState::peekCharAhead() { return static_cast(view.span.ptr[view.offset + distance]); } else { auto &cbuf = content.get(); - assume(distance < ARRAY_SIZE(cbuf.buf)); + assume(distance < std::size(cbuf.buf)); if (cbuf.size <= distance) cbuf.refill(); if (cbuf.size > distance) - return static_cast(cbuf.buf[(cbuf.offset + distance) % ARRAY_SIZE(cbuf.buf)]); + return static_cast(cbuf.buf[(cbuf.offset + distance) % std::size(cbuf.buf)]); } // If there aren't enough chars, give up @@ -2339,7 +2339,7 @@ Capture lexer_CaptureRept() { endCapture(capture); // The final ENDR has been captured, but we don't want it! // We know we have read exactly "ENDR", not e.g. an EQUS - capture.span.size -= QUOTEDSTRLEN("ENDR"); + capture.span.size -= literal_strlen("ENDR"); return capture; } depth--; @@ -2385,7 +2385,7 @@ Capture lexer_CaptureMacro() { endCapture(capture); // The ENDM has been captured, but we don't want it! // We know we have read exactly "ENDM", not e.g. an EQUS - capture.span.size -= QUOTEDSTRLEN("ENDM"); + capture.span.size -= literal_strlen("ENDM"); return capture; default: diff --git a/src/asm/main.cpp b/src/asm/main.cpp index 4346a0ac..66713a1c 100644 --- a/src/asm/main.cpp +++ b/src/asm/main.cpp @@ -41,7 +41,7 @@ static std::string make_escape(std::string &str) { break; escaped.append(str, pos, nextPos - pos); escaped.append("$$"); - pos = nextPos + QUOTEDSTRLEN("$"); + pos = nextPos + literal_strlen("$"); } escaped.append(str, pos, str.length() - pos); return escaped; diff --git a/src/asm/warning.cpp b/src/asm/warning.cpp index b96b4a08..2288c106 100644 --- a/src/asm/warning.cpp +++ b/src/asm/warning.cpp @@ -149,16 +149,16 @@ void processWarningFlag(char const *flag) { if (rootFlag.starts_with("error=")) { // `-Werror=` enables the flag as an error state = {.state = WARNING_ENABLED, .error = WARNING_ENABLED}; - rootFlag.erase(0, QUOTEDSTRLEN("error=")); + rootFlag.erase(0, literal_strlen("error=")); } else if (rootFlag.starts_with("no-error=")) { // `-Wno-error=` prevents the flag from being an error, // without affecting whether it is enabled state = {.state = WARNING_DEFAULT, .error = WARNING_DISABLED}; - rootFlag.erase(0, QUOTEDSTRLEN("no-error=")); + rootFlag.erase(0, literal_strlen("no-error=")); } else if (rootFlag.starts_with("no-")) { // `-Wno-` disables the flag state = {.state = WARNING_DISABLED, .error = WARNING_DEFAULT}; - rootFlag.erase(0, QUOTEDSTRLEN("no-")); + rootFlag.erase(0, literal_strlen("no-")); } else { // `-W` enables the flag state = {.state = WARNING_ENABLED, .error = WARNING_DEFAULT}; diff --git a/src/gfx/process.cpp b/src/gfx/process.cpp index a80a5033..9aeeb6e6 100644 --- a/src/gfx/process.cpp +++ b/src/gfx/process.cpp @@ -623,7 +623,7 @@ static std::tuple, std::vector> for (uint16_t cgbColor : list) { ptr += snprintf(ptr, sizeof(", $XXXX"), ", $%04x", cgbColor); } - return &buf[QUOTEDSTRLEN(", ")]; + return &buf[literal_strlen(", ")]; }; // Iterate through proto-palettes, and try mapping them to the specified palettes diff --git a/src/link/object.cpp b/src/link/object.cpp index 20b4b11b..4253d30d 100644 --- a/src/link/object.cpp +++ b/src/link/object.cpp @@ -528,7 +528,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) { int matchedElems; if (fscanf(file, RGBDS_OBJECT_VERSION_STRING "%n", &matchedElems) == 1 - && matchedElems != QUOTEDSTRLEN(RGBDS_OBJECT_VERSION_STRING)) + && matchedElems != literal_strlen(RGBDS_OBJECT_VERSION_STRING)) errx("%s: Not a RGBDS object file", fileName); verbosePrint("Reading object file %s\n", fileName); diff --git a/src/version.cpp b/src/version.cpp index 1dec1fcb..1b346677 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -29,7 +29,7 @@ extern "C" { #endif char const *get_package_version_string() { - if constexpr (QUOTEDSTRLEN(BUILD_VERSION_STRING) > 0) { + if constexpr (literal_strlen(BUILD_VERSION_STRING) > 0) { return BUILD_VERSION_STRING; } // Fallback if version string can't be obtained from Git