【发布时间】:2019-01-02 04:48:45
【问题描述】:
问题
我到处都出现了以下简单的情况。大量请求通过如下函数签名到达设备:
Err execute( const ICommandContext &context,
const RoutineArguments &arguments,
RoutineResults &results)
本质上是一个请求处理服务器,它将调用这个函数来执行具有这些签名的各种请求类型的函数。在出错的情况下,我们有 2 条返回路径。
-
Err输出类型(认为它等同于int)用于通知服务器或系统出现与系统有关的问题,而不是请求。在处理用户请求之前,它始终排在函数的顶部。 -
RoutineResults提供了一个setStatus函数,可用于将请求的失败信息返回给客户端。
出于这个原因,我们弹出了很多此类代码:
// Failure due to request
Err error = someFunctionCall(clientInput);
if (!error.success()) {
results.setStatus(error); // Inform the client of the error
return SUCCESS; // Inform the system that we are all good
}
我们有一个特定的请求类型,它包含大约15 参数,这些参数在系统周围传入和发送。从概念上讲,我们需要 15 个 if error do 集合,这似乎很浪费。如果我们需要检查并更改返回方式的任何内容,也很容易出错。 我们如何有效地委派setStatus 并返回只需要在函数中发生一次的少量代码?
宏观解决方案
c 系统可能会使用宏来解决此问题,例如:
#define M_InitTry Err error
#define M_Try(statement) if (!(error = statement).success()) { goto catch_lab; }
#define M_Catch catch_lab: if (!error.successs())
#define M_Return return error
会这样使用:
Err execute( const ICommandContext &context, ...) {
M_InitTry;
...
M_Try(someFunctionCall(clientInput));
M_Try(someFunctionCall(otherClientInput));
...
M_Catch {
// Other specific actions for dealing with the return.
results.setStatus(error);
error = SUCCESS;
}
M_Return;
}
这很好地清理了代码,但对于goto 并不是特别好。如果定义可能被goto 跳过的变量,则会导致问题。
委托解决方案
我试图想出更多 C++,所以我认为 RAII 类型的委托可能会有所帮助。比如:
class DelegateToFunctionEnd {
typedef std::function<void(void)> EndFunction;
public:
DelegateToFunctionEnd(EndFunction endFunction) : callAtEnd(endFunction) { }
~DelegateToFunctionEnd() {
callAtEnd();
}
private:
EndFunction callAtEnd;
};
很简单,它通过在析构函数中实现动作来执行动作的委托,直到函数返回。你可以这样使用它:
Err execute( const ICommandContext &context, ...) {
Err error;
DelegateToFunctionEnd del(std::bind(&RoutineResults::setStatus, &results, std::cref(error)));
error = someFunctionCall(clientInput));
if (error) return SUCCESS;
...
}
Live example. 这个解决方案看起来不错,但有几个问题:
- 目前还不清楚发生了什么。
- 正确设置错误更容易出错。
- 您仍然需要大量的
if语句来处理退货。 - 配置终止操作的能力不是很好。
- 如果用户在函数返回时没有仔细考虑项目的销毁顺序,就会很危险。
更好的解决方案?
这一定是一个经常出现的问题。是否有一个通用的解决方案可以提供该集合的干净委托并返回类型操作?
我在下面有一些不幸的限制。不要让这些阻止你回答,因为它可能对未来的人有帮助。
- 我正在开发一个 c++03 受限系统。我们有
boost,但没有 c++11。 - 嵌入式系统,我们有关于异常和内存分配的愚蠢规则。
【问题讨论】:
-
你能有一个
function<Err()>的数组,然后用你的检查遍历那个数组吗?for (auto&& f : functions) { auto error = f(); if (error) {/*..*/} }之类的东西? -
@Jarod42 可能是的,使用
boost::function实现会很有趣,但可能非常可行。您应该将此添加为答案。 -
简短回答 - 看看 haskell
Exception是如何创建的。基本上它包装函数并根据发生的情况传播返回值或错误,直到它到达结束/错误处理程序。我会尽快给出更详细的答案