Mod API: Aggregate-initialize import/export records

Starting with VS 18 / cl 14.51, MSVC's constant evaluator
rejects a pointer-to-member in a constant initializer that also
contains any computed data, a consteval call or even a copy of a
constexpr object (C2127).

Hook records are now aggregate-initialized from literals only: the
computed name bytes (vtable symbol + display name) are baked into
char... template arguments and sizes/kinds are spelled inline.
This commit is contained in:
Luke Street
2026-07-13 01:07:30 -06:00
parent 9687a9d95a
commit cca104584e
6 changed files with 118 additions and 90 deletions
+8 -8
View File
@@ -288,7 +288,7 @@ static bool parse_meta(NativeMod& native, LoadedMod& mod) {
return invalid("truncated import record");
}
auto* record = reinterpret_cast<ModMetaImport*>(const_cast<uint8_t*>(cursor));
if (!terminated_within(record->service_id, sizeof(record->service_id))) {
if (!terminated_within(record->service_id.chars, sizeof(record->service_id.chars))) {
return invalid("unterminated import service id");
}
parsed.imports.push_back(record);
@@ -299,7 +299,7 @@ static bool parse_meta(NativeMod& native, LoadedMod& mod) {
return invalid("truncated export record");
}
auto* record = reinterpret_cast<ModMetaExport*>(const_cast<uint8_t*>(cursor));
if (!terminated_within(record->service_id, sizeof(record->service_id))) {
if (!terminated_within(record->service_id.chars, sizeof(record->service_id.chars))) {
return invalid("unterminated export service id");
}
parsed.exports.push_back(record);
@@ -552,18 +552,18 @@ static ModManifestInfo build_manifest_info(const ModMetaParsed& parsed) {
ModManifestInfo info;
info.imports.reserve(parsed.imports.size());
for (const auto* record : parsed.imports) {
if (!svc::valid_service_id(record->service_id)) {
if (!svc::valid_service_id(record->service_id.chars)) {
continue;
}
info.imports.push_back({record->service_id, record->major_version,
info.imports.push_back({record->service_id.chars, record->major_version,
(record->rec.flags & SERVICE_IMPORT_OPTIONAL) == 0});
}
info.exports.reserve(parsed.exports.size());
for (const auto* record : parsed.exports) {
if (!svc::valid_service_id(record->service_id)) {
if (!svc::valid_service_id(record->service_id.chars)) {
continue;
}
info.exports.push_back({record->service_id, record->major_version});
info.exports.push_back({record->service_id.chars, record->major_version});
}
return info;
}
@@ -608,11 +608,11 @@ static void warn_unpublished_deferred_exports(const LoadedMod& mod) {
continue;
}
const auto* record =
svc::find_service_record(serviceExport->service_id, serviceExport->major_version);
svc::find_service_record(serviceExport->service_id.chars, serviceExport->major_version);
if (record != nullptr && record->service == nullptr) {
log::write(mod.metadata.id, LOG_LEVEL_WARN,
"declared deferred service '{}@{}' but never published it during initialization",
serviceExport->service_id, serviceExport->major_version);
serviceExport->service_id.chars, serviceExport->major_version);
}
}
}
+10 -8
View File
@@ -222,7 +222,7 @@ bool ModLoader::register_static_service_exports(LoadedMod& mod) {
}
for (const auto* serviceExport : mod.native->parsed.exports) {
if (!svc::valid_service_id(serviceExport->service_id)) {
if (!svc::valid_service_id(serviceExport->service_id.chars)) {
fail_mod(mod, MOD_INVALID_ARGUMENT, "Invalid service export descriptor");
return false;
}
@@ -234,7 +234,7 @@ bool ModLoader::register_static_service_exports(LoadedMod& mod) {
}
const auto result =
svc::register_service(serviceExport->service_id, serviceExport->major_version,
svc::register_service(serviceExport->service_id.chars, serviceExport->major_version,
serviceExport->minor_version, serviceExport->service, &mod, deferred);
if (result != MOD_OK) {
fail_mod(mod, result, "Service export registration failed");
@@ -262,8 +262,8 @@ std::string ModLoader::describe_missing_import(
continue;
}
for (const auto* serviceExport : other.native->parsed.exports) {
if (svc::valid_service_id(serviceExport->service_id) &&
std::string_view{serviceExport->service_id} == serviceId &&
if (svc::valid_service_id(serviceExport->service_id.chars) &&
std::string_view{serviceExport->service_id.chars} == serviceId &&
serviceExport->major_version == majorVersion)
{
return fmt::format("Required service {}@{} unavailable: provider '{}' {}",
@@ -282,12 +282,14 @@ bool ModLoader::resolve_service_imports(LoadedMod& mod) {
}
for (const auto* serviceImport : mod.native->parsed.imports) {
if (!svc::valid_service_id(serviceImport->service_id) || serviceImport->slot == nullptr) {
if (!svc::valid_service_id(serviceImport->service_id.chars) ||
serviceImport->slot == nullptr)
{
fail_mod(mod, MOD_INVALID_ARGUMENT, "Invalid service import descriptor");
return false;
}
const auto* service = svc::find_service(serviceImport->service_id,
const auto* service = svc::find_service(serviceImport->service_id.chars,
serviceImport->major_version, serviceImport->min_minor_version);
if (service == nullptr) {
*static_cast<const void**>(serviceImport->slot) = nullptr;
@@ -296,8 +298,8 @@ bool ModLoader::resolve_service_imports(LoadedMod& mod) {
}
fail_mod(mod, MOD_UNAVAILABLE,
describe_missing_import(serviceImport->service_id, serviceImport->major_version,
serviceImport->min_minor_version));
describe_missing_import(serviceImport->service_id.chars,
serviceImport->major_version, serviceImport->min_minor_version));
return false;
}