如何将一个动作委托给函数return?

How to delegate an action to function return?

问题

我到处都出现以下简单情况。大量请求使用这样的函数签名到达设备:

Err execute( const ICommandContext &context, 
         const RoutineArguments &arguments, 
         RoutineResults &results)

本质上有一个请求处理服务器,它将调用此函数来执行具有这些签名的各种请求类型的函数。在出错的情况下,我们有 2 return 个路径。

  1. Err 输出类型(认为它等同于 int)用于通知服务器或系统出现与系统有关的错误,而不是要求。在处理用户请求之前,它总是排在函数的顶部。
  2. RoutineResults提供了一个setStatus函数,可以用来return向客户端发送请求的失败信息

因此,我们弹出了很多此类代码:

// 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 个参数,这些参数在系统中传入和传出。我们在概念上需要这个 if error do 集合中的 15 个,这看起来很浪费。如果我们需要修改和更改我们 return 的方式,它也很容易出错。 我们如何有效地将 setStatus 和 return 委托给函数中只需要发生一次的少量代码?

宏解决方案

一个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;
};

非常简单,它通过在析构函数中实现操作来委托操作直到函数 return。你可以这样使用它:

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. 这个解决方案看起来不错,但有几个问题:

  1. 不清楚发生了什么。
  2. 正确设置error更容易出错
  3. 你还需要大量的if语句来处理returns。
  4. 配置终止动作的能力不是很好。
  5. 如果用户不仔细考虑函数 return 中的项目的销毁顺序是危险的。

更好的解决方案?

这一定是经常出现的问题。是否有一个通用解决方案可以提供此集合和 returns 类型操作的干净委托?


我在下面有一些不幸的限制。不要让这些阻止你回答,因为它可能对未来的人有帮助。

  1. 我正在使用 c++03 受限系统。我们有 boost,但没有 c++11。
  2. 嵌入式系统,我们有关于异常和内存分配的愚蠢规则。

如果错误状态代码很麻烦,您应该考虑改用异常。也就是说,改变你的函数API

  • 所以他们保证在post-条件下取得成功
  • 失败时抛出一个合适的std::exception

如果这样做,"forget" 将无法检查状态代码。如果您选择不处理错误情况,低级代码抛出的异常会自动向上渗透。如果

,你只需要catch一个低级异常
  • 您需要在发生错误时进行一些手动回滚或重新分配, 并且 RAII 是不切实际的。在这种情况下,您将重新抛出异常。
  • 您想使用抛出的嵌套异常将低级异常消息或异常类型转换为高级消息。

拆分函数。

内部函数returns一个基于用户输入的错误代码;外部将其转换为客户端错误,并且只有 returns 服务器端错误。

内部函数包含:

if(Err error = someFunctionCall(clientInput))
  return error;

反复。外部有中继到客户端的错误代码,但只有一次。

Err 只需要一个运算符 bool。如果它不能拥有它,请创建一个转换 to/from Err 并具有运算符 bool.

的类型

你能给错误添加一个方法来做检查等吗return一个布尔值。

if(!someFunctionCall(clientInput).handleSuccess(results))
{
    return SUCCESS;
}

或许,您可以将语句写成数组,例如:

Err execute( const ICommandContext &context, ...)
{
    const boost::function<Err()> functions[] = {
        boost::bind(&someFunctionCall, std::ref(clientInput)),
        boost::bind(&someFunctionCall, std::ref(otherClientInput)),
        // ...
    };

    for (std::size_t i = 0; i != sizeof(functions) / sizeof(functions[0]); ++i) {
        Err err = functions[i]();

        if (!err.successs()) {
            results.setStatus(err);
            return SUCCESS;
        }
    }
    return SUCCESS;
}

如果你多次使用不同的语句, 你可能会创造

Err execute_functions(const ICommandContext &context, std::function<Err()> functions);

也许还可以根据您的需要提供其他入口点 OnError