mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 01:31:10 +00:00
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.
This commit is contained in:
@@ -121,6 +121,7 @@ QString lidlMakeHeader(const ModuleDecl& module, BindMode bindMode)
|
||||
s << "#include \"logos_types.h\"\n";
|
||||
s << "#include \"logos_api.h\"\n";
|
||||
s << "#include \"logos_api_client.h\"\n";
|
||||
s << "#include \"logos_call_error.h\"\n";
|
||||
s << "#include \"logos_object.h\"\n\n";
|
||||
|
||||
s << "class " << className << " {\n";
|
||||
@@ -155,7 +156,10 @@ QString lidlMakeHeader(const ModuleDecl& module, BindMode bindMode)
|
||||
emitParam(s, lidlTypeToQt(md.params[i].type), md.params[i].name);
|
||||
if (i + 1 < md.params.size()) s << ", ";
|
||||
}
|
||||
s << ");\n";
|
||||
// Optional error out-channel: pass a logos::CallError* to distinguish
|
||||
// a failed remote call from a legitimately default-valued result.
|
||||
if (!md.params.isEmpty()) s << ", ";
|
||||
s << "logos::CallError* err = nullptr);\n";
|
||||
QString asyncCb = (ret == "void")
|
||||
? QString("std::function<void()>")
|
||||
: QString("std::function<void(") + ret + ")>";
|
||||
@@ -261,11 +265,13 @@ QString lidlMakeSource(const ModuleDecl& module, BindMode bindMode)
|
||||
emitParam(s, lidlTypeToQt(md.params[i].type), md.params[i].name);
|
||||
if (i + 1 < nParams) s << ", ";
|
||||
}
|
||||
s << ") {\n";
|
||||
if (nParams > 0) s << ", ";
|
||||
s << "logos::CallError* err) {\n";
|
||||
|
||||
// Call through the err-out overload: a failed call throws
|
||||
// logos::LogosCallError instead of silently degrading to the return
|
||||
// type's default value (see logos_call_error.h).
|
||||
// Call through the err-out overload: with a logos::CallError* the
|
||||
// caller can distinguish a failed remote call from a legitimately
|
||||
// default-valued result; without it the historical default-on-failure
|
||||
// behavior is kept, plus a warning in the module log.
|
||||
s << " logos::CallError _err;\n";
|
||||
if (ret != "void") s << " QVariant _result = ";
|
||||
else s << " ";
|
||||
@@ -276,7 +282,9 @@ QString lidlMakeSource(const ModuleDecl& module, BindMode bindMode)
|
||||
if (i + 1 < nParams) s << ", ";
|
||||
}
|
||||
s << "}, Timeout(), &_err);\n";
|
||||
s << " if (!_err.ok()) throw logos::LogosCallError(_err);\n";
|
||||
s << " if (err) *err = _err;\n";
|
||||
s << " else if (!_err.ok()) qWarning() << \"" << className << "::" << md.name
|
||||
<< ": remote call failed:\" << QString::fromStdString(_err.message);\n";
|
||||
|
||||
if (ret != "void")
|
||||
s << " " << returnConversion(ret) << "\n";
|
||||
|
||||
@@ -448,10 +448,9 @@ QString lidlMakeProviderDispatch(const ModuleDecl& module)
|
||||
s << "#include <exception>\n\n";
|
||||
|
||||
// --- callMethod ---
|
||||
// The dispatch body is wrapped in a catch-all: anything the author's code
|
||||
// (or a generated typed wrapper — see logos::LogosCallError) lets escape
|
||||
// becomes an ordinary method failure (invalid QVariant) instead of an
|
||||
// exception unwinding through Qt event dispatch and killing the module
|
||||
// The dispatch body is wrapped in a catch-all: any exception the author's
|
||||
// code lets escape becomes an ordinary method failure (invalid QVariant)
|
||||
// instead of unwinding through Qt event dispatch and killing the module
|
||||
// process.
|
||||
s << "QVariant " << providerObjectClass
|
||||
<< "::callMethod(const QString& methodName, const QVariantList& args)\n{\n";
|
||||
|
||||
@@ -213,6 +213,7 @@ QString makeHeader(const QString& moduleName, const QString& className, const QJ
|
||||
s << "#include \"logos_result.h\"\n";
|
||||
s << "#include \"logos_api.h\"\n";
|
||||
s << "#include \"logos_api_client.h\"\n";
|
||||
s << "#include \"logos_call_error.h\"\n";
|
||||
// Needed for the m_eventReplica member when the module declares
|
||||
// any events. Cheap to include unconditionally — keeps the
|
||||
// header symmetric with the Qt-style branch.
|
||||
@@ -230,6 +231,7 @@ QString makeHeader(const QString& moduleName, const QString& className, const QJ
|
||||
s << "#include \"logos_types.h\"\n";
|
||||
s << "#include \"logos_api.h\"\n";
|
||||
s << "#include \"logos_api_client.h\"\n";
|
||||
s << "#include \"logos_call_error.h\"\n";
|
||||
s << "#include \"logos_object.h\"\n\n";
|
||||
}
|
||||
s << "class " << className << " {\n";
|
||||
@@ -318,7 +320,11 @@ QString makeHeader(const QString& moduleName, const QString& className, const QJ
|
||||
else s << pt << " " << pn;
|
||||
if (i + 1 < params.size()) s << ", ";
|
||||
}
|
||||
s << ");\n";
|
||||
// Optional error out-channel: pass a logos::CallError* to distinguish
|
||||
// a failed remote call from a legitimately default-valued result.
|
||||
// Existing call sites compile unchanged.
|
||||
if (!params.isEmpty()) s << ", ";
|
||||
s << "logos::CallError* err = nullptr);\n";
|
||||
// Async overload: same params + callback + optional Timeout
|
||||
QString asyncCallbackType = (ret == "void")
|
||||
? QString("std::function<void()>")
|
||||
@@ -581,14 +587,15 @@ QString makeSource(const QString& moduleName, const QString& className, const QS
|
||||
emitParam(params.at(i).toObject(), byRef);
|
||||
if (i + 1 < params.size()) s << ", ";
|
||||
}
|
||||
s << ") {\n";
|
||||
if (!params.isEmpty()) s << ", ";
|
||||
s << "logos::CallError* err) {\n";
|
||||
|
||||
// Body: perform call through the err-out overload. A failed call
|
||||
// (e.g. the bound module is missing) throws logos::LogosCallError
|
||||
// instead of silently degrading to the return type's default value —
|
||||
// a caller cannot otherwise tell failure from a legitimate 0 / "".
|
||||
// Generated provider dispatch catches anything the author lets
|
||||
// escape and converts it into an ordinary method failure.
|
||||
// Body: perform call through the err-out overload. When the caller
|
||||
// passes a logos::CallError* it can distinguish a failed remote call
|
||||
// (e.g. the bound module is missing) from a legitimately
|
||||
// default-valued result; without it the historical default-on-failure
|
||||
// behavior is kept, now with a warning so failures are at least
|
||||
// visible in the module log.
|
||||
s << " logos::CallError _err;\n";
|
||||
if (ret != "void") s << " QVariant _result = ";
|
||||
else s << " ";
|
||||
@@ -599,7 +606,9 @@ QString makeSource(const QString& moduleName, const QString& className, const QS
|
||||
if (i + 1 < params.size()) s << ", ";
|
||||
}
|
||||
s << "}, Timeout(), &_err);\n";
|
||||
s << " if (!_err.ok()) throw logos::LogosCallError(_err);\n";
|
||||
s << " if (err) *err = _err;\n";
|
||||
s << " else if (!_err.ok()) qWarning() << \"" << className << "::" << name
|
||||
<< ": remote call failed:\" << QString::fromStdString(_err.message);\n";
|
||||
|
||||
// Return conversion
|
||||
if (ret == "void") {
|
||||
|
||||
@@ -612,10 +612,9 @@ static int generateProviderDispatch(const QString& headerPath, const QString& ou
|
||||
methodsByName[m.name].append(&m);
|
||||
}
|
||||
|
||||
// The dispatch body is wrapped in a catch-all: anything the author's code
|
||||
// (or a generated typed wrapper — see logos::LogosCallError) lets escape
|
||||
// becomes an ordinary method failure (invalid QVariant) instead of an
|
||||
// exception unwinding through Qt event dispatch and killing the module
|
||||
// The dispatch body is wrapped in a catch-all: any exception the author's
|
||||
// code lets escape becomes an ordinary method failure (invalid QVariant)
|
||||
// instead of unwinding through Qt event dispatch and killing the module
|
||||
// process.
|
||||
s << "QVariant " << className << "::callMethod(const QString& methodName, const QVariantList& args)\n";
|
||||
s << "{\n";
|
||||
|
||||
Generated
+3
-3
@@ -30,11 +30,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1781266151,
|
||||
"narHash": "sha256-RECe/lpZD7DsZEdwme13XXJEF7lxEqF2+Rjd4CUhW64=",
|
||||
"lastModified": 1781267326,
|
||||
"narHash": "sha256-n3CHoCFn16up4q3RieFrcS4GgneTO7wqTFjABo0AGKw=",
|
||||
"owner": "logos-co",
|
||||
"repo": "logos-protocol",
|
||||
"rev": "176fbc80304b78c385f64eb78af9ec5470c29955",
|
||||
"rev": "6492494a7c6ee1de531c25aeac86d0ea05f17d94",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -94,8 +94,8 @@ TEST(MakeHeaderTest, ContainsMethodDeclarations)
|
||||
QJsonArray methods = makeTestMethods();
|
||||
QString h = makeHeader("test_mod", "TestMod", methods);
|
||||
|
||||
EXPECT_TRUE(h.contains("int add(int a, int b)"));
|
||||
EXPECT_TRUE(h.contains("void reset()"));
|
||||
EXPECT_TRUE(h.contains("int add(int a, int b, logos::CallError* err = nullptr)"));
|
||||
EXPECT_TRUE(h.contains("void reset(logos::CallError* err = nullptr)"));
|
||||
// Non-invokable should not appear
|
||||
EXPECT_FALSE(h.contains("internal"));
|
||||
}
|
||||
@@ -197,7 +197,7 @@ TEST(MakeHeaderTest, QVariantListAsyncOverload)
|
||||
}
|
||||
|
||||
QString h = makeHeader("mod", "Mod", methods);
|
||||
EXPECT_TRUE(h.contains("QVariantList getItems()"));
|
||||
EXPECT_TRUE(h.contains("QVariantList getItems(logos::CallError* err = nullptr)"));
|
||||
EXPECT_TRUE(h.contains("getItemsAsync("));
|
||||
EXPECT_TRUE(h.contains("std::function<void(QVariantList)> callback"));
|
||||
}
|
||||
@@ -215,7 +215,7 @@ TEST(MakeHeaderTest, QVariantMapAsyncOverload)
|
||||
}
|
||||
|
||||
QString h = makeHeader("mod", "Mod", methods);
|
||||
EXPECT_TRUE(h.contains("QVariantMap getData()"));
|
||||
EXPECT_TRUE(h.contains("QVariantMap getData(logos::CallError* err = nullptr)"));
|
||||
EXPECT_TRUE(h.contains("getDataAsync("));
|
||||
EXPECT_TRUE(h.contains("std::function<void(QVariantMap)> callback"));
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ TEST(MakeSourceTest, ZeroParams)
|
||||
QJsonArray methods;
|
||||
methods.append(makeMethod("doStuff", "int", 0));
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"doStuff\")"));
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"doStuff\", QVariantList{}, Timeout(), &_err)"));
|
||||
EXPECT_TRUE(src.contains("return _result.toInt()"));
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ TEST(MakeSourceTest, OneParam)
|
||||
QJsonArray methods;
|
||||
methods.append(makeMethod("fn", "bool", 1));
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"fn\", p0)"));
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"fn\", QVariantList{p0}, Timeout(), &_err)"));
|
||||
EXPECT_TRUE(src.contains("return _result.toBool()"));
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ TEST(MakeSourceTest, TwoParams)
|
||||
QJsonArray methods;
|
||||
methods.append(makeMethod("fn", "void", 2));
|
||||
QString src = makeSource("mod", "Mod", "mod.h", methods);
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"fn\", p0, p1)"));
|
||||
EXPECT_TRUE(src.contains("m_client->invokeRemoteMethod(\"mod\", \"fn\", QVariantList{p0, p1}, Timeout(), &_err)"));
|
||||
}
|
||||
|
||||
TEST(MakeSourceTest, ThreeParams)
|
||||
|
||||
Reference in New Issue
Block a user