这是宏观滥用吗?

Jas*_*ick 12 c macros file-io coding-style

我正在对一些代码进行逆向工程并遇到了这个......

/************************************************************************/
/*                                                                      */
/*                          MACRO CHECK_FREAD                           */
/*                                                                      */
/*  CHECK_FREAD is used to check the status of a file read.  It         */
/*   is passed the return code from read and a string to print out if   */
/*   an error is detected.  If an error is found, an error message is   */
/*   printed out and the program terminates.  This was made into a      */
/*   macro because it had to be done over and over and over . . .       */
/*                                                                      */
/************************************************************************/

#define CHECK_FREAD(X, msg)  if (X==-1) \
                 { \
                return(DCD_BADREAD); \
                 }
Run Code Online (Sandbox Code Playgroud)

基本上,只要这些函数执行(c-read)检查结果是否有错误,该文件中的特定函数集就会调用它.他们也有一个类似的EOF检查......基本上我可以告诉他们,他们这样做是为了在一堆地方的函数中间插入错误返回.

例如

/*  Read in an 4                */
    ret_val = read(fd, &input_integer, sizeof(int32));

    CHECK_FREAD(ret_val, "reading an 4");
    CHECK_FEOF(ret_val, "reading an 4");

    if (input_integer != 4)
    {
        return(DCD_BADFORMAT);
    }

    /*  Read in the number of atoms         */
    ret_val = read(fd, &input_integer, sizeof(int32));
    *N = input_integer;

    CHECK_FREAD(ret_val, "reading number of atoms");
    CHECK_FEOF(ret_val, "reading number of atoms");
Run Code Online (Sandbox Code Playgroud)

现在我只是回到c/c ++编程,我从来没有使用过宏,但是这个宏滥用了吗?

我读过这个...... C++宏什么时候有用?

......这听起来不像任何犹太示例,所以我的猜测是"是".但我想得到一个更明智的意见,而不仅仅是做出有根据的猜测... :)

...错误,使用某种包装不是更好吗?

tda*_*ers 22

因为它不一样.如果你把同一个东西放到一个函数中,你将有一个短函数,DCD_BADWRITE如果X等于-1则返回.然而,有问题的宏从包含函数返回.例:

int foobar(int value) {
    CHECK_FREAD(value, "Whatever");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

会扩大到:

int foobar(int value) {
    if (value == -1) {
        return (DCD_BADWRITE);
    };
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果它是一个函数,外部函数(foobar)将很乐意忽略结果并返回0.

一个宏观看起来非常像一个函数,但在一个关键的方式表现不同,是一个主要的禁止IMO.我会说是的,这宏观滥用.


Mar*_*som 8

这不能作为函数完成,因为它return是从调用函数返回的,而不是宏中的块.

这是一种给宏提供错误名称的东西,因为它隐藏了一个重要的控制逻辑.


bta*_*bta 5

关于是否要称这种宏*虐待*,我已经蠢蠢欲动,但我会说这不是一个好主意.

我认为,一般来说,你应该避免return在宏中使用.很难创建正确处理返回未做调用它笨拙或难以理解的功能的控制流程的宏.你绝对不想在没有意识到的情况下"意外"从函数返回.调试可能很烦人.

此外,此宏可能会导致代码流问题,具体取决于它的使用方式.例如,代码

if (some_condition)
    CHECK_FREAD(val, "string")
else
    something_else();
Run Code Online (Sandbox Code Playgroud)

不会做你想象的事情(elseif宏内部相关联).需要封闭宏以{ }确保它不会改变任何周围的条件.

此外,不使用第二个参数.这里显而易见的问题是实现与文档不匹配.隐藏的问题是当像这样调用宏时:

char* messages[4];          // Array of output messages
char* pmsg = &messages[0];
....
CHECK_FREAD(val, pmsg++);
Run Code Online (Sandbox Code Playgroud)

您希望在调用宏之后指针移动到下一个数组元素.但是,不使用第二个参数,因此增量永远不会发生.这是带有副作用的语句导致宏出现问题的另一个原因.

而不是使用宏来完成所有这些,为什么不编写一个函数来封装read和错误检查?它可以在成功或错误代码时返回零.如果错误处理代码在所有read块之间是通用的,则可以执行以下操作:

int your_function(whatever) {
    int ret;
    ...

    if ((ret=read_helper(fd, &input_integer)) != 0)
        goto error_case;

    ...

    // Normal return from the function
    return 0;

error_case:
    print_error_message_for_code(ret);
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

只要您将所有read_helper返回值分配给它们ret,就可以在整个函数中重复使用相同的错误处理代码块.您的函数print_error_message_for_code只需将错误代码作为输入,并使用switch或数组打印出与其对应的错误消息.

我承认很多人都害怕goto,但重复使用常见的错误处理代码而不是一系列嵌套的块和条件变量可能是一个合适的用例.只需保持清洁和可读(每个功能一个标签,并保持错误处理代码简单).