Определение функциональных макросов - PullRequest
1 голос
/ 16 ноября 2009

gcc 4.4.1 C99

Я написал функцию, которую буду использовать для отображения сообщений пользователю. Это работает нормально, и мне нужно, чтобы оно было масштабируемым, так как у меня будет больше кодов ошибок, чтобы добавить позже.

Однако входными данными будут приоритет, код ошибки и краткое описание ошибки. Я включил функцию и номер строки.

Однако я хочу обернуть вызов функции в макрос, но не знаю, как это сделать?

Может кто-нибудь указать мне направление записи?

Большое спасибо за любые предложения,

#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 ]

4 голосов
/ 16 ноября 2009

C99 поддерживает макросы vararg , которые, кажется, вы могли бы использовать, чтобы сделать его немного более удобным:

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

На самом деле, похоже, это не экономит столько времени, сколько просто вызов функции. Возможно, вам следует переопределить макрос REPORT, чтобы включить приоритет, или что-то еще?

1 голос
/ 16 ноября 2009

Нужно ли вызывающей стороне на самом деле переменное количество аргументов? Например, вы хотите, чтобы они могли делать что-то подобное?

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 ]

Последнее замечание: если эти коды приоритетов и ошибок используются только с этой функцией, то, возможно, лучше сначала определить их как строки, и изменить соответствующие параметры на 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, и до тех пор, пока вы убедитесь, что перечисление и строки синхронизированы. Так что это немного сложнее изменить, чем большой оператор switch, но IMO немного легче читать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...