Why Can't Std::Function Bind to C-Style Variadic Functions

Why can't std::function bind to C-style variadic functions?

The issue is one of implementation. Let's say it was possible. Then std::function would have to declare (in the case of printf)

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

When called, it would then have to pass the contents of ... to whatever object you assigned. The issue is that it doesn't know enough about the arguments to know HOW to pass that down, which is an issue. printf() parses the format, but others use other mechanisms (an 'end' value is popular).

For the printf family of functions, I suggest you look at the vXXX versions (e.g. vprintf). Since they use well defined arguments (the last one being the variable argument list), it would be possible to bind std::function to those versions.

Edit:

What you can do, however, is write your own wrapper that uses the vprintf functions, and handles the vararg-> va_list conversion.

#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

Binding Variadic Functions C++

Worth mentioning that C++20 also provides std::bind_front for exactly this purpose:

auto bound_sum = std::bind_front(sum, 0);

(This has a number of potential advantages over the naïve lambda implementation; see the paper proposing the feature for details if you are so concerned.)

std::function with C variadic arguments, not templated variable arguments

This isn't possible with std::function.

It's pretty much impossible to forward C-style variadic arguments directly, without knowing what arguments were actually passed.

Depending on the circumstances, you might just take a function pointer - a int(*)(const char*, ...), or, if type erasure is necessary, rework your code to use the variant taking a va_list.

std::bind with variadic template member function and universal references

If you specify the template argument as int explicitly, then the parameter type of func_to_bind would become int&&, i.e. an rvalue-reference type. Note that the stored arguments are passed to the invokable object as lvalues by std::bind:

Otherwise, the ordinary stored argument arg is passed to the invokable object as lvalue argument:

The lvalue can't be bound to the rvalue-referece parameter then invocation fails.

If you specify the template argument as int& explicitly, then the parameter type of func_to_bind becomes int&, i.e. an lvalue-reference type; lvalue could be bound to lvalue-reference then it works fine.

And if you change the parameter type of func_to_bind to ARGS&, it'll be always an lvalue-reference, for the same reason above it'll work fine.

How does std::bind take variadic arguments by value, even with its universal reference?

It has almost nothing to do with the signature, it is a design choice. std::bind must of course store all its bound arguments somehow and it stores them as values. The "universality" is only used to properly construct them - by move or copy.

std::ref is also stored by value but due to its nature, the wrapped object is "stored" by reference.

std::thread has exactly the same behaviour. One can argue that (move) constructing a copy by default is safer because both returned objects tend to outlive locals which are the likeliest to be captured.

C++ Passing std::function object to variadic template

Why doesn't it work without explicit <int>?

Prior to C++17, template type deduction is pure pattern matching.

std::function<void(Foo*)> can store a member function pointer of type void(Foo::*)(), but a void(Foo::*)() is not a std::function of any kind.

MakeFoo takes its argument, and pattern matches std::function<void(Bar*, Args...)>. As its argument is not a std::function, this pattern matching fails.

In your other case, you had fixed Args..., and all it had to do was convert to a std::function<void(Bar*, Args...)>. And there is no problem.

What can be converted to is different than what can be deduced. There are a myriad of types of std::function a given member function could be converted to. For example:

struct Foo {
void set( double );
};
std::function< void(Foo*, int) > hello = &Foo::set;
std::function< void(Foo*, double) > or_this = &Foo::set;
std::function< void(Foo*, char) > why_not_this = &Foo::set;

In this case there is ambiguity; in the general case, the set of template arguments that could be used to construct some arbitrary template type from an argument requires inverting a turing-complete computation, which involves solving Halt.

Now, C++17 added deduction guides. They permit:

std::function f = &Foo::set;

and f deduces the signature for you.

In C++17, deduction doesn't guides don't kick in here; they may elsewhere, or later on.

Why doesn't it work with explicit <int>?

Because it still tries to pattern match and determine what the rest of Args... are.

If you changed MakeFoo to

template<class T>
std::unique_ptr<Foo<T>> MakeFoo(std::function<void(Bar*, T)> f) {
return std::make_unique<Foo<T>>(f);
}

suddenly your code compiles. You pass it int, there is no deduction to do, and you win.

But when you have

template<class...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(std::function<void(Bar*, Args...)> f) {
return std::make_unique<Foo<T>>(f);
}

the compiler sees <int> and says "ok, so Args... starts with int. What comes next?".

And it tries to pattern match.

And it fails.

How can you fix it?

template<class T>struct tag_t{using type=T; constexpr tag_t(){}};
template<class T>using block_deduction=typename tag_t<T>::type;

template<class...Args>
std::unique_ptr<Foo<Args...>> MakeFoo(
block_deduction<std::function<void(Bar*, Args...)>> f
) {
return std::make_unique<Foo<T>>(f);
}

now I have told the compiler not to deduce using the first argument.

With nothing to deduce, it is satisfied that Args... is just int, and... it now works.

std::bind with variable function arguments including a callback function with variable arguments

Since you can't use C++14's generic lambdas you can make your own by making a functor. If you have

struct CallWithArgsFunctor
{
pointer_type pointer_to_call_on;
CallWithArgsFunctor(pointer_type pointer_to_call_on) : pointer_to_call_on(pointer_to_call_on) {}
template<typename... Args>
auto operator()(Args&&... args) -> decltype(CallWithArgs(pointer_to_call_on, std::forward<Args>(args)...))
{
return CallWithArgs(pointer_to_call_on, std::forward<Args>(args)...)
}
};

then you can use it in your code block like

bool value = true;
std::future<bool> fut = std::async(CallWithArgsFunctor{rawPtr}, cbk, value);
fut.wait();

This allows overload resolution to work in the body of the call operator instead of you having to cast the function pointer to the type you want to call.


If you can upgrade to C++14 your code would just become

bool value = true;
auto f1 = [=](auto&&... args){ return CallWithArgs(rawPtr, std::forward<decltype(args)>(args)...); };
std::future<bool> fut = std::async(f1, cbk, value);
fut.wait();


Related Topics



Leave a reply



Submit