mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 17:51:07 +00:00
docs-generator-shared-dep-reader
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
aea29d3797 |
Per-module concurrent dispatch: C++ module async export (#93)
* feat: emit logos_module_dispatch_async for concurrency:multi C++ modules The cdylib C-ABI exports gain an async dispatch entry (each call run on a worker thread, reply on completion) for universal + cdylib C++ modules. --concurrency multi flag in logos-cpp-generator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: cpp-sdk concurrent-dispatch doctest (concurrency:"multi" showcase) A concurrency:multi C++ worker + a single driver firing concurrent calls, showing the multi worker overlaps them. The C++ cdylib generator needs NO change — its logos_module_dispatch is already safe to call concurrently; the worker pool lives in the Qt glue and the result is deferred via a sentinel + completion event. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: wire universal-cdylib modules() independent of the context latch A C++ interface:"universal" cdylib that calls another module via modules().<dep>... segfaulted on its FIRST cross-module call: the typed dependency surface (LogosModules) was wired inside lidlTryFireContext, which returns early when no persistence context was stored (g_ctxStored == false). When the daemon never delivers a context (observed: zero set_context calls for a context-less module), maybeSetLogosModules never ran, m_logosModulesPtr stayed null, and LogosModuleContext::modules() dereferenced null. modules() does not need the context — each dependency client bakes its target+origin at codegen time and creates its lp client lazily on first call. So wire it in its own context-independent once-latch (lidlEnsureModulesWired), called at the top of lidlTryFireContext before the context-gated early return, i.e. on the first dispatch / set_context / set_emit_callback. A module with deps but no stored context now has modules() wired before any handler runs. (Bump the concurrent-dispatch doctest's post-daemon-start sleep 3 -> 6 to match the rust spec's cold-start margin.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: green the universal-cdylib concurrent-dispatch doctest + wire into CI The driver/worker split a declarations-only impl header (so the cpp-generator's --header-to-lidl doesn't choke on inline std calls) from the impl body. That body was never compiled — metadata's nix.cmake.extra_sources is parsed but not consumed by the LogosModule.cmake the build actually uses — so the impl symbols (FanoutDriverModuleImpl::fanOut / ::peak) were UNDEFINED in the dylib and the plugin null-jumped (bl -> 0x0) on the first cross-module call. Pass the impl .cpp via logos_module()'s existing SOURCES argument so it's compiled and linked. With this the cpp universal-cdylib reaches worker peak overlap 4 end-to-end (a single-threaded driver fans out 4 async calls into a concurrency:"multi" worker and all four overlap), matching the Rust half. Wire the spec into doctests.yml so the workspace pipeline runs it. (Auto-wiring metadata.extra_sources — so the split pattern works without listing SOURCES by hand — needs the consumer added to the backend LogosModule.cmake copies in logos-plugin-core / logos-plugin-qt; tracked separately.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: bump logos-protocol to merged master (protocol#5) logos-protocol 9de4165 → 4ea32a3 (concurrent-dispatch handshake coalescing, now on master) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
4fc85b71bf |
docs: generator doc-test — the three LIDL ⇄ C++ flows (#94)
Add an executable doc-test that exercises logos-cpp-generator across the
three flows it supports, on the commit under test:
1. provider header → LIDL (--header-to-lidl): distil the contract from
a plain C++ impl class.
2. LIDL → provider (--lidl --backend cdylib): emit the C-ABI
provider glue that exports the impl class to the host.
3. LIDL → consumer header (--lidl --module-only): emit the typed
caller header consumers compile against.
The example is a rich `sensor_module` interface — methods of arity 0–4,
the full type set (int/uint/float64/bool/tstr/bstr, typed arrays,
result), and multi-line docs on methods and events. A final section adds
the composite types (records, maps, optionals).
Registered in doctests/run.sh and the doctests workflow.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
d1d341fdcf |
ci: drop doctest chain pins — the qt-split chain is fully merged (#87)
logoscore-cli and module-builder masters now contain the chain; the temporary --release-for pins (added so stacked-branch CI could resolve compatible cross-repo revs) default back to latest releases. |
||
|
|
f0fe8cbfeb |
Make the base SDK Qt-free: Qt developer layer moves to logos-qt-sdk (#83)
* Extract the protocol layer into logos-protocol; consume it as a flake input The transport/token/IPC layer (transports incl. QRO + plain TCP/TLS, consumer core LogosAPIClient/LogosAPIConsumer with the capability auto-requestModule flow, ModuleProxy, token manager, QVariant<->JSON conversion, the abstract LogosProviderObject interface) now lives in the logos-protocol repo behind the versioned lp_* C ABI. This SDK keeps the typed C++ developer layer (LogosAPI, provider base classes + Qt provider glue, module context, code generator) and still compiles the protocol sources INTO liblogos_sdk.a from the flake input, so the installed artifact (archive symbols, include/ + include/cpp layouts, cmake config) stays byte-compatible: existing consumers need no changes. Public headers are unchanged; logos_provider_object.h keeps its name and now re-exports the abstract interface from logos_provider_interface.h. Transport/protocol component tests moved to logos-protocol with the code; the remaining sdk/generator/experimental suites are unchanged (432/432 green against the local protocol checkout). * lock: add logos-protocol input * Make the base SDK Qt-free: move the Qt developer layer to logos-qt-sdk LogosAPI, LogosAPIProvider, LogosProviderBase/LOGOS_PROVIDER macros, the QObject provider glue (QtProviderObject) and the legacy PluginInterface (core/interface.h) move to the new logos-qt-sdk repo. The protocol sources are no longer compiled into a monolithic archive — consumers link logos-qt-sdk (which layers on logos-protocol) instead. What remains here is header-only std C++: logos_module_context.h, logos_result.h (StdLogosResult), logos_json.h — exported as the CMake INTERFACE target logos-cpp-sdk::logos_headers — plus the code generator (a build-time tool; its introspection mode now includes logos_provider_interface.h from logos-protocol, where LogosProviderPlugin moved). Mechanically verified Qt-free: the logos-cpp-lib / logos-cpp-include closures contain only nlohmann_json. Tests: 245/245 (module-context std suite + generator + experimental). * fix: accept the installed source-export layout in the protocol-root check The fail-fast only tested <root>/cpp/logos_protocol.h, but the LP_SRC selection right below (and the error message itself) support the installed export layout <root>/include/cpp as well. Pointing LOGOS_PROTOCOL_ROOT at an installed export tripped the FATAL_ERROR before that fallback could apply. Caught by Copilot review on #82. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * lock: pin logos-protocol to the qt-free-split branch head The Qt-free SDK (and the cdylib backend stacked on it) reference LogosProviderPlugin from protocol's logos_provider_interface.h, which lands on feat/qt-free-split — the P1-branch pin no longer compiles standalone. Temporary — drop when the chain PRs merge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * doctest: pin the logoscore runtime via its {release} placeholder The spec built logoscore-cli at bare master with only the cpp-sdk inputs overridden — master's stack cannot compile against the qt-free SDK, so the suite failed on the chain branches. With the placeholder, CI's --release-for pins expand it to the workspace's logoscore commit (and local runs without a pin still fall back to master, unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * doctest: override the nested module builders to {release} too capability_module (via logoscore's lock) and the cloned accounts module resolve module-builder from their own locks — pre-split revs whose LogosModule.cmake still detects the SDK by logos_api.h, which the qt-free SDK no longer ships ('logos-cpp-sdk not found'). Overriding the builder itself to the workspace-pinned chain rev (keeping the nested cpp-sdk override) builds both modules with the split-aware builder. Verified end-to-end locally with the exact doctest command. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * doctest: apply the {release} + nested-builder overrides to all three specs The runtime spec got the treatment in 210eea1; the composition and worker-thread specs have the same logoscore/module build commands and failed identically (pre-split builders from the modules' own locks). All executed run: blocks now pin logoscore-cli{release} and override the nested module builders to logos-module-builder{release}; the displayed code_block: variants stay in their generic master form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * codegen: typed wrappers throw on call failure; dispatch catches escapes Generated sync client wrappers call the new err-out invokeRemoteMethod overload and throw logos::LogosCallError when the call fails (e.g. the bound module is missing) — previously the empty QVariant silently degraded to the return type's default and a caller could not tell failure from a legitimate 0 / "". Both generators (legacy + LIDL), both API styles. Async paths unchanged. Generated provider dispatch (universal qt glue + LOGOS_PROVIDER) wraps the method body in a catch-all that logs and returns an invalid QVariant — an escaped exception becomes an ordinary METHOD_FAILED instead of unwinding through Qt event dispatch and killing the module process. * codegen: CallError out-param instead of throwing wrappers Per review, the generated sync wrappers expose the error channel as an optional trailing parameter — add(a, b, &err) — rather than throwing: explicit, stateless, works on temporaries, and existing call sites compile unchanged (they keep default-on-failure, now with a qWarning so failures are visible in the module log). The dispatch catch-all from the previous commit stays: it contains author exceptions, it doesn't introduce any. * glue: fire onContextReady AFTER modules()/event wiring The generated onInit set the context (which fires the impl's onContextReady hook) before constructing the LogosModules aggregate and wiring typed event emission — so an impl doing its documented one-time setup there (typed dependency calls, event subscriptions) dereferenced a null aggregate and crashed the module process (signal 11). Found by the first module to subscribe to a dependency's typed event from onContextReady. Context now goes last. * ci: run workflows on stacked PRs + workflow_dispatch Both workflows filtered pull_request to master-based PRs, so stacked PRs (feat/qt-free-sdk -> feat/extract-logos-protocol, feat/cdylib-authoring -> feat/qt-free-sdk) ran NO checks at all. Drop the base-branch filter for pull_request and add workflow_dispatch for manual runs. Same fix as logos-module-builder 232b8a2. * lock: protocol at the typed-requestModule port (3de5398) * ci: chain pins for the doc-tests (drop at merge) In repo CI only cpp-sdk's {release} is the commit under test — logoscore-cli and module-builder expanded to master, which doesn't link against the chain SDK the specs override in ('Build the CLI with the SDK override' failed on every run since the stacked-PR triggers were enabled). Pin both to the extraction-chain heads; the workspace pipeline is unaffected (it pins every repo itself). * generator: distribute the LIDL frontend for external generators First step of moving ALL Qt glue emission out of this repo into logos-qt-sdk's logos-qt-generator (cpp-sdk's generator keeps only the Qt-free outputs: std typed wrappers, logos_sdk umbrella, cdylib impl-exports, LIDL derivation). - Shared emit helpers (lidlToPascalCase, lidlTypeToQt, lidlTypeToStd, lidlIsStdConvertible) move to a new lidl_emit_common.{h,cpp} unit, used by both generators. - The frontend set (AST, lexer, parser, serializer, validator, impl-header parser, emit-common) is installed under share/lidl-frontend/ — the qt generator compiles these sources in directly, so the two tools share one frontend without a binary ABI. * lock: protocol#3 merged — pin advances to protocol master --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
40e7631402 |
Marshal inter-module calls to the owner thread (#79)
* Marshal inter-module calls to the owner thread Logos inter-module calls go over Qt Remote Objects, whose replicas only work on the thread that created them (the module's main/event-loop thread). A module that makes calls from a worker thread — e.g. an embedded HTTP server serving /metrics — would otherwise hang on replica acquisition. Make LogosAPIClient transparently marshal to its owner thread when called off-thread (guarded so same-thread calls run directly with no overhead): - LogosAPI::getClient creates the client/consumer/replicas on the owner thread - LogosAPIClient::invokeRemoteMethod / requestObject / onEvent run there too New header logos_thread_marshal.h (runOnOwnerThread). No new data members — ABI-safe for statically-linked plugins. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add regression test for worker-thread inter-module calls A provider records the thread its method runs on; a consumer calls it from a worker thread via LogosAPIClient::invokeRemoteMethod. The call must execute on the owner (main/event-loop) thread, not the worker thread. Fails without the marshaling change (the call runs on the worker thread — 0x..d80d0 vs owner 0x..c53e0, "executed on the worker thread instead of the owner thread"); passes with it (511/511). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Address review: async marshaling, helper constraints, test ownership - invokeRemoteMethodAsync now also marshals to the owner thread (non-blocking QueuedConnection) — the async path acquires a replica too, so calling it from a worker thread previously re-introduced the off-thread bug. - runOnOwnerThread: document the return-type constraints (void or default-constructible, non-reference) and static_assert against references. - test: declare the provider before its LogosAPI so the ModuleProxy (which holds a raw pointer to it) is torn down first — removes the leak and the inaccurate comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * doctest: call a module from a worker thread (HTTP server) Adds cpp-sdk-worker-thread-http.test.yaml: builds a sensor_module callee and an http_module caller that embeds a libmicrohttpd server, runs them in logoscore, starts the server, and curls it. The HTTP handler calls sensor_module.readTemperature() from the server's worker thread — which only works because the SDK marshals the cross-module call onto the module's owner thread. The module stays pure C++. Wired into doctests/run.sh and the doctests CI workflow. Validated locally (23/23 steps pass): `curl` returns `temperature 42`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b7eb07efed |
add doctests (#75)
* add doctests * add doctest for cross module call |
||
|
|
d633575677 |
add IDL parser & generator (wip) (#33)
* add IDL parser & generator (wip) * fix fixtures issues affecting tests * fix fixtures issues affecting tests |
||
|
|
39b0a9acce |
Add tests; CI (#27)
* add tests for cpp-generator and sdk * add tests for new api, module_proxy and factories * add CI tests * add tests for a module definition |