Route all module interaction through dispatch

A module is addressed by name and command string, and nothing else.
Typed API access handed a caller a pointer into the callee's function
table, which meant compiling against that module's struct layout — a
build-time dependency between modules that the design does not permit.
Removing it also removes the manifest, the table magic constant, and
the table size field, which existed only to describe and validate
those tables.

Load validation is now three steps: reserved contract symbols resolve,
the minimum-version handshake passes, and the version and description
probes return well-formed values. The contract is four reserved
symbols, and a module's interface is the command vocabulary it
documents.

Documentation is brought in line, and artifacts are named exactly:
libdpm-core.so for the library, <dpm/core.h> for the header, the dpm
binary for the command-line tool.
This commit is contained in:
2026-08-15 02:15:18 -04:00
parent 267529bee3
commit 97b39cac6c
17 changed files with 263 additions and 702 deletions

View File

@@ -3,9 +3,8 @@
* @brief Test binary: drives libdpm-core through its public C API
*
* Exercises context lifecycle, configuration, the full load-time
* validation matrix against the fixture modules, versioned require,
* typed API access, generic dispatch, and enumeration. Exits nonzero
* on any failure.
* validation matrix against the fixture modules, require, dispatch,
* and enumeration. Exits nonzero on any failure.
*
* @copyright Copyright (c) 2026 SILO GROUP LLC
* @author Chris Punches <chris.punches@silogroup.org>
@@ -57,12 +56,6 @@ static bool error_contains(dpm_ctx* ctx, const char* needle)
return err != nullptr && std::strstr(err, needle) != nullptr;
}
/* Mirror of the good fixture's table layout (consumer-side spec decl). */
struct good_api_v1_s {
dpm_api_table_header hdr;
int (*ping)(void);
};
int main(void)
{
/* ---- dpm_open: invalid explicit override refuses ---- */
@@ -115,12 +108,6 @@ int main(void)
CHECK(dpm_require(ctx, "bad_version") == nullptr);
CHECK(error_contains(ctx, "malformed version"));
CHECK(dpm_require(ctx, "lying_manifest") == nullptr);
CHECK(error_contains(ctx, "does not resolve"));
CHECK(dpm_require(ctx, "bad_magic") == nullptr);
CHECK(error_contains(ctx, "magic"));
CHECK(dpm_require(ctx, "nonexistent") == nullptr);
CHECK(error_contains(ctx, "not found"));
}
@@ -144,21 +131,11 @@ int main(void)
CHECK(dpm_module_info_of(ctx, nullptr, &seen) != 0);
CHECK(dpm_module_info_of(ctx, good, nullptr) != 0);
/* typed access */
const void* table = dpm_get_api(ctx, good, "good", 1);
CHECK(table != nullptr);
if (table) {
const auto* api = static_cast<const good_api_v1_s*>(table);
CHECK(api->hdr.magic == DPM_API_TABLE_MAGIC);
CHECK(api->hdr.size == sizeof(good_api_v1_s));
CHECK(api->ping() == 42);
}
/* absent api/version pairs are known from the manifest */
CHECK(dpm_get_api(ctx, good, "good", 2) == nullptr);
CHECK(dpm_get_api(ctx, good, "ghost", 1) == nullptr);
/* generic dispatch */
/* dispatch: the module's return value comes back verbatim, so a
command round trip is observable without any compile-time
knowledge of the module */
CHECK(dpm_execute(ctx, good, "ping", 0, nullptr) == 42);
CHECK(dpm_execute(ctx, good, "anything_else", 0, nullptr) == 0);
CHECK(dpm_execute(ctx, good, nullptr, 0, nullptr) == 0);
}