C++ 如何";如果当前语句出错,则退出当前函数;在C++;
我从一个例子开始阐述我的问题。并在最后以问题的确切陈述作为结论 在C语言中,我们可以编写这样的宏C++ 如何";如果当前语句出错,则退出当前函数;在C++;,c++,c,logging,macros,runtime-error,C++,C,Logging,Macros,Runtime Error,我从一个例子开始阐述我的问题。并在最后以问题的确切陈述作为结论 在C语言中,我们可以编写这样的宏 #define NO_ERROR 0 #define RETURN_IF_ERROR(function) \ { \ RetCode = function;
#define NO_ERROR 0
#define RETURN_IF_ERROR(function) \
{ \
RetCode = function; \
if (RetCode != NO_ERROR) \
{ \
LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) ); \
return RetCode; \
} \
else { \
LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );\
} \
}
int ConstructAxes() {
RETURN_IF_ERROR(GetAxis("alpha"));
RETURN_IF_ERROR(GetAxis("beta"));
RETURN_IF_ERROR(GetAxis("gamma"));
RETURN_IF_ERROR(GetAxis("delta"));
.
.
.
}
现在这个宏可以用在这样的函数中
#define NO_ERROR 0
#define RETURN_IF_ERROR(function) \
{ \
RetCode = function; \
if (RetCode != NO_ERROR) \
{ \
LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) ); \
return RetCode; \
} \
else { \
LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );\
} \
}
int ConstructAxes() {
RETURN_IF_ERROR(GetAxis("alpha"));
RETURN_IF_ERROR(GetAxis("beta"));
RETURN_IF_ERROR(GetAxis("gamma"));
RETURN_IF_ERROR(GetAxis("delta"));
.
.
.
}
因此,如果在其中调用的函数之一返回错误,我们立即退出当前函数(例如ConstructAxis),并返回错误代码。如果没有这个宏,对于这4条语句中的每一条,我都必须编写一个if…else块。最后,我将使用4行代码来显示实际的功能或任务,然后再执行16行错误检查(和/或可选日志记录)。(我见过700多行的函数,只有20~30行的功能,600多行的if…else错误检查和日志记录。遵循主逻辑不是很容易。)
(p.s.在任何人指出之前,我不能使用异常。这是一个遗留项目,没有使用也不希望使用异常,我也不是编写异常安全代码的专家。在任何人指出之前,返回的错误代码会在更高级别上重新解释为一些有意义的文本。在哪里以及如何与此无关(我手边有一块石头。)
问题是,宏可能是有问题的,我更喜欢函数。所以C++中有一些简洁而优雅的方式来使用宏吗?我感觉不可能。 你不能直接做你想做的事情,你可以接近:
int ConstructAxes() {
int retCode = NO_ERROR;
auto fail_if_error = [&retCode](int result) -> bool {
retCode = result;
if (retCode != NO_ERROR) {
LOG(ERROR, "[%d] line [%d] [%s]", retCode, __LINE__, __FILE__);
return false;
}
LOG(VERBOSE, "[%d] line [%d] [%s]", retCode, __LINE__, __FILE__);
return true;
};
fail_if_error(GetAxis("alpha"))
&& fail_if_error(GetAxis("beta"))
&& fail_if_error(GetAxis("gamma"))
&& fail_if_error(GetAxis("delta"));
return retCode;
}
虽然你不能直接做你想做的事,但你可以接近:
int ConstructAxes() {
int retCode = NO_ERROR;
auto fail_if_error = [&retCode](int result) -> bool {
retCode = result;
if (retCode != NO_ERROR) {
LOG(ERROR, "[%d] line [%d] [%s]", retCode, __LINE__, __FILE__);
return false;
}
LOG(VERBOSE, "[%d] line [%d] [%s]", retCode, __LINE__, __FILE__);
return true;
};
fail_if_error(GetAxis("alpha"))
&& fail_if_error(GetAxis("beta"))
&& fail_if_error(GetAxis("gamma"))
&& fail_if_error(GetAxis("delta"));
return retCode;
}
由于您似乎在寻找更多的C解决方案,因此我建议的唯一改进是在函数中有一个错误处理点(由Felix在注释中建议),如果需要,该点还将执行清理
#define RETURN_IF_ERROR(function) \
{ \
RetCode = function; \
if (RetCode != NO_ERROR) \
{ \
goto return_with_error; \
} \
else { \
LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );\
} \
}
int ConstructAxes() {
int RetCode;
RETURN_IF_ERROR(GetAxis("alpha"));
RETURN_IF_ERROR(GetAxis("beta"));
RETURN_IF_ERROR(GetAxis("gamma"));
RETURN_IF_ERROR(GetAxis("delta"));
.
.
.
return RetCode;
return_with_error:
cleanup();
LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
return RetCode;
}
使用goto进行错误处理和清理。由于您似乎在寻找更多的C解决方案,因此我建议对您现在所做的唯一改进是在函数中设置一个错误处理点(Felix在注释中建议),然后在需要时也执行清理
#define RETURN_IF_ERROR(function) \
{ \
RetCode = function; \
if (RetCode != NO_ERROR) \
{ \
goto return_with_error; \
} \
else { \
LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );\
} \
}
int ConstructAxes() {
int RetCode;
RETURN_IF_ERROR(GetAxis("alpha"));
RETURN_IF_ERROR(GetAxis("beta"));
RETURN_IF_ERROR(GetAxis("gamma"));
RETURN_IF_ERROR(GetAxis("delta"));
.
.
.
return RetCode;
return_with_error:
cleanup();
LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
return RetCode;
}
使用GOTO进行错误处理和清理。
< P> OK,对于嵌入式设备,您可能需要避免任何复杂的C++内存,这可能需要内存分配。但是,我会将必须总是发生的日志部分和块短出口分开。 int test_and_log(int RetCode) {
if (RetCode != NO_ERROR)
{
LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
}
else {
LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
}
return RetCode;
}
然后是一个极简宏(C方式):
<>但是对于C++,我仍然使用极小值异常处理来抛出一个int值:
void test_and_log(int RetCode) {
if (RetCode != NO_ERROR)
{
LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
throw RetCode;
}
else {
LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
}
}
然后:
int ConstructAxes() {
try {
test_and_log(GetAxis("beta"));
test_and_log(GetAxis("gamma"));
test_and_log(GetAxis("delta"));
.
.
.
}
catch (int RetCode) {
return RetCode;
}
return 0; // ensure a return value if every line passes
}
这是一个相当棘手的问题,因为最佳实践建议只抛出
std::exception
的子类来进行一致的异常处理。但是正如您所说的,您不想在应用程序中使用异常,这是可以接受的。嵌入式系统的优点是从来没有构造过简单的异常对象。但是请不要在普通代码中使用它
如果您想用内存交换处理时间,您可以用<代码> >内联< /COL>说明符…< /P> > P> >声明“<代码>测试程序>和代码>……对于嵌入式设备,您可能需要避免任何复杂的C++内存,这可能需要内存分配。但是,我会将必须总是发生的日志部分分割,并且该块短出口。
int test_and_log(int RetCode) {
if (RetCode != NO_ERROR)
{
LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
}
else {
LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
}
return RetCode;
}
然后是一个极简宏(C方式):
<>但是对于C++,我仍然使用极小值异常处理来抛出一个int值:
void test_and_log(int RetCode) {
if (RetCode != NO_ERROR)
{
LOG(ERROR,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
throw RetCode;
}
else {
LOG(VERBOSE,"[%d] line [%d] [%s]", RetCode, __LINE__, __FILE__ ) );
}
}
然后:
int ConstructAxes() {
try {
test_and_log(GetAxis("beta"));
test_and_log(GetAxis("gamma"));
test_and_log(GetAxis("delta"));
.
.
.
}
catch (int RetCode) {
return RetCode;
}
return 0; // ensure a return value if every line passes
}
这是一个相当棘手的问题,因为最佳实践建议只抛出
std::exception
的子类来进行一致的异常处理。但是正如您所说的,您不想在应用程序中使用异常,这是可以接受的。嵌入式系统的优点是从来没有构造过简单的异常对象。但是请不要在普通代码中使用它
如果您想用内存交换处理时间,则始终可以使用
内联说明符声明test\u和
。
你的方法还可以,如果使用不当,马可斯只是个问题
因此,我选择结构良好的do{…break on error…}while(0)
方法。这也有助于您坚持一个模式,即一个函数只有一个入口点和一个出口点
另外,为了调试和易于阅读,我将“跳转”语句(break
此处)移出宏
#define NO_ERROR (0)
// log level IDs
#define LL_ERROR (0)
// more log levels here
#define LL_VERBOSE (10)
// more log levels here
#define LL_DEBUG (13)
// more log levels here
#define LL_MAXIMUM (16)
// log level long names
static const char * log_level_names[LL_MAXIMUM] = {
"error",
// more here
"verbose"
// more here
"debug"
// more here
}
int loglevel = LL_ERROR; // default logging-level to error; to be set to any LL_xxxx
// return date-time-stamp string (can be any, and most likely should be ;-)
#define DATETIMESTAMP_STR asctime(localtime(time(NULL)))
// Generic logging
#define LOG(ll, fmt, rc, ...) \
while (ll <= loglevel) { \
fprintf(stderr, "%s [%s]: " fmt, DATETIMESTAMP_STR, log_level_name[loglevel], __VA_ARGS__); \
break; \
};
// Specific logging
#define LOG_ERROR_OR_VERBOSE(rc, line, fname) \
do { \
LOG(NO_ERROR != (rc) ?LL_ERROR :LL_VERBOSE, "[%d] line [%d] [%s]", rc, line, fname); \
while (0)
int foo(void)
{
int result = NO_ERROR;
LOG(LL_DEBUG, "entering '%s'", __func__);
do {
result = bar1(...);
LOG_ERROR_OR_VERBOSE(result, __LINE__, __FILE__);
if (NO_ERROR <> result)
{ break; }
result = bar2(...);
LOG_ERROR_OR_VERBOSE(result, __LINE__, __FILE__);
if (NO_ERROR <> result)
{ break; }
...
} while (0);
LOG(LL_DEBUG, "leaving '%s' (rc = %d)", __func__, result);
return result;
}
到
这使您的信噪比为1,这是最佳的,我觉得…:-)谈论C:
你的方法还可以,如果使用不当,马可斯只是个问题
因此,我选择结构良好的do{…出错时中断…},而(0)
方法。这也有助于您坚持一个模式,即一个函数只有一个入口点和一个出口点
另外,为了调试和易于阅读,我将“跳转”语句(break
此处)移出宏
#define NO_ERROR (0)
// log level IDs
#define LL_ERROR (0)
// more log levels here
#define LL_VERBOSE (10)
// more log levels here
#define LL_DEBUG (13)
// more log levels here
#define LL_MAXIMUM (16)
// log level long names
static const char * log_level_names[LL_MAXIMUM] = {
"error",
// more here
"verbose"
// more here
"debug"
// more here
}
int loglevel = LL_ERROR; // default logging-level to error; to be set to any LL_xxxx
// return date-time-stamp string (can be any, and most likely should be ;-)
#define DATETIMESTAMP_STR asctime(localtime(time(NULL)))
// Generic logging
#define LOG(ll, fmt, rc, ...) \
while (ll <= loglevel) { \
fprintf(stderr, "%s [%s]: " fmt, DATETIMESTAMP_STR, log_level_name[loglevel], __VA_ARGS__); \
break; \
};
// Specific logging
#define LOG_ERROR_OR_VERBOSE(rc, line, fname) \
do { \
LOG(NO_ERROR != (rc) ?LL_ERROR :LL_VERBOSE, "[%d] line [%d] [%s]", rc, line, fname); \
while (0)
int foo(void)
{
int result = NO_ERROR;
LOG(LL_DEBUG, "entering '%s'", __func__);
do {
result = bar1(...);
LOG_ERROR_OR_VERBOSE(result, __LINE__, __FILE__);
if (NO_ERROR <> result)
{ break; }
result = bar2(...);
LOG_ERROR_OR_VERBOSE(result, __LINE__, __FILE__);
if (NO_ERROR <> result)
{ break; }
...
} while (0);
LOG(LL_DEBUG, "leaving '%s' (rc = %d)", __func__, result);
return result;
}
到
这使您的信噪比为1,这是最佳的,我觉得…:-) 您可以编写一个循环,类似于:
int RunFunctions(int line, // __LINE__
const char* file, // __FILE__
std::initializer_list<std::function<int()>> functions)
{
int counter = 0;
for (auto f : functions) {
auto RetCode = f();
if (RetCode != NO_ERROR) {
LOG(ERROR,"[%d] line [%d] [%s] [%d]", RetCode, line, file, counter ));
return RetCode;
} else {
LOG(VERBOSE,"[%d] line [%d] [%s] [%d]", RetCode, line, file, counter ) );
}
++counter;
}
return NO_ERROR;
}
int ConstructAxes() {
return RunFunctions(__LINE__, __FILE__, {
[]() { return GetAxis("alpha"); },
[]() { return GetAxis("beta"); },
[]() { return GetAxis("gamma"); },
[]() { return GetAxis("delta"); }
});
}
int运行函数(int行,//\uu行__
常量字符*文件,//\u文件__
std::初始值设定项(列表函数)
{
int计数器=0;
用于(自动f:功能){
自动重新编码=f();
如果(RetCode!=无错误){
日志(错误,[%d]行[%d][%s][%d]”,RetCode,行,文件,计数器);
返回代码;
}否则{
日志(详细,[%d]行[%d][%s][%d]”,RetCode,行,文件,计数器);
}
++计数器;
}
返回无错误;
}
int constructionaxes(){
返回运行函数(_行、_文件、{
[](){return GetAxis(“alpha”);},
[](){return GetAxis(“beta”);},
[](){return GetAxis(“gamma”);},
[]()