Jak przekazać zmienną liczbę argumentów do printf/sprintf

Mam klasę, która posiada funkcję" error", która sformatuje jakiś tekst. Chcę zaakceptować zmienną liczbę argumentów, a następnie sformatować je za pomocą printf.

Przykład:

class MyClass
{
public:
    void Error(const char* format, ...);
};

Metoda błędów powinna przyjąć parametry, wywołać printf / sprintf, aby sformatować je, a następnie coś z nimi zrobić. Nie chcę sam pisać całego formatowania, więc warto spróbować i dowiedzieć się, jak korzystać z istniejącego formatowania.

Author: hippietrail, 2009-06-29

7 answers

Bad

void Error(const char* format, ...)
{
    char dest[1024 * 16];
    va_list argptr;
    va_start(argptr, format);
    vsprintf(dest, format, argptr);
    va_end(argptr);
    printf(dest);
}

Ten kod nie jest taki dobry. Używa bufora znaków o stałej wielkości, co może prowadzić do błędu przekroczenia bufora, jeśli łańcuch znaków jest patologicznie długi. Dowolny rozmiar {[2] } powinien wywołać flagę w twojej głowie. Ponadto, wywołanie printf może napotkać problemy, Jeśli dest zakończy się zawierając kody formatujące. Lepiej byłoby printf("%s", dest). Ale jeszcze lepiej byłoby użyć vprintf lub vfprintf:

Dobry

void Error(const char* format, ...)
{
    va_list argptr;
    va_start(argptr, format);
    vfprintf(stderr, format, argptr);
    va_end(argptr);
}

Jeśli chcesz manipulować ciągiem zanim go wyświetlisz i naprawdę potrzebujesz go najpierw przechowywać w buforze, proszę, użyj vsnprintf zamiast vsprintf. vsnprintf zapobiegnie przypadkowemu przepełnieniu bufora.

 125
Author: John Kugelman,
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-08-28 17:41:24

Spójrz na vsnprintf jak to zrobi co chcesz http://www.cplusplus.com/reference/clibrary/cstdio/vsprintf/

Będziesz musiał najpierw uruchomić ARG array va_list, a następnie ją wywołać.

Przykład z tego linku: / * przykład vsprintf * /

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

void Error (char * format, ...)
{
  char buffer[256];
  va_list args;
  va_start (args, format);
  vsnprintf (buffer, 255, format, args);


  //do something with the error

  va_end (args);
}
 30
Author: Lodle,
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
2009-06-29 04:36:33

Używanie funkcji z elipsami nie jest zbyt bezpieczne. Jeśli wydajność nie jest krytyczna dla funkcji log, rozważ użycie przeciążenia operatora jak w formacie boost::. Mógłbyś napisać coś takiego:

#include <sstream>
#include <boost/format.hpp>
#include <iostream>
using namespace std;

class formatted_log_t {
public:
    formatted_log_t(const char* msg ) : fmt(msg) {}
    ~formatted_log_t() { cout << fmt << endl; }

    template <typename T>
    formatted_log_t& operator %(T value) {
        fmt % value;
        return *this;
    }

protected:
    boost::format                fmt;
};

formatted_log_t log(const char* msg) { return formatted_log_t( msg ); }

// use
int main ()
{
    log("hello %s in %d-th time") % "world" % 10000000;
    return 0;
}

Poniższa próbka pokazuje możliwe błędy z elipsami:

int x = SOME_VALUE;
double y = SOME_MORE_VALUE;
printf( "some var = %f, other one %f", y, x ); // no errors at compile time, but error at runtime. compiler do not know types you wanted
log( "some var = %f, other one %f" ) % y % x; // no errors. %f only for compatibility. you could write %1% instead.
 5
Author: Kirill V. Lyadvinsky,
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
2009-06-29 07:44:57

Powinienem przeczytać więcej na temat istniejących pytań w stack overflow.

C++ przekazywanie zmiennej liczby argumentów jest podobnym pytaniem. Mike F ma następujące wyjaśnienie:

Nie ma możliwości wywołania (np) printf nie wiedząc ile argumentów przechodzisz do niego, chyba że chcesz aby dostać się do niegrzecznych i nie przenośnych sztuczki.

Powszechnie stosowanym rozwiązaniem jest zawsze podaj alternatywną formę funkcje vararg, czyli printf ma vprintf, który pobiera va_list z....... The ... wersje są tylko otacza wersje va_list.

Właśnie tego szukałem. Wykonałem taką implementację testową:
void Error(const char* format, ...)
{
    char dest[1024 * 16];
    va_list argptr;
    va_start(argptr, format);
    vsprintf(dest, format, argptr);
    va_end(argptr);
    printf(dest);
}
 3
Author: user5722,
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:37

Szukasz funkcji wariacyjnych . printf () i sprintf() są funkcjami zmiennymi - mogą przyjmować zmienną liczbę argumentów.

Wiąże się to zasadniczo z następującymi krokami:

  1. Pierwszy parametr musi wskazywać liczbę następujących parametrów. Tak więc w printf () parametr "format" podaje to wskazanie - jeśli masz 5 specyfikatorów formatu, to będzie szukał jeszcze 5 argumentów (w sumie 6 argumentów.) Pierwszy argument może być liczbą całkowitą(np. "mojfunction (3, a, b, c)" gdzie "3" oznacza "3 argumenty)

  2. Następnie wykonaj pętlę i pobieraj każdy kolejny argument, używając va_start () itd. funkcje.

Jest mnóstwo tutoriali jak to zrobić-powodzenia!

 3
Author: poundifdef,
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
2009-06-29 03:17:13

Prosty przykład poniżej. Uwaga powinieneś przejść do większego bufora i przetestować, czy bufor był wystarczająco duży, czy nie

void Log(LPCWSTR pFormat, ...) 
{
    va_list pArg;
    va_start(pArg, pFormat);
    char buf[1000];
    int len = _vsntprintf(buf, 1000, pFormat, pArg);
    va_end(pArg);
    //do something with buf
}
 2
Author: DougN,
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
2009-06-29 03:17:12

Spójrz na przykład http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg / , przekazują liczbę argumentów do metody, ale można to ommit i odpowiednio zmodyfikować kod(patrz przykład).

 0
Author: stefanB,
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
2009-06-29 03:16:28