Przekazywanie zmiennej liczby argumentów wokół

Powiedzmy, że mam funkcję C, która przyjmuje zmienną liczbę argumentów: Jak mogę wywołać inną funkcję, która oczekuje zmiennej liczby argumentów z wewnątrz niej, przekazując wszystkie argumenty, które dostały się do pierwszej funkcji?

Przykład:

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);
 }
Author: Yakk - Adam Nevraumont, 2008-10-15

10 answers

Aby przekazać elipsy dalej, musisz przekonwertować je na va_list i użyć tej va_list w swojej drugiej funkcji. Konkretnie;

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);
}
 190
Author: Shane MacLaughlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-09 19:28:19

Nie ma sposobu na wywołanie (np) printf bez wiedzy, ile argumentów PRZEKAZUJESZ do niego, chyba że chcesz dostać się do niegrzecznych i nie przenośnych sztuczek.

Powszechnie stosowanym rozwiązaniem jest zawsze dostarczenie alternatywnej formy funkcji vararg, więc printf mA vprintf, która przyjmuje va_list w miejsce .... Wersje ... są tylko owijkami wokół wersji va_list.

 52
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 17:11:50

Funkcje Zmienne mogą być dangerous. Oto bezpieczniejsza sztuczka:

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

func((type[]){val1,val2,val3,val4,0});
 51
Author: Rose Perrone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:43

W C++0x można użyć szablonów wariacyjnych:

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...);
}
 28
Author: user2023370,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-24 22:14:54

Możesz użyć wbudowanego zestawu do wywołania funkcji. (w tym kodzie zakładam, że argumenty są znakami).

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);
    }
 6
Author: Yoda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-26 19:40:49

Możesz również spróbować makro.

#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;
}
 2
Author: JagsVG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-19 14:28:28

Chociaż możesz rozwiązać przekazywanie formatera, przechowując go najpierw w lokalnym buforze, ale wymaga to stosu i może być czasem problemem. Próbowałem śledzić i wydaje się, że działa dobrze.

#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");
}
Mam nadzieję, że to pomoże.
 2
Author: VarunG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-04 09:22:53

Roztwór Rossa trochę wyczyścił. Działa tylko wtedy, gdy wszystkie args są wskaźnikami. Również implementacja języka musi wspierać pomijanie poprzedniego przecinka, Jeśli {[1] } jest puste (zarówno Visual Studio C++, jak i 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);}
 1
Author: BSalita,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-08 09:02:36

Załóżmy, że masz typową zmienną funkcję, którą napisałeś. Ponieważ co najmniej jeden argument jest wymagany przed zmienną ..., musisz zawsze napisać dodatkowy argument w usage.

A może ty?

Jeśli zawijasz zmienną funkcję w makro, nie potrzebujesz poprzedzającego arg. Rozważ ten przykład:

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

Jest to oczywiście o wiele wygodniejsze, ponieważ nie musisz za każdym razem podawać początkowego argumentu.

 -1
Author: Arcane Engineer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-28 11:42:46

Nie jestem pewien, czy to działa dla wszystkich kompilatorów, ale jak dotąd działało dla mnie.

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

Możesz dodać ... do inner_func (), jeśli chcesz, ale nie potrzebujesz go. Działa, ponieważ va_start używa adresu podanej zmiennej jako punktu początkowego. W tym przypadku dajemy mu odniesienie do zmiennej w func(). Więc używa tego adresu i odczytuje zmienne po tym na stosie. Funkcja inner_func () odczytuje z adresu stosu func (). Więc działa tylko wtedy, gdy obie funkcje używają tego samego segmentu stosu.

Makra va_start i va_arg będą na ogół działać, jeśli podasz im dowolny var jako punkt wyjścia. Więc jeśli chcesz, możesz przekazać wskaźniki do innych funkcji i korzystać z nich zbyt. Możesz łatwo tworzyć własne makra. Wszystkie makra robią to wpisując adresy pamięci. Jednak zmuszanie ich do pracy dla wszystkich kompilatorów i wywoływania konwencji jest irytujące. Więc generalnie łatwiej jest używać tych, które są dostarczane wraz z kompilatorem.

 -4
Author: Jim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-08 21:30:33