Question

Disons que j'ai une fonction C qui prend un nombre variable d'arguments: comment puis-je appeler une autre fonction qui attend un nombre variable d'arguments de l'intérieur, en passant tous les arguments contenus dans la première fonction?

Exemple:

void format_string(char *fmt, ...);

void debug_print(int dbg_lvl, char *fmt, ...) {
    format_string(fmt, /* how do I pass all the arguments from '...'? */);
    fprintf(stdout, fmt);
 }
Était-ce utile?

La solution

Pour transmettre les points de suspension, vous devez les convertir en une liste va_list et utiliser cette liste va_list dans votre deuxième fonction. Plus précisément;

void format_string(char *fmt,va_list argptr, char *formatted_string);


void debug_print(int dbg_lvl, char *fmt, ...) 
{    
 char formatted_string[MAX_FMT_SIZE];

 va_list argptr;
 va_start(argptr,fmt);
 format_string(fmt, argptr, formatted_string);
 va_end(argptr);
 fprintf(stdout, "%s",formatted_string);
}

Autres conseils

Il n’est pas possible d’appeler printf, par exemple, sans connaître le nombre d’arguments que vous lui transmettez, à moins que vous ne vouliez vous lancer dans des trucs coquins et non portables.

La solution généralement utilisée est de toujours fournir une forme alternative de fonctions vararg, donc printf a vprintf qui prend une va_list à la place de le ... . Les versions ... ne sont que des wrappers autour des versions va_list .

Fonctions variées être dangereux . Voici un truc plus sûr:

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});

Dans un magnifique C ++ 0x, vous pouvez utiliser des modèles variadiques:

template <typename ... Ts>
void format_string(char *fmt, Ts ... ts) {}

template <typename ... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts ... ts)
{
  format_string(fmt, ts...);
}

Vous pouvez utiliser un assemblage en ligne pour l'appel de fonction. (dans ce code, je suppose que les arguments sont des caractères).

void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
    {
        va_list argumentsToPass;
        va_start(argumentsToPass, fmt);
        char *list = new char[numOfArgs];
        for(int n = 0; n < numOfArgs; n++)
            list[n] = va_arg(argumentsToPass, char);
        va_end(argumentsToPass);
        for(int n = numOfArgs - 1; n >= 0; n--)
        {
            char next;
            next = list[n];
            __asm push next;
        }
        __asm push fmt;
        __asm call format_string;
        fprintf(stdout, fmt);
    }

Vous pouvez également essayer la macro.

#define NONE    0x00
#define DBG     0x1F
#define INFO    0x0F
#define ERR     0x07
#define EMR     0x03
#define CRIT    0x01

#define DEBUG_LEVEL ERR

#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...)  fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...)  if((DEBUG_LEVEL & X) == X) \
                                      DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)

int main()
{
    int x=10;
    DEBUG_PRINT(DBG, "i am x %d\n", x);
    return 0;
}

Bien que vous puissiez résoudre le problème en passant le formateur en le stockant d'abord dans le tampon local, cela nécessite une pile et peut parfois être problématique à traiter. J'ai essayé de suivre et cela semble bien fonctionner.

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

void print(char const* fmt, ...)
{
    va_list arg;
    va_start(arg, fmt);
    vprintf(fmt, arg);
    va_end(arg);
}

void printFormatted(char const* fmt, va_list arg)
{
    vprintf(fmt, arg);
}

void showLog(int mdl, char const* type, ...)
{
    print("\nMDL: %d, TYPE: %s", mdl, type);

    va_list arg;
    va_start(arg, type);
    char const* fmt = va_arg(arg, char const*);
    printFormatted(fmt, arg);
    va_end(arg);
}

int main() 
{
    int x = 3, y = 6;
    showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
    showLog(1, "ERR");
}

J'espère que cela vous aidera.

La solution de Ross est un peu nettoyée. Ne fonctionne que si tous les arguments sont des pointeurs. L'implémentation de langage doit également permettre d'éliminer la virgule précédente si __ VA_ARGS __ est vide (Visual Studio C ++ et GCC le font).

// pass number of arguments version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}


// NULL terminated array version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}

Supposons que vous ayez écrit une fonction variadique typique. Étant donné qu’au moins un argument est requis avant le variadic ... , vous devez toujours écrire un argument supplémentaire lors de l’utilisation.

Ou le faites-vous?

Si vous enveloppez votre fonction variadique dans une macro, vous n'avez pas besoin de l'argument précédent. Considérez cet exemple:

#define LOGI(...)
    ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))

Ceci est évidemment beaucoup plus pratique, car vous n'avez pas besoin de spécifier l'argument initial à chaque fois.

Je ne sais pas si cela fonctionne pour tous les compilateurs, mais cela a fonctionné jusqu'à présent pour moi.

void inner_func(int &i)
{
  va_list vars;
  va_start(vars, i);
  int j = va_arg(vars);
  va_end(vars); // Generally useless, but should be included.
}

void func(int i, ...)
{
  inner_func(i);
}

Vous pouvez ajouter ... à inner_func () si vous le souhaitez, mais vous n'en avez pas besoin. Cela fonctionne parce que va_start utilise l'adresse de la variable donnée comme point de départ. Dans ce cas, nous lui donnons une référence à une variable dans func (). Donc, il utilise cette adresse et lit ensuite les variables sur la pile. La fonction inner_func () lit l’adresse de pile de func (). Cela ne fonctionne donc que si les deux fonctions utilisent le même segment de pile.

Les macros va_start et va_arg fonctionneront généralement si vous leur donnez une variable var comme point de départ. Donc, si vous le souhaitez, vous pouvez passer des pointeurs sur d’autres fonctions et les utiliser également. Vous pouvez créer vos propres macros assez facilement. Toutes les macros sont des adresses de mémoire transtypées. Cependant, les faire fonctionner pour tous les compilateurs et les conventions d’appel est agaçant. Il est donc généralement plus facile d’utiliser ceux fournis avec le compilateur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top