Pergunta

Digamos que eu tenha uma função C que receba um número variável de argumentos:Como posso chamar outra função que espera um número variável de argumentos de dentro dela, passando todos os argumentos que entraram na primeira função?

Exemplo:

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);
 }
Foi útil?

Solução

Para passar as elipses, você deve convertê -las em uma lista VA_LIST e usar esse VA_LIST na sua segunda função. Especificamente;

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

Outras dicas

Não há como chamar (por exemplo) printf sem saber quantos argumentos você está passando, a menos que você queira entrar em truques travessos e não portáveis.

A solução geralmente usada é sempre fornecer uma forma alternativa de funções vararg, então printf tem vprintf que leva um va_list no lugar do .... o ... versões são apenas embalagens ao redor do va_list versões.

Funções variádicas pode ser perigoso. Aqui está um truque mais seguro:

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

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

Em magníficos C ++ 0x, você pode usar modelos variádicos:

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

Você pode usar montagem embutida para a chamada de função. (Neste código, presumo que os argumentos sejam caracteres).

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

Você pode experimentar o Macro também.

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

Embora você possa resolver passar o formatador, armazenando -o primeiro no buffer local, mas isso precisa da pilha e, em algum momento, pode ser um problema para lidar. Eu tentei seguir e parece funcionar bem.

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

Espero que isto ajude.

A solução de Ross limpou um pouco. Funciona apenas se todos os args forem indicadores. Também a implementação do idioma deve apoiar a eliminação de vírgula anterior se __VA_ARGS__ está vazio (tanto o Visual Studio C ++ quanto o GCC).

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

Digamos que você tenha uma função variada típica que você escreveu. Porque pelo menos um argumento é necessário antes do variável ..., você deve sempre escrever um argumento extra em uso.

Ou você?

Se você envolver sua função variádica em uma macro, não precisará de arg anterior. Considere este exemplo:

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

Obviamente, isso é muito mais conveniente, pois você não precisa especificar o argumento inicial sempre.

Não tenho certeza se isso funciona para todos os compiladores, mas funcionou até agora para mim.

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

Você pode adicionar o...para inner_func() se quiser, mas não precisa disso.Funciona porque va_start usa o endereço da variável fornecida como ponto inicial.Neste caso, estamos dando uma referência a uma variável em func().Então ele usa esse endereço e lê as variáveis ​​depois disso na pilha.A função inner_func() está lendo o endereço da pilha de func().Portanto, só funciona se ambas as funções usarem o mesmo segmento de pilha.

As macros va_start e va_arg geralmente funcionarão se você fornecer a elas qualquer var como ponto de partida.Então, se quiser, você pode passar ponteiros para outras funções e usá-las também.Você pode criar suas próprias macros com bastante facilidade.Tudo o que as macros fazem é digitar endereços de memória.No entanto, fazê-los funcionar para todos os compiladores e convenções de chamada é irritante.Portanto, geralmente é mais fácil usar aqueles que acompanham o compilador.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top