Domanda

Ho questo sistema di registrazione per il quale sto cercando di rinunciare a parte della manipolazione delle stringhe.

Il sistema di registrazione viene utilizzato tramite macro funzionali che poi inoltrano a una singola chiamata di funzione. Per esempio #define Warning(...) LogMessage(eWarning, __VA_ARGS__);.

LogMessage quindi fa un snprintf in un nuovo buffer e quindi presenta quel messaggio a qualsiasi obiettivo di registro è installato; printf, outputdebugstring, ecc.

Sfortunatamente, ho riscontrato un problema in cui il buffer che abbiamo non è abbastanza grande, quindi l'output viene troncato. Mi sono anche reso conto che questo metodo fallirà se il messaggio di output ha simboli percentuali in esso, poiché SnPrintf proverà a elaborare il VA_ARGS. Infine, poiché la maggior parte dei nostri messaggi di registro non utilizza il VA_ARGS, sembra sciocco copiare la stringa solo per presentarla ai logger.

Quindi, dato il mio prototipo di funzione, dovrei essere in grado di sovraccaricare in base alla presenza delle ellissi? In altre parole, dovrei essere in grado di supporre che posso fare qualcosa del tipo:

LogMessage(LogLevel, const char* message, ...);
LogMessage(LogLevel, const char* message);

I miei tentativi di Google non hanno prodotto nulla di particolarmente utile (solo mostrandomi che le ellissi corrisponderanno se nient'altro lo fa, variando dalle mie esigenze niente corrispondenze) e la mia pugnalata iniziale in un'implementazione mi ha dato un errore di chiamata di funzione ambiguo.

Con l'errore, dovrei solo accettare che non posso farlo, ma mi chiedo se sia solo il compilatore che sto usando o se forse sto sbagliando. Posso ottenere un effetto simile con

// edited version of what I really have to remove our local APIs,
// please excuse minor errors
const char* message = NULL;
char buffer[512];

va_list args;
va_start(args, format);

if(strcmp(format, "%s") == 0) {
    message = va_arg(args, const char*);
}
else if (strchr(format, '%') == NULL) {
    message = format;
}
else {
    vsnprintf(buffer, 512, format, args);
    message = buffer;
}

va_end(args);

... Ma questo sembra dispendioso nel caso tipico che può essere conosciuto semplicemente dal numero di parametri che vengono passati. Ad esempio, se le ellissi non corrispondono a nulla, selezionare l'altra funzione? Se questo non funziona, c'è un altro metodo che posso provare che non richiede all'utente di decidere con il nome macro quale funzione verrà chiamata? Onestamente non si tratta nemmeno di "rifiuti" una volta che mi sono reso conto che se qualcuno ha detto a casaccio Error("Buffer not 100% full"); nel loro messaggio di registro e ottenuto "buffer non 1007.732873e10ull" di conseguenza.

Modificare: Mentre il mio esempio è stato risolto da un "non farlo", si può rispondere alla domanda stessa?

È stato utile?

Soluzione

Sono stato ispirato dalla risposta originale a questa domanda, ma ho trovato un leggero miglioramento.

static void LogMessage(LogLevel level, const char* message);

template <typename T>
static void LogMessage(LogLevel level, const char* format, T t, ...)
{
    LogMessageVA(level, format, (va_list)&t);
}

static void LogMessageVA(LogLevel level, const char* format, va_list argptr);

Funziona senza dover "supporre" che il secondo argomento sia const char*.

Altri suggerimenti

Mi sono anche reso conto che questo metodo fallirà se il messaggio di output ha simboli percentuali in esso, poiché SnPrintf proverà a elaborare il VA_ARGS.

Quindi attenzione al chiamante. Se la tua funzione è documentata per assumere stringhe in formato in stile printf, è responsabilità del chiamante sfuggire a qualsiasi percentuale di segni. Non è proprio il tuo compito tentare di gestire stringhe di formato non valido.

Onestamente non si tratta nemmeno di "rifiuti" una volta che mi sono reso conto che se qualcuno ha detto a casaccio Error("Buffer not 100% full"); nel loro messaggio di registro e ottenuto "buffer non 1007.732873e10ull" di conseguenza.

Penso che tu stia meglio andare con l'etica C ++. Nei metodi Java comunemente verificare gli argomenti validi e lanciare eccezioni quando passate valori non validi. In C ++ ti lasci semplicemente sparare ai chiamanti al piede. È meglio che li scrivano 100%% piuttosto che saltare attraverso i cerchi per proteggerli dall'apprendimento di come chiamare correttamente la tua funzione.

In C ++ 11 è possibile utilizzare modelli variadici con una specializzazione esplicita per il caso dell'argomento singolo:

void bar(int a, ...) {
  // va_list stuff
}

template <typename... T>
void foo(int a, T... args) { // (1)
  bar(a, args...); // or do all the vararg stuff here directly
}

template <>
void foo(int a) {            // (2)
  printf("single\n");
}

Quindi:

//foo();    // compile error, as expected
foo(1);     // uses (2)
foo(2,1);   // uses (1)
foo(3,1,"asdf"); // uses (1)
...

Ok, penso di aver trovato una soluzione per la domanda.

Il fatto è che tu Non posso sovraccarico basato solo sul fatto che ci siano parametri per le ellissi o meno. Cioè non puoi avere funzioni che hanno firme che variano solo alla presenza delle ellissi.

Tuttavia, esso è possibile fare qualcosa di simile a quello che stavo chiedendo se lascio cadere il const char* Parametro dal prototipo Ellispeses. Cioè

LogMessage(LogLevel, ...);
LogMessage(LogLevel, const char* message);

è inequivocabile, ma ora combatti con il fatto che devi presumere che il primo parametro sia un const char*, ma potrebbe non esserlo. Prendendo il consiglio di John Kugelman, forse va bene; Documi i parametri consentiti e l'attenzione dell'utente. La funzione non ellipses verrà chiamata se c'è solo un const char*, e la funzione Ellispes verrà chiamata se c'è nessun altro incluso il documentato const char* seguito da un certo numero di parametri.

Sfortunatamente sembra che questa sia l'estensione di una possibile soluzione che ti consente di trasmettere le funzioni VA_ARGS alle funzioni figlio, nel mio caso di esempio a vsnprintf.

Probabilmente è una cattiva forma accettare la mia risposta, anche se è quello che risponde alla domanda presentata.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top