2013-08-22 8 views
15

Ad esempio, questa non riesce a compilare:Perché la funzione std :: function non può essere associata a funzioni Variadic in stile C?

std::function<decltype(printf)> my_printf(printf);

con GCC, il messaggio di errore si legge:

error: variable 'std::function<int(const char*, ...)> my_printf' has initializer but incomplete type 
    std::function<decltype(printf)> my_printf(printf); 

In un primo momento ho pensato che questo fosse un bug in gcc, ma poi ho guardato lo standard e sembra che questo non sia supportato. Qual è la ragione tecnica per questo?

+1

Potrei scrivere una classe equivalente a 'std :: function' in C++ 11: come è,' std :: function' può essere scritta come una classe file di intestazione puro un modo conforme standard. Non sono sicuro di poter scrivere 'std :: function ' senza estendere la lingua o usando estensioni specifiche del compilatore. Possiamo avere metodi variardici 'virtuali' in stile C? E anche in questo caso, l'oggetto di cancellazione di tipo interno dovrebbe quindi inoltrare gli argomenti '...' a 'printf', che non è supportato AFAIK. – Yakk

+0

@Yakk: FYI, puoi avere un metodo varadico in stile C 'virtuale'. Il problema è che non è possibile passare i parametri varadici in stile C a un'altra funzione senza convertirli in una va_list o analizzarli da soli. –

risposta

7

Il problema è di implementazione. Diciamo che era possibile. Poi std::function avrebbe dichiarare (nel caso di printf)

int operator()(char* fmt, ...)

Quando viene chiamato, avrebbe poi dovuto passare il contenuto della ... a qualsiasi oggetto che è stato assegnato. Il problema è che non conosce abbastanza gli argomenti per sapere COME passarlo, che è un problema. printf() analizza il formato, ma altri usano altri meccanismi (un valore 'end' è popolare).

Per la famiglia di funzioni printf, suggerisco di guardare le versioni vXXX (ad esempio vprintf). Poiché utilizzano argomenti ben definiti (l'ultimo è l'elenco di argomenti variabili), sarebbe possibile associare std::function a quelle versioni.

Edit:

Cosa si può fare, però, è scrivere il proprio involucro che utilizza le funzioni vprintf, e gestisce la vararg-> di conversione va_list.

#include <cstdio> 
#include <cstdarg> 
#include <functional> 

class PrintWrapper 
{ 

    public: 
    PrintWrapper() = default; 


    template<typename T> 
    PrintWrapper(T&& t) : func(std::forward<T>(t)) 
    { 

    } 


    int operator()(char const* format, ...) 
    { 
     va_list args; 
     va_start(args, format); 
     int result = func(format, args); 
     va_end(args); 
     return result; 
    } 
    private: 

    std::function< int(char const*, va_list)> func; 

}; 

int main() 
{ 
    // Note, you have to use the 'v' versions 
    PrintWrapper p = std::vprintf; 
    p("%d %d %s\n", 1,2, "hello"); 
    char buffer[256]; 
    using namespace std::placeholders; 
    p = std::bind(std::vsnprintf, buffer, sizeof(buffer), _1, _2); 

    p("%lf %s\n", 0.1234, "goodbye"); 

    // Since the previous step was a wrapper around snprintf, we need to print 
    // the buffer it wrote into 
    printf("%s\n", buffer); 

    return 0; 
} 

http://ideone.com/Sc95ch

+2

In realtà, 'operator()' in 'std :: function' non è il problema - potrebbe semplicemente usare l'inoltro perfetto. La parte difficile sarebbe rendere il lavoro di cancellazione di tipo interno ... – Yakk

+0

La chiave è che ad un certo punto dell'implementazione, devi passare dall'inoltro perfetto alla chiamata del meccanismo di cancellazione del tipo, che non può sapere quali sono gli argomenti al chiama il sito, ed è lì che fallisce. –

+3

Sarei tentato di eseguire una firma variardica in stile C++ 11 sul mio 'PrintWrapper', quindi farla rimbalzare internamente a una lista' ... 'variardica in stile C, che poi esegue il lavoro' va_list' in Semplicemente per nascondere che '...' il più lontano possibile dall'utente finale ... – Yakk

Problemi correlati