C ++如何重用具有多个returns(出口点)的代码块?
C++ How to reuse code blocks with multiple returns (exit points)?
我有一个执行运动控制程序的代码块。基本上,它要求伺服电机移动到某个位置。
//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;
}
每次我需要这个 "move to" 程序时,我都必须复制粘贴这个代码块,只修改运动参数。而且实际的代码块比上面显示的要大得多,这就是为什么我不知道将它定义为 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"?
基本上可以把代码块移到自己的函数中,只需要添加一个成功return.
请注意,我不知道你的类型,所以所有内容都在 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;
}
写代码的时候,一定要写得好看、易读,这样以后维护起来就不会有问题了。宏没有提供保持代码可读性的好方法,因为宏调用被替换为宏主体本身,这也可能导致错误行为和意外结果。
将它放在函数中更容易:
#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;
}
这样就不需要 copy/paste 代码,没有不需要的变量声明,并且您有一个函数可以在一个地方收集所有必需的功能
只有在 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 风格的 return 标志时。我可以建议以下之一:
- 创建正确的错误层次结构并抛出错误
- 使用断言,比如
assert(setVelocity(vel) != 0);
(顺便说一句,为什么这个叫set?不会在这里结合赋值和错误处理,如果那是例。)
- 至少使用一些不仅仅是整数的东西,比如枚举class(我自己不喜欢那个解决方案,但比宏值更好)
- 至少使用一个
constexpr
而不是宏值
但我真的会推荐前两个中的一个。在大部分代码中,除了主要部分,我将 return 解释为成功。其他一切都应由适当的错误或断言处理。
(我假设这些实际上是错误,如果这些确实是其他部分使用的结果,那么一定要使用 return 值。但是再次考虑使用枚举 class.)
您可以创建辅助函数:
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); }
});
}
我有一个执行运动控制程序的代码块。基本上,它要求伺服电机移动到某个位置。
//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;
}
每次我需要这个 "move to" 程序时,我都必须复制粘贴这个代码块,只修改运动参数。而且实际的代码块比上面显示的要大得多,这就是为什么我不知道将它定义为 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"?
基本上可以把代码块移到自己的函数中,只需要添加一个成功return.
请注意,我不知道你的类型,所以所有内容都在 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;
}
写代码的时候,一定要写得好看、易读,这样以后维护起来就不会有问题了。宏没有提供保持代码可读性的好方法,因为宏调用被替换为宏主体本身,这也可能导致错误行为和意外结果。
将它放在函数中更容易:
#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;
}
这样就不需要 copy/paste 代码,没有不需要的变量声明,并且您有一个函数可以在一个地方收集所有必需的功能
只有在 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 风格的 return 标志时。我可以建议以下之一:
- 创建正确的错误层次结构并抛出错误
- 使用断言,比如
assert(setVelocity(vel) != 0);
(顺便说一句,为什么这个叫set?不会在这里结合赋值和错误处理,如果那是例。) - 至少使用一些不仅仅是整数的东西,比如枚举class(我自己不喜欢那个解决方案,但比宏值更好)
- 至少使用一个
constexpr
而不是宏值
但我真的会推荐前两个中的一个。在大部分代码中,除了主要部分,我将 return 解释为成功。其他一切都应由适当的错误或断言处理。
(我假设这些实际上是错误,如果这些确实是其他部分使用的结果,那么一定要使用 return 值。但是再次考虑使用枚举 class.)
您可以创建辅助函数:
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); }
});
}