1 votes

Définition des macros de fonctions

Gcc 4.4.1 C99

J'ai écrit une fonction que j'utiliserai pour afficher des messages à l'utilisateur. Cette fonction fonctionne correctement, et j'ai besoin qu'elle soit évolutive car j'aurai d'autres codes d'erreur à ajouter plus tard.

Toutefois, les entrées seront la priorité, le code d'erreur et une brève description de l'erreur. J'ai inclus la fonction et le numéro de ligne.

Cependant, je souhaite intégrer l'appel de la fonction dans une macro, mais je ne sais pas exactement comment m'y prendre ?

Quelqu'un peut-il m'indiquer la marche à suivre pour écrire ?

Merci de me faire part de vos suggestions,

#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);
}

1voto

Steve Jessop Points 166970

L'appelant a-t-il réellement besoin d'un nombre variable d'arguments ? Par exemple, voulez-vous qu'il puisse faire quelque chose comme ceci ?

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

Je le suppose, car si ce n'est pas le cas, il n'y a pas besoin de varargs du tout.

Partant de ce principe, je pense que je ferais d'abord ceci :

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

Pero Si vous faites cela, vous comptez sur l'appelant pour incorporer correctement la fonction et la ligne dans le message d'erreur. C'est une source de tracas pour l'appelant. En fait, j'opterais plutôt pour quelque chose comme ceci :

#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);
}

L'appelant fait ensuite ceci :

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

L'erreur se présente comme suit :

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

Dernière remarque : si ces codes de priorité et d'erreur ne sont utilisés qu'avec cette fonction, il serait préférable de les #définir en tant que chaînes de caractères et de modifier les paramètres correspondants en const char* . De cette façon, vous n'avez pas besoin de l'instruction switch et vous pouvez ajouter de nouveaux codes sans effort (ou les appelants peuvent simplement spécifier rapidement une autre chaîne, s'ils veulent un accroche-regard pendant le débogage).

Même s'il doit s'agir de chiffres, vos commutateurs contiennent un formatage inutile. Vous pourriez faire quelque chose comme ceci :

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

ou ceci :

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];
}

Cela fonctionne tant que vous savez que les priorités sont des nombres consécutifs à partir de 1, et tant que vous vous assurez que l'énumération et les chaînes de caractères sont synchronisées. C'est donc un peu plus difficile à modifier que la grande instruction switch, mais un peu plus facile à lire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X