如何将动作委派给函数返回?

时间:2018-07-25 11:22:46

标签: c++ c++03

问题

我到处都有以下简单情况。具有如下功能签名的大量请求到达设备:

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

本质上是一个请求处理服务器,它将对具有这些签名的各种请求类型调用此执行功能。如果发生错误,我们有2条返回路径。

  1. Err输出类型(认为它等同于int),用于通知服务器或系统出现问题的原因与系统有关,而不是与请求有关。在处理用户请求之前,始终将其排在函数顶部。
  2. 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个参数,这些参数传入并在系统周围发送。从概念上讲,我们需要此if error do集中的15个似乎很浪费。如果我们需要经历并更改有关返回方式的任何信息,也容易出错。 我们如何有效地委派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++,所以我认为RAI​​I类型的委托可能会有所帮助。像这样:

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. 这个解决方案似乎还可以,但是有几个问题:

  1. 目前还不清楚。
  2. 在正确设置错误方面容易出错。
  3. 您仍然需要大量if语句来处理退货。
  4. 配置终止操作的能力不是很好。
  5. 如果用户在返回函数时没有仔细考虑项目的销毁顺序,将很危险。

更好的解决方案?

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


以下我有一些不幸的限制。不要让这些阻止您回答,因为这可能对将来的人有所帮助。

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

4 个答案:

答案 0 :(得分:5)

如果错误状态代码证明很麻烦,则应考虑使用异常代替。也就是说,更改您的函数的API

  • 因此,保证他们在事后取得成功
  • 发生故障时抛出合适的std::exception

如果这样做,“忘记”检查状态码是不可能的。如果选择不处理错误情况,则低级代码引发的异常会自动向上渗透。您仅需要catch一个低级别的例外,如果

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

答案 1 :(得分:1)

拆分功能。

内部函数根据用户输入返回错误代码;外部将其转换为客户端错误,并且仅返回服务器端错误。

内部功能包含:

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

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

Err只需要一个运算符bool。如果无法创建,请创建一个可转换为Err或从Err转换并具有操作符bool的类型。

答案 2 :(得分:1)

也许,您可以将语句写为数组,例如:

{
  "name": "WRONG NAME"
}

,如果您仅使用不同的语句多次这样做, 您可以创建

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);

答案 3 :(得分:0)

您可以向错误中添加执行检查等操作并返回布尔值的方法吗?

if(!someFunctionCall(clientInput).handleSuccess(results))
{
    return SUCCESS;
}
相关问题