我从一个例子开始阐述我的问题。并在最后给出问题的确切说明。
所以在C语言中,我们可以编写这样的宏
#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"));
.
.
.
}
因此,我们使用错误代码立即退出当前函数(例如ConstructAxes),如果在其中调用的函数之一返回错误。如果没有该宏,则对于这4条语句中的每条语句,我都必须编写一个if ... else块。我最终得到4行代码,它们显示实际的功能或任务,然后再进行16行错误检查(和/或可选的日志记录)。 (我见过的功能有700多行,只有20〜30行功能,还有600多行if ... else错误检查和记录。那么遵循主要逻辑并不容易。)
(ps,在任何人指出之前,我不能使用异常。这是一个遗留项目,并且不使用也不希望使用异常,我也不是编写异常安全代码的专家。在任何人指出之前,返回的错误代码会在更高级别上重新解释为一些有意义的文本。与该问题无关的地方和方式。
问题是,宏可能有问题,我更喜欢一个函数。那么,在C ++中有没有使用宏的干净而优雅的方法呢?我觉得这是不可能的。
答案 0 :(得分:2)
好吧,对于嵌入式设备,您可能需要避免使用任何可能需要分配内存的复杂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语言):
#define RETURN_IF_ERROR(x) { int cr; if ((cr = test_and_log(x)) != NO_ERROR) return cr; }
int ConstructAxes() {
RETURN_IF_ERROR(GetAxis("beta"));
RETURN_IF_ERROR(GetAxis("gamma"));
RETURN_IF_ERROR(GetAxis("delta"));
.
.
.
return 0; // ensure a return value if every line passes
}
但是对于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
的子类以具有一致的异常处理。但是正如您所说的那样,您不希望在应用程序中使用异常,这是可以接受的。在嵌入式系统上的好处是,从来没有构造任何普通的异常对象。但是请不要在 normal 代码中使用它。
如果您要交换内存以获取处理时间,则始终可以使用test_and_log
指定符声明inline
...
答案 1 :(得分:0)
虽然您不能直接做自己想做的事,却可以亲近:
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;
}
答案 2 :(得分:0)
由于您似乎正在寻找更多的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进行错误处理和清除in C is fine。
答案 3 :(得分:0)
交谈C:
您的方法还可以,如果未正确使用marcos只是一个问题。
因此,我会选择结构合理的do {... break on error ...} while(0)
方法。这也有助于您坚持这种模式,即一个函数应只有一个入口点和一个出口点。
为了调试和易于阅读 ,我将移动“跳转”语句({{1 }))。超出宏。
break
这大致为您提供了1:3的信噪比,您可以通过更改来明显批准
#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;
}
到
if (NO_ERROR <> result)
{ break; }
另一个可能的改进就是更改
if (NO_ERROR <> result) { break; }
到
result = bar1(...);
LOG_ERROR_OR_VERBOSE(result, __LINE__, __FILE__);
这让您的SNR为1,这是我认为的最佳值::-)
答案 4 :(得分:0)
您可能会编写一个循环,例如:
colnames(mydata) <- c("country", "q1", "q2", "q4", "forestc","hdi", "ef", "hap")
results <- lme(forestc~q1*q2*q4, random=list(~1|ef, ~1|hdi, ~1|hap),data=mydata)
summary(results)
直到我们有std::source_location或类似的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"); }
});
}
,__LINE__
。
如果不需要捕获,则可以通过函数指针更改__FILE__
。