2013-08-22 6 views
15

Na przykład, to nie trafia do kompilacji:Dlaczego funkcja std :: nie może być powiązana z funkcjami variadic w stylu C?

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

z GCC, komunikat o błędzie brzmi:

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

Na początku myślałem, że to był błąd w gcc, ale potem spojrzał na standard i wygląda na to, że nie jest to obsługiwane. Jaki jest tego techniczny powód?

+1

mógłbym napisać klasy równoważne 'std :: zakresie funkcji w C++ 11: jak to jest,' std :: zakresie funkcji można zapisać w postaci czystego pliku nagłówka w klasie standardowy zgodny sposób. Nie jestem pewien, czy mógłbym napisać 'std :: function ' bez rozszerzania języka lub używania rozszerzeń specyficznych dla kompilatora. Czy możemy mieć "wirtualne" metody w wariancie C? Nawet jeśli tak, wewnętrzny obiekt wymazywania typów musiałby przekazywać argumenty '...' do 'printf', który nie jest obsługiwany AFAIK. – Yakk

+0

@Yakk: FYI, możesz mieć "wirtualną" metodę varadic w stylu C. Problem polega na tym, że nie ma sposobu na przekazanie parametrów varadic w stylu C do innej funkcji bez konwersji na va_list lub samodzielnego analizowania ich. –

Odpowiedz

7

Kwestia jest jedną z realizacji. Powiedzmy, że to było możliwe. Następnie std::function musiałby zadeklarować (w przypadku printf)

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

Wywołany, to wtedy trzeba zdać zawartość ... aby cokolwiek obiekt przypisany. Problem polega na tym, że nie ma wystarczającej wiedzy na temat argumentów, aby wiedzieć, JAK przekazać to, co jest problemem. printf() analizuje format, ale inni używają innych mechanizmów (popularna jest wartość "końca").

Dla rodziny funkcji printf proponuję zapoznać się z wersjami vXXX (np. Vprintf). Ponieważ używają dobrze zdefiniowanych argumentów (z których ostatnia jest zmienną listą argumentów), możliwe jest związanie tych wersji z std::function.

Edit:

Co można zrobić, jest jednak napisać własne otoki, który używa funkcji vprintf, i obsługuje vararg-> konwersji 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

W rzeczywistości 'operator()' w 'std :: function' nie stanowi problemu - może po prostu użyć idealnego forwardowania. Najtrudniejszą częścią byłoby wykonanie wewnętrznego obiektu do wymazywania typów ... – Yakk

+0

Kluczem jest to, że w pewnym momencie implementacji musisz przejść od perfekcyjnego przekierowania do wywołania mechanizmu usuwania typów, który nie może wiedzieć, jakie są argumenty zadzwoń do strony i to się nie udaje. –

+3

Chciałbym pokusić się na sygnaturę zmienną w stylu C++ 11 na moim 'PrintWrapper', a następnie odesłać ją wewnętrznie do listy" ... "w stylu C, która następnie wykonuje pracę' va_list' Po prostu ukryć, że '...' tak daleko od użytkownika końcowego, jak rozsądnie ... – Yakk

Powiązane problemy