Почему невозможно привязать std:: function к вариационным функциям C-стиля?

Например, это не скомпилируется:

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

В gcc сообщение об ошибке читается:

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

Сначала я думал, что это ошибка в gcc, но затем я посмотрел на стандарт, и похоже, что это просто не поддерживается. Какая техническая причина для этого?

Ответ 1

Проблема заключается в реализации. Пусть говорят, что это возможно. Тогда std::function должно было бы объявить (в случае printf)

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

При вызове он должен будет передать содержимое... любому объекту, который вы назначили. Проблема в том, что он недостаточно разбирается в аргументах, чтобы знать, КАК это передать это, что является проблемой. printf() анализирует формат, но другие используют другие механизмы (значение "end" является популярным).

Для семейства функций printf я предлагаю вам посмотреть версии vXXX (например, vprintf). Поскольку они используют хорошо определенные аргументы (последний из которых является списком аргументов переменных), можно было бы привязать std::function к этим версиям.

Edit:

Однако вы можете написать собственную оболочку, которая использует функции vprintf, и обрабатывает преобразование vararg- > 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