feat(sdk): make the by-name call path a supported API

The dynamic (by-name) invoke path already existed and was already ungated at
every layer — lp_client_create / lp_invoke in the C ABI, logos::LpClient above
it, and the Qt client above that. Nothing checked a host service; there was no
gate to open. What was missing was the ERGONOMICS, which is what turned a
supported capability into something callers reached around the umbrella to get.

Three additive pieces, no gate touched:

1. LogosModuleContext::moduleName() — the module's own registry name, i.e. the
   origin it authenticates as. The typed wrappers bake their origin in at
   codegen time; a by-name call has to state one, and a wrong origin
   authenticates as nobody and fails far from the call site. Set through a NEW
   `_logosCoreSetModuleName_`, deliberately not a fourth parameter on
   `_logosCoreSetContext_`: every generated provider calls that signature, so
   widening it would break each one until regenerated, for a value the
   generator knows statically. Set before the context, so moduleName() is live
   inside onContextReady().

2. LogosModules::dynamic(target) on the origin-bound umbrella — the untyped
   client, with the origin baked in exactly as the typed members' is, and
   cached per target because LpClient owns a connection. The typed members over
   metadata.json#dependencies stay the ordinary way to call another module;
   this is for the cases whose target is a runtime value (a proxy, a router).

3. LpClient::getMethods() over the already-exported lp_get_methods. Invoke
   without introspect is guessing — a caller that cannot ask what exists can
   only hardcode, and a wrong guess fails at runtime like a typo.

Verified: #default builds, and both checks pass (tests, generator-cli).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Dario Gabriel Lipicar
2026-08-18 16:51:48 -03:00
co-authored by Claude Opus 5
parent 1afcf46f4d
commit 1be71bbbeb
4 changed files with 74 additions and 0 deletions
+32
View File
@@ -85,6 +85,14 @@ public:
// resources bundled next to the plugin (icons, qml/, schema files…).
const std::string& modulePath() const { return m_modulePath; }
// This module's own registry name — the name other modules address it by,
// and the `origin` it authenticates as. Needed by any BY-NAME call: the
// typed wrappers bake their origin in at codegen time, but a dynamic call
// has to state it, and a wrong origin authenticates as nobody and fails far
// from the call site. Empty outside a framework-provisioned context, like
// the getters below.
const std::string& moduleName() const { return m_moduleName; }
// Short ID the host assigns to this instance. Stable across restarts
// for the same on-disk persistence directory; multiple side-by-side
// instances of the same module get distinct IDs.
@@ -152,6 +160,16 @@ public:
onContextReady();
}
// Framework-only — sets moduleName(). Separate from
// `_logosCoreSetContext_` on purpose: that signature is called by every
// generated provider, so widening it would break each one until
// regenerated, for a value the generator knows statically anyway. Called
// BEFORE the context setter, so moduleName() is already populated when
// onContextReady() fires.
void _logosCoreSetModuleName_(std::string moduleName) {
m_moduleName = std::move(moduleName);
}
// Framework-only — sets the typed `LogosModules` pointer that
// `logos<T>()` dereferences. Untyped (void*) at this layer because
// the SDK header is shared by every module; the codegen-generated
@@ -194,6 +212,7 @@ protected:
virtual void onContextReady() {}
private:
std::string m_moduleName;
std::string m_modulePath;
std::string m_instanceId;
std::string m_instancePersistencePath;
@@ -230,6 +249,19 @@ private:
// ---------------------------------------------------------------------------
namespace _logos_codegen_ {
template<class T>
inline auto maybeSetModuleName(T& impl, std::string moduleName)
-> std::enable_if_t<std::is_base_of_v<LogosModuleContext, T>>
{
static_cast<LogosModuleContext&>(impl)._logosCoreSetModuleName_(std::move(moduleName));
}
template<class T>
inline auto maybeSetModuleName(T&, std::string)
-> std::enable_if_t<!std::is_base_of_v<LogosModuleContext, T>>
{
}
template<class T>
inline auto maybeSetContext(T& impl,
std::string modulePath,