C ++如何重用具有多个返回(退出点)的代码块?

时间:2017-08-15 08:52:56

标签: c++ macros readability

我有一个执行运动控制程序的代码块。基本上,它要求伺服电机移动到某个位置。

//code block starts

err=setVelocity(vel);
if(err!=0) return ERR_FAIL;
err=setAcceleration(acc);
if(err!=0) return ERR_FAIL;
err=setDeceleration(dec);
if(err!=0) return ERR_FAIL;
err=setPosition(pos);
if(err!=0) return ERR_FAIL;
err=startMotion();
if(err!=0) return ERR_FAIL;

//code block ends

例如,在归位程序中使用代码块。

#define ERR_SUCCESS      0
#define ERR_FAIL         1

short GoHome(){

long vel,acc,dec,pos;
short err;

//Move to home
//code block starts
vel=vel1,acc=acc1,dec=dec1,pos=pos1;
...
//code block ends

//Move to offset
//code block starts
vel=vel2,acc=acc2,dec=dec2,pos=pos2;
...
//code block ends

return ERR_SUCCESS;
}

每当我需要这个“移动到”过程时,我必须复制粘贴此代码块,只需修改运动参数。实际的代码块比上面显示的大得多,这就是为什么我不知道将它定义为MACRO是否是个好主意。

实际上我确实定义了一个MACRO来处理错误。

#define IfErrThenReturn(err){  \
CString errInfo;               \
if(err!=0){                    \
    switch(err){               \
    case 0: errInfo = "Command succeeded.\n"; break;        \
    case 1: errInfo = "Command failed.\n"; break;           \
    case 2: errInfo = "Unsupported license.\n"; break;      \
    case 3: errInfo = "Parameter Error.\n"; break;          \
    ...                        \
    }                          \
    AfxMessageBox(errInfo);    \
    return EM_ERR_FAIL;        \
}                              \

所以代码块就像

//code block starts

err=setVelocity(vel);
IfErrThenReturn(err);
...

//code block ends

但我不知道将整个代码块定义为宏是明智的。有人能告诉我是否有其他方法可以提高更高级别程序的可读性,例如使用代码块的“GoHome”?

5 个答案:

答案 0 :(得分:1)

基本上,您可以将代码块移动到自己的函数中,只需要添加成功返回。

注意我不知道你的类型,所以这里的所有内容都是int

int foo(int vel, int acc, int dec, int pos)
{
    //code block starts

    err=setVelocity(vel);
    if(err!=0) return ERR_FAIL;
    err=setAcceleration(acc);
    if(err!=0) return ERR_FAIL;
    err=setDeceleration(dec);
    if(err!=0) return ERR_FAIL;
    err=setPosition(pos);
    if(err!=0) return ERR_FAIL;
    err=startMotion();
    if(err!=0) return ERR_FAIL;

    return SUCCESS;
    //code block ends
}

使用

int result;
if ((result = foo(vel,acc,dec,pos)) != SUCCESS)
{
    return result;
}

答案 1 :(得分:1)

在编写代码时,始终保持良好和可读性非常重要,以便将来的维护不会出现问题。由于宏调用被宏体本身替换,宏不能提供保持代码可读性的好方法,这也可能导致不良行为产生意外结果。

在函数中使用它更容易:

#define ERR_SUCCESS      0
#define ERR_FAIL         1

bool relocate(long vel, lon acc, long dec, long pos)
{
    int err=setVelocity(vel);
    if(err!=0) {
        return false;
    }

    err=setAcceleration(acc);
    if(err!=0) {
        return false;
    }

    err=setDeceleration(dec);
    if(err!=0) {
        return false;
    }

    err=setPosition(pos);
    if(err!=0) {
        return false;
    }

    err=startMotion();
    if(err!=0) {
        return false;
    }

    return true;
}

short GoHome()
{

    //Move to home
    if(!relocate(vel1, acc1, dec1, pos1)
    {
        return ERR_FAIL;
    }

    //Move to offset
    if(!relocate(vel2, acc2, dec2, pos2)
    {
        return ERR_FAIL;
    }
    return ERR_SUCCESS;
}

这样就不需要复制/粘贴代码,不需要变量声明,并且你有一个功能可以在一个地方收集所有必需的功能

答案 2 :(得分:1)

不是一个真正的答案,但我认为是相关的:

我有点反对在没有真正需要的情况下使用宏。特别是在将它们绑在C型返回标志上时。我可能会建议以下之一:

  • 创建正确的错误层次结构并抛出错误
  • 使用assert,就像assert(setVelocity(vel) != 0);一样(顺便说一下,为什么这个名为 set ?如果是这样的话,那么就不会将赋值和错误处理结合起来。)
  • 至少使用不仅仅是整数的东西,比如枚举类(不要自己喜欢这个解决方案,但要比宏值更好)
  • 至少使用constexpr代替宏值

但我真的推荐前两个中的一个。在代码的大部分内容中,除了主要代码之外,我将回报视为成功。其他一切都应该通过适当的错误或断言来处理。

(我假设那些实际上是错误,如果那些确实是其他部分使用的结果,那么无论如何都要使用返回值。但是再次考虑使用枚举类。)

答案 3 :(得分:0)

仅在C / C ++中没有可能时才使用处理器

您的案例可以改写为

#define ERR_SUCCESS      0
#define ERR_FAIL         1

bool move(long vel, long acc, long dec, long pos)
{
  return 
    (0 == setVelocity(vel)) &&
    (0 == setAcceleration(acc)) &&
    (0 == setDeceleration(dec)) &&
    (0 == setPosition(pos)) &&
    (0 == startMotion());
}

short GoHome() {

//Move to home
   if (! move(vel1,acc1,dec1,pos1)) return ERR_FAIL;

//Move to offset
   if (! move(vel2,acc2,dec2,pos2)) return ERR_FAIL;

   return ERR_SUCCESS;
}

答案 4 :(得分:0)

您可以创建辅助函数:

enum class ErrorStatus { Fail, Success };

ErrorStatus DoJobs(std::initializer_list<std::function<ErrorStatus()>> actions)
{
    for (auto&& action : actions) {
        auto err = action();
        if (err != ErrorStatus::Success) {
            return err;
        }
    }
    return ErrorStatus::Success;
 }

ErrorStatus relocate(long vel, long acc, long dec, long pos)
{
    return DoJobs( {
        [&](){ return setVelocity(vel); },
        [&](){ return setAcceleration(acc); },
        [&](){ return setDeceleration(dec); },
        [&](){ return setPosition(pos); },
        [](){ return startMotion(); }});
}

最后:

ErrorStatus GoHome(){
    return DoJobs( {
        [](){return relocate(vel1, acc1, dec1, pos1); },
        [](){return relocate(vel2, acc2, dec2, pos2); }
    });
}