mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 01:31:10 +00:00
feat(generator): async callers can see the error, sync callers can set a deadline (#132)
The two consumer surfaces had complementary holes:
sync : T foo(params…, logos::CallError* err = nullptr) error yes, timeout NO
async: void fooAsync(params…, cb, Timeout = Timeout()) timeout yes, error NO
so an async caller could not tell a failed remote call from a provider that
legitimately returned 0 / "" / false — the exact ambiguity the sync path's
CallError* was added to resolve — and a sync caller could not say how long it
was willing to wait, even though the transport overload the generator already
calls takes both.
Both fixes are additive:
T foo(params…, logos::CallError* err = nullptr, Timeout timeout = Timeout());
void fooAsync(params…, std::function<void(T)> cb, Timeout timeout = Timeout()); // unchanged
void fooAsyncResult(params…, std::function<void(logos::AsyncResult<T>)> cb,
Timeout timeout = Timeout()); // new
logos::AsyncResult<T> (new, Qt-free, cpp/logos_async_result.h) is {value, error}
plus ok(); AsyncResult<void> carries only the error so every fooAsyncResult has
the same callback shape. The name is distinct rather than an overload because
std::function<void(AsyncResult<T>)> next to std::function<void(T)> is ambiguous
for a generic lambda.
Applied to both emitters that produce this surface — legacy/generator_lib.cpp
(the module-builder path) and experimental/lidl_gen_client.cpp (`--lidl
--module-only`, from a published contract) — since a consumer can reach either
for the same contract.
The Qt-free (ApiStyle::Lp) surface gets the sync timeout (spelled `int
timeout_ms`; `Timeout` lives behind a Qt header) but NOT fooAsyncResult:
logos-protocol's lp_invoke_async hard-codes `cb(1, …)`, so an AsyncResult there
would report ok() on a failed call. Measured, not assumed. See the note in
makeHeaderLp.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d972fe207a
commit
f3369faca4
@@ -329,16 +329,22 @@ Fixture files in `tests/experimental/fixtures/`:
|
||||
single-`_bytes`-field record is refused under both spellings. It used to read `f.type`,
|
||||
which refused `? _bytes: tstr` and let `_bytes: ?tstr` through — the same declaration,
|
||||
two answers. `?bstr` is unaffected either way: the tag lives in the value, not the slot.
|
||||
- **A provider REJECTION reaches an async consumer callback only as a log line.** A
|
||||
provider that refuses a call answers the canonical
|
||||
`{"code":"dispatch_failed", "message":…, "origin":…}` object as its RESULT, not as a
|
||||
transport error, and the Qt return table would convert it like any other value —
|
||||
- **A provider REJECTION reaches `…Async`'s callback only as a log line** (but
|
||||
`…AsyncResult`'s callback gets it properly). A provider that refuses a call answers the
|
||||
canonical `{"code":"dispatch_failed", "message":…, "origin":…}` object as its RESULT, not
|
||||
as a transport error, and the Qt return table would convert it like any other value —
|
||||
erasing it (`_result.toList()` on that map is `[]`). The Qt consumer emitter therefore
|
||||
detects it and folds it into the `logos::CallError` out-parameter the sync wrapper
|
||||
already carries, so `mod.echoUintList(v, &err)` can tell a rejection from an empty
|
||||
return. The generated `…Async` overload has no such channel — its callback is
|
||||
`std::function<void(T)>`, and adding an error parameter would change the generated
|
||||
public surface (which logos-qt-sdk's `qt-generator --backend consumer` veneer mirrors
|
||||
1:1) — so an async rejection is reported with `qWarning` and the callback still
|
||||
receives the default-converted value. Giving async an error channel is an API change,
|
||||
not a code-generation fix.
|
||||
detects it (`logosDispatchRejection`, emitted once per wrapper) and folds it into the
|
||||
error channel of every surface that HAS one:
|
||||
- **sync** — the `logos::CallError*` out-parameter, so `mod.echoUintList(v, &err)` can
|
||||
tell a rejection from an empty return;
|
||||
- **`…AsyncResult`** — `logos::AsyncResult<T>::error`, so `r.ok()` is false and
|
||||
`r.error.code == "dispatch_failed"` exactly as on the sync path.
|
||||
|
||||
The historical **`…Async`** overload is the one exception: its callback is
|
||||
`std::function<void(T)>`, and adding an error parameter would change a generated public
|
||||
surface (which logos-qt-sdk's `qt-generator --backend consumer` veneer mirrors 1:1). It
|
||||
is left untouched, so there an async rejection is reported with `qWarning` and the
|
||||
callback still receives the default-converted value. `…AsyncResult` exists precisely
|
||||
because giving async an error channel was an API addition rather than a code-generation
|
||||
fix — a caller that needs to SEE the rejection uses it.
|
||||
|
||||
Reference in New Issue
Block a user