diff --git a/include/backtrace.hpp b/include/backtrace.hpp index ba945a3f..95710898 100644 --- a/include/backtrace.hpp +++ b/include/backtrace.hpp @@ -9,6 +9,7 @@ #include #include +#include "helpers.hpp" // InvocableR #include "style.hpp" #define TRACE_SEPARATOR "<-" @@ -25,8 +26,12 @@ extern Tracing tracing; bool trace_ParseTraceDepth(char const *arg); -template -void trace_PrintBacktrace(std::vector const &stack, NameFnT getName, LineNoFnT getLineNo) { +template +void trace_PrintBacktrace( + std::vector const &stack, + InvocableR auto getName, + InvocableR auto getLineNo +) { size_t n = stack.size(); if (n == 0) { return; // LCOV_EXCL_LINE diff --git a/include/diagnostics.hpp b/include/diagnostics.hpp index b1d67af8..d3c099d6 100644 --- a/include/diagnostics.hpp +++ b/include/diagnostics.hpp @@ -30,7 +30,7 @@ struct WarningState { std::pair> getInitialWarningState(std::string &flag); -template +template struct WarningFlag { char const *name; LevelEnumT level; @@ -38,14 +38,14 @@ struct WarningFlag { enum WarningBehavior { DISABLED, ENABLED, ERROR }; -template +template struct ParamWarning { WarningEnumT firstID; WarningEnumT lastID; uint8_t defaultLevel; }; -template +template struct DiagnosticsState { WarningState flagStates[WarningEnumT::NB_WARNINGS]; WarningState metaStates[WarningEnumT::NB_WARNINGS]; @@ -53,7 +53,7 @@ struct DiagnosticsState { bool warningsAreErrors = false; }; -template +template struct Diagnostics { std::vector> metaWarnings; std::vector> warningFlags; @@ -71,7 +71,7 @@ struct Diagnostics { void processWarningFlag(char const *flag); }; -template +template WarningBehavior Diagnostics::getWarningBehavior(WarningEnumT id) const { // Check if warnings are globally disabled if (!state.warningsEnabled) { @@ -120,7 +120,7 @@ WarningBehavior Diagnostics::getWarningBehavior(Warnin return WarningBehavior::DISABLED; } -template +template void Diagnostics::processWarningFlag(char const *flag) { std::string rootFlag = flag; diff --git a/include/helpers.hpp b/include/helpers.hpp index e8ce37bd..c6180a00 100644 --- a/include/helpers.hpp +++ b/include/helpers.hpp @@ -3,6 +3,8 @@ #ifndef RGBDS_HELPERS_HPP #define RGBDS_HELPERS_HPP +#include + // Ideally we'd use `std::unreachable`, but it has insufficient compiler support #ifdef __GNUC__ // GCC or compatible #ifdef NDEBUG @@ -101,12 +103,29 @@ static inline int clz(unsigned int x) { // MSVC does not inline `strlen()` or `.length()` of a constant string template + requires(SizeOfString > 0) static constexpr int literal_strlen(char const (&)[SizeOfString]) { return SizeOfString - 1; // Don't count the ending '\0' } +// Concept for template parameters that must be an `enum` type +template +concept Enum = std::is_enum_v; + +// Concept for template parameters that must be a function returning `void` +template +concept Procedure = std::is_void_v>; + +// Concept for template parameters that must be a function returning a particular type +template +concept InvocableR = std::is_invocable_r_v; + +// Concept for template parameters that must match a particular type modulo cv-qualifiers +template +concept QualifiedEquivalent = std::is_same_v, UnqualifiedT>; + // For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))` -template +template struct Defer { DeferredFnT deferred; Defer(DeferredFnT func) : deferred(func) {} diff --git a/include/itertools.hpp b/include/itertools.hpp index 1579eba9..9de58c65 100644 --- a/include/itertools.hpp +++ b/include/itertools.hpp @@ -12,6 +12,8 @@ #include #include +#include "helpers.hpp" // Enum + // A wrapper around iterables to reverse their iteration order; used in `for`-each loops. template struct ReversedIterable { @@ -78,7 +80,7 @@ public: }; // An iterable of `enum` values in the half-open range [start, stop). -template +template class EnumSeq { EnumT _start; EnumT _stop; @@ -169,7 +171,7 @@ template using ZipHolder = std::conditional_t< std::is_lvalue_reference_v, IterableT, - std::remove_cv_t>>; + std::remove_cvref_t>; // Iterates over N containers at once, yielding tuples of N items at a time. // Does the same number of iterations as the first container's iterator! diff --git a/include/link/section.hpp b/include/link/section.hpp index 0fb3ba36..003cc19d 100644 --- a/include/link/section.hpp +++ b/include/link/section.hpp @@ -8,6 +8,7 @@ #include #include +#include "helpers.hpp" // QualifiedEquivalent #include "linkdefs.hpp" struct FileStackNode; @@ -52,7 +53,7 @@ struct Section { private: // Template class for both const and non-const iterators over the "pieces" of this section - template + template SectionT> class PiecesIterable { SectionT *_firstPiece; diff --git a/include/util.hpp b/include/util.hpp index 19b37f9b..4b06531f 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -39,8 +39,11 @@ bool startsIdentifier(int c); bool continuesIdentifier(int c); template +inline constexpr bool BaseV = Base > 0 && Base <= 36; + +template + requires BaseV bool isDigit(int c) { - static_assert(Base <= 36, "Base must be 36 or less to allow digits 0-9A-Z"); if constexpr (Base <= 10) { return c >= '0' && c < static_cast('0' + Base); } else { @@ -50,8 +53,8 @@ bool isDigit(int c) { } template + requires BaseV uint8_t parseDigit(int c) { - static_assert(Base <= 36, "Base must be 36 or less to allow digits 0-9A-Z"); assume(isDigit(c)); if constexpr (Base <= 10) { return c - '0'; diff --git a/src/asm/charmap.cpp b/src/asm/charmap.cpp index 4c6c2064..0154e132 100644 --- a/src/asm/charmap.cpp +++ b/src/asm/charmap.cpp @@ -3,6 +3,7 @@ #include "asm/charmap.hpp" #include +#include // predicate #include #include #include @@ -65,8 +66,9 @@ struct Charmap { }; // Traverse the trie depth-first to derive the character mappings in definition order -template -bool forEachChar(Charmap const &charmap, CallbackFnT callback) { +bool forEachChar( + Charmap const &charmap, std::predicate auto callback +) { // clang-format off: nested initializers for (std::stack> prefixes({{0, ""}}); !prefixes.empty();) { // clang-format on diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp index dd0277e6..2296ac05 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -4,6 +4,7 @@ #include #include +#include // predicate #include #include #include @@ -527,6 +528,7 @@ static void shiftChar(); static int bumpChar(); static int nextChar(); template + requires BaseV static uint32_t readNumber(int initial, char const *prefix); static uint32_t readBracketedMacroArgNum() { @@ -818,8 +820,7 @@ static int nextChar() { return peek(); } -template -static int skipChars(PredicateFnT predicate) { +static int skipChars(std::predicate auto predicate) { int c = peek(); while (predicate(c)) { c = nextChar(); @@ -1099,6 +1100,7 @@ void lexer_SetGfxDigits(char const digits[4]) { } template + requires BaseV static uint32_t readNumber(int initial, char const *prefix) { auto isSomeDigit = [](int c) { if constexpr (Base == 2) { @@ -2283,8 +2285,7 @@ yy::parser::symbol_type yylex() { } } -template -static Capture makeCapture(char const *name, CallbackFnT callback) { +static Capture makeCapture(char const *name, InvocableR auto callback) { // Due to parser internals, it reads the EOL after the expression before calling this. // Thus, we don't need to keep one in the buffer afterwards. // The following assumption checks that. diff --git a/src/asm/parser.y b/src/asm/parser.y index 1e2c7780..3dc2f9e2 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -32,6 +32,7 @@ %code { #include + #include // invocable #include #include #include @@ -57,9 +58,10 @@ yy::parser::symbol_type yylex(); // Provided by lexer.cpp - template static auto handleSymbolByType( - std::string const &symName, NumCallbackFnT numCallback, StrCallbackFnT strCallback + std::string const &symName, + std::invocable auto numCallback, + std::invocable auto strCallback ) { if (Symbol *sym = sym_FindScopedSymbol(symName); sym && sym->type == SYM_EQUS) { return strCallback(*sym->getEqus()); diff --git a/src/gfx/pal_spec.cpp b/src/gfx/pal_spec.cpp index 8ac6256f..c3714a39 100644 --- a/src/gfx/pal_spec.cpp +++ b/src/gfx/pal_spec.cpp @@ -5,6 +5,7 @@ #include #include #include +#include // unsigned_integral #include #include #include @@ -202,7 +203,7 @@ static void warnExtraColors( } // Parses the initial part of a string_view, advancing the "read index" as it does -template // Should be uint*_t +template static std::optional parseDec(std::string const &str, size_t &n) { UintT value = 0; auto result = std::from_chars(str.data() + n, str.data() + str.length(), value); diff --git a/src/link/lexer.cpp b/src/link/lexer.cpp index 54193866..c2602dfa 100644 --- a/src/link/lexer.cpp +++ b/src/link/lexer.cpp @@ -95,6 +95,7 @@ static std::string readKeyword(int initial) { } template + requires BaseV static yy::parser::symbol_type readNumber(int initial, char const *prefix, char const *name) { LexerStackEntry &context = lexerStack.back(); uint32_t number; diff --git a/src/link/output.cpp b/src/link/output.cpp index e08eb629..8d9db7ad 100644 --- a/src/link/output.cpp +++ b/src/link/output.cpp @@ -310,8 +310,9 @@ static bool compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2) { < std::tie(sym2.addr, sym2_local, sym2.parentAddr, sym2_name); } -template -static void forEachSortedSection(SortedSections const &bankSections, CallbackFnT callback) { +static void forEachSortedSection( + SortedSections const &bankSections, Procedure
auto callback +) { for (Section const *sect : bankSections.zeroLenSections) { for (Section const &piece : sect->pieces()) { callback(piece); @@ -411,8 +412,7 @@ static void writeSectionName(std::string const &name, FILE *file) { } } -template -uint16_t forEachSection(SortedSections const §List, CallbackFnT callback) { +uint16_t forEachSection(SortedSections const §List, Procedure
auto callback) { uint16_t used = 0; auto section = sectList.sections.begin(); auto zeroLenSection = sectList.zeroLenSections.begin();