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

C++ C++;如何重用具有多个返回(退出点)的代码块?,c++,macros,readability,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) re

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

//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;
}
每次我需要这个“移动到”过程时,我必须复制粘贴这个代码块,只修改运动参数。实际的代码块比上面显示的要大得多,这就是为什么我不知道将其定义为宏是否是个好主意

实际上,我定义了一个宏来处理错误

#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”?

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

注意我不知道你的类型,所以这里的所有内容都是
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
}
使用


在编写代码时,一定要使代码美观易读,这样将来的维护就不会有维护问题。宏不能提供保持代码可读性的好方法,因为宏调用被替换为宏体本身,这也会导致错误行为和意外结果

将其置于函数中更容易:

#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;
}

通过这种方式,无需复制/粘贴代码,无需声明变量,并且您有一个函数可以在一个位置收集所有必需的功能

仅在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;
}

这不是一个实际的答案,但我认为是相关的:

我有点反对在没有实际需要的情况下使用宏。特别是当把它们绑在C型返回标志上时。我是否可以提出以下建议之一:

  • 创建正确的错误层次结构并抛出错误
  • 使用assert,比如
    assert(setVelocity(vel)!=0)(顺便问一下,为什么这个叫做set?如果是这样的话,这里不会将赋值和错误处理结合起来。)
  • 至少使用一些不仅仅是整数的东西,比如枚举类(我自己不喜欢这个解决方案,但比宏值更好)
  • 至少使用
    constexpr
    而不是宏值
但我真的推荐前两个中的一个。在大部分代码中,除了main之外,我将返回解释为成功。其他一切都应该通过适当的错误或断言来处理


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

您可以创建帮助器函数:

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

比起宏,你更喜欢内联函数和lambda。为什么不把这个块放到函数中呢?而且
vel
acc
dec
pos
闻起来好像它们属于一个结构(您可以传递给该函数),您应该使用异常进行错误处理。@tobi303因为如果代码块中的任何函数返回错误,我需要退出“GoHome”。@ricecakebear这不是不使用函数的理由。您也可以随时从函数返回(如果需要,还可以返回一个额外的标志以指示成功),我不喜欢布尔返回。这不是自我记录。可能有不同类型的错误返回。枚举会更好。@FrankPuffer OP也只有
返回ERR\u FAIL
@FrankPuffer当然可以,如果需要更复杂的处理,并且调用方需要知道哪个函数失败,那么它可以实现。然而,从这个问题开始,我觉得只要看看它是否失败就足够了,但你确实是对的:对于现实生活来说,这应该是一条路。@Frank Puffer是的,你是对的。只要我在“重新定位”中调用错误处理程序。我可以直接返回bool来告诉主函数是否返回。@fritzone:实际上我的重点不是处理多个案例。即使是两种情况,我也更喜欢枚举。如果没有进一步的信息,就不清楚返回值的含义。这样就可以了。谢谢。@ricecakebear我知道你还没有接受你之前问题的任何答案。。。如果你的问题解决了,你应该接受答案。有关如何接受答案的详细信息,请参阅:但“err”不是程序的错误。但是运动控制卡上的信息显示“运动控制卡拒绝或无法执行该功能”。所以我认为这与终止整个计划无关。如果我理解正确的话。@ricecakebear是的,我想我开始时的假设有点错误。不过,在这种情况下,还是使用enum类。@Aziuth:在
assert
as
assert(setVelocity(vel)!=0)
中没有副作用。否则发布版本(带有
NDEBUG
set)将无法按预期运行。@Jarod42不确定setVelocity的功能,如果有副作用,请确定。我觉得有一个setter(?)首先似乎返回(?)一些值有点奇怪-宁愿去掉这个副作用,而不是断言,然后作为一个单独的步骤来做现在的副作用。
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); }
    });
}