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