C 错误处理异常,如
Posted
技术标签:
【中文标题】C 错误处理异常,如【英文标题】:C error-handling exception like 【发布时间】:2017-10-11 17:22:47 【问题描述】:我想在 C 中做一些错误处理。我喜欢 C# 异常,所以决定做这样的事情。目标是:
调用函数中的单行错误处理,例如 C# 中的抛出异常 中断发生错误的调用函数中的代码序列 了解发生错误的文件/行 关于错误的口头描述您认为这种方法有什么问题吗?
调用函数:
EError activate_add(uint32_t key)
if (activate_bufferSize+1>=activate_bufferMax) THROW_ERROR("activate list full");
activate_buffer[activate_bufferSize].Id = activate_bufferSize+1;
activate_buffer[activate_bufferSize].StoredKey = key;
activate_bufferSize++;
TRY(activate_saveToEEProm());
NO_ERROR();
我的解决方案如下:
#ifndef ERROR_H_
#define ERROR_H_
typedef enum
Message_None,
Message_Error,
Message_Warning,
Message_Info
EMessage_type;
typedef struct
EMessage_type message_type;
char* message;
const char* file;
uint32_t line;
EErrorStruct,*EError;
#define THROW_ERROR(message, ...) EError __err=GET_MESSAGE(Message_Error,(char*)__FILE__,__LINE__,message,##__VA_ARGS__); SEND_MESSAGE(__err); return __err;
#define TRY(__x) EError __err = __x; if (__err->message_type==Message_Error) return __err;
#define TRYHAL(__x) HAL_StatusTypeDef __res = __x; if (__res != HAL_OK) THROW_ERROR("HAL problem: %u",__res);
#define TRYFAT(__x) FRESULT __res = __x; if (__res != FR_OK) THROW_ERROR("FAT problem: %u",__res);
#define NO_ERROR() return GET_MESSAGE(Message_None,NULL,0,NULL,0);
#define SEND_ERROR(message , ...) SEND_MESSAGE(GET_MESSAGE(Message_Error,(char*)__FILE__,__LINE__,message,##__VA_ARGS__));
#define SEND_WARN(message , ...) SEND_MESSAGE(GET_MESSAGE(Message_Warning,(char*)__FILE__,__LINE__,message,##__VA_ARGS__));
#define SEND_INFO(message , ...) SEND_MESSAGE(GET_MESSAGE(Message_Info,(char*)__FILE__,__LINE__,message,##__VA_ARGS__));
EError GET_MESSAGE(EMessage_type messagetype,const char* file, uint32_t line,const char *format, ... );
void SEND_MESSAGE(EError err);
uint8_t ISFAILED(EError err);
#endif /* ERROR_H_ */
源文件
#include <error.h>
#include "stdio.h"
#include "stdarg.h"
static EErrorStruct error;
static const EError m = &error;
static char buffer[300];
EError GET_MESSAGE(EMessage_type messagetype,const char* file, uint32_t line,const char *format, ... )
va_list args;
va_start(args,format);
vsprintf(buffer, format, args);
va_end(args);
m->message = buffer;
m->message_type = messagetype;
m->file = file;
m->line = line;
return m;
void SEND_MESSAGE(EError err)
switch (err->message_type)
case Message_Error:
printf("ERRO: %s \r\n\t\t\t\t\t\t\t\t\t\t at %s line: %u\r\n",err->message,err->file,(unsigned int)err->line);
break;
case Message_Warning:
printf("WARN: %s \r\n\t\t\t\t\t\t\t\t\t\t at %s line: %u\r\n",err->message,err->file,(unsigned int)err->line);
break;
case Message_Info:
printf("INFO: %s \r\n\t\t\t\t\t\t\t\t\t\t at %s line: %u\r\n",err->message,err->file,(unsigned int)err->line);
break;
default:
break;
uint8_t ISFAILED(EError err)
if (err->message_type == Message_Error) return 1;
return 0;
【问题讨论】:
更多好运codereview.stackexchange.com/ 这对您想要报告错误的函数非常具有侵入性,因为您的方法指定了返回类型(作为指针),需要调用者检查该指针。它也不适用于多线程代码(C11 及更高版本),因为它使用不受保护的静态。如果你真的想使用异常之类的东西来报告错误,可能最好使用 C++,因为解决方案会更简单,对用户代码的干扰也更少。 我有点怀疑这在实践中是否真的有效。我看不出你如何在不使用setjmp/longjmp
(你似乎没有使用)的情况下真正模拟try
的功能。无论如何,如果你能让它工作,那么它就不是惯用的 C。最好使用 C 习惯用法进行错误处理(验证输入、返回错误代码)或遵循@Peter 的想法并使用 C++。
感谢您的 cmets。 @JohnColeman:确实没有解决方案。但是错误可能会在调用堆栈上冒泡,直到它被处理。多线程也不见了。
【参考方案1】:
以下都被广泛认为是不好的做法:
类似函数的宏。 可变参数宏。 可变函数。 通过宏发明您自己的语言功能。 使用非标准语言功能,例如##__VA_ARGS__
。
这些做法不好的原因有很多:不存在类型安全、很难阅读、难以调试/维护、可移植性等等。
因此,将以上所有内容组合到一个程序中是一个非常糟糕的想法。
C 中的错误处理是通过事实上的行业标准处理的,基本上是这样的:
错误代码是通过枚举定义的。 任何 API 函数的返回值都是为错误代码保留的。 发生错误时,函数会返回错误代码。 编译器设置为警告级别,如果您忽略函数的结果,则会收到警告。 源代码文档说明了其他参数在成功和错误时会发生什么/不会发生什么。这是其他 C 程序员期望并立即理解的。他们不理解或期望一些自制的异常处理系统。
此外,在支持异常处理的语言中存在很多批评。见Why is exception handling bad?
【讨论】:
我确信 C 编程有几个行业标准,但我不知道哪一个是事实上的。 @IanAbbott 几乎所有专业 API 和软件堆栈都是这样编写的。 这在开源世界中似乎并不常见。至少我想不出任何这样的工作! 非常有用的解释。带有返回值的宏之类的函数给了我编译器警告,它们很讨厌。无论如何,我必须结合 FILE LINE 常量,它在调试时非常有用。我想我会使用很多库都使用的枚举错误代码样式。以上是关于C 错误处理异常,如的主要内容,如果未能解决你的问题,请参考以下文章