定义函数宏

时间:2009-11-16 14:59:21

标签: c

gcc 4.4.1 C99

我编写了一个函数,用于向用户显示消息。这项工作正常,我将需要它可扩展,因为我将有更多错误代码稍后添加。

但是,输入将是优先级,error_code和错误的简短描述。我已经包含了该功能和行号。

但是,我想将函数调用包装成一个宏,但不知道如何去做呢?

有人能指出我的写作方向吗?

非常感谢任何建议,

#include <stdio.h>
#include <stdarg.h>

typedef enum
{
    ST_BIND_ERR = 1,
    ST_SOCK_ERR,
    ST_CONNECT_ERR,
    ST_ACCEPT_ERR
}error_codes;

typedef enum
{
    ST_CRITICAL = 1,
    ST_WARNING,
    ST_DEBUG,
    ST_INFO
}priority;

#define REPORT(prio, err, msg) /* Defining macro here */

void report_msg(int prio, int err, const char *fmt, ...);

int main(void)
{
    printf("=== Starting program ===\n");

    report_msg(ST_WARNING, ST_CONNECT_ERR, "Error trying to make connection : FUNCTION [ %s ] : LINE [ %d ]", 
        __func__, __LINE__);

    return 0;
}

void report_msg(int prio, int err, const char *fmt, ...)
{
    va_list ap;
    char priority_msg[512] = {0};
    char error_code[256]   = {0};
    char format[256]       = {0};
    char output_msg[256]   = {0};

    switch(prio)
    {
    case ST_CRITICAL:
        sprintf(priority_msg, "[ ST_CRITICAL ]");
        break;

    case ST_WARNING:
        sprintf(priority_msg, "[ ST_WARNING ]");
        break;

    case ST_DEBUG:
        sprintf(priority_msg, "[ ST_DEBUG ]");
        break;

    case ST_INFO:
        sprintf(priority_msg, "[ ST_INFO ]");
        break;

    default:
        sprintf(priority_msg, "[ UNKNOWN_PRIO ]");
        break;
    }

    switch(err)
    {
    case ST_BIND_ERR:
        sprintf(error_code, "[ ST_BIND_ERR ]");
        break;

    case ST_SOCK_ERR:
        sprintf(error_code, "[ ST_SOCK_ERR ]");
        break;

    case ST_CONNECT_ERR:
        sprintf(error_code, "[ ST_CONNECT_ERR ]");
        break;

    case ST_ACCEPT_ERR:
        sprintf(error_code, "[ ST_ACCEPT_ERR ]");
        break;

    default:
        sprintf(error_code, "[ UNKNOWN_ERR ]");
        break;
    }

    va_start(ap, fmt);
    vsprintf(format, fmt, ap);
    va_end(ap);

    sprintf(output_msg,"%s %s %s", priority_msg, error_code, format);

    fprintf(stderr, output_msg);
}

2 个答案:

答案 0 :(得分:4)

C99支持vararg macros,它似乎可以用它来使它更方便:

#define REPORT(prio, err, format, ...) report_msg(prio, err, format, __VA_ARGS__)

但实际上,这似乎并没有为调用该函数而节省太多工作。也许你应该重新定义REPORT宏来包含优先级,还是什么?

答案 1 :(得分:1)

调用者是否确实需要可变数量的参数?例如,您是否希望他们能够做到这样的事情?

REPORT(ST_WARNING, ST_CONNECT_ERR, "Error trying to make connection : "
    "FUNCTION [ %s ] : LINE [ %d ], target address [ %s]", target);

我会假设,因为如果没有,那么根本不需要varargs。

所以,基于这个假设,我想我会先这样做:

#define REPORT(prio, error, format, ...) report_msg(prio, error, format, __func__, __LINE__, __VA_ARGS__)

但是,如果你这样做,那么你依靠调用者正确地将函数和行合并到错误消息中。这对来电者来说很麻烦。所以实际上我可能会选择更像这样的东西:

#define REPORT(prio, error, format, ...) report_msg(prio, error, format, __func__, __LINE__, __VA_ARGS__)

void report_msg(int prio, int err, const char *fmt, const char *func, int line, ...) {
    // print the prio and error codes
    // ...

    // I've put some fprintf in here to avoid introducing even more buffers,
    // but you can still do what you were doing before, building one big message.
    fprintf("in function %s at line %d\n", func, line);
    va_start(ap, fmt);
    vsprintf(format, fmt, ap);
    va_end(ap);
    fprintf("\t%s\n", format);
}

然后来电者这样做:

REPORT(ST_WARNING, ST_CONNECT_ERROR, "target address [ %s ]", my_addr);

错误看起来像这样:

[ST_WARNING] [ST_CONNECT_ERROR] in function main at line 28
    target address [ 69.59.196.211 ]

最后注意事项:如果这些优先级和错误代码仅用于此函数,那么最好将#define它们作为字符串放在首位,并将相应的参数更改为const char*。这样你就不需要switch语句了,你可以毫不费力地添加新的代码(或者调用者可以快速指定其他字符串,如果他们在调试时需要引人注目的话)。

即使它们必须是数字,您的开关也包含不必要的格式。你可以这样做:

char *priority_msg;
switch(prio) {
    case ST_WARNING: priority_msg = "[ ST_WARNING ]"; break;
    // other cases...
    default: priority_msg = "[ UNKNOWN_PRIO ]"; break;
}

或者这个:

char *priorities[] = {"[ UNKOWN_PRIO ]", "[ ST_ERROR ]", "[ ST_WARNING ]", ... };
char *priority_msg = priorities[0];
if (prio >= 0) && (prio < sizeof(priorities) / sizeof(*priorities)) {
    priority_msg = priorities[prio];
}

只要您知道优先级是从1开始的连续数字,只要您确保枚举和字符串保持同步,这就可以正常工作。因此,修改比大转换语句更难,但IMO更容易阅读。