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 错误处理异常,如的主要内容,如果未能解决你的问题,请参考以下文章

异常--常见处理方式,异常安全规范

C++——异常

C++-异常

C++-异常

C++-异常

C++ 异常处理