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
How to Read a File at Compile Time
What Is the Purpose of Unary Plus Operator on Char Array
C++ Get Description of an Exception Caught in Catch(...) Block
How to Use the Windows API in Mingw
Memoized, Recursive Factorial Function
How to Get Process Handle from Process Id
Specifying Default Parameter When Calling C++ Function
How Is Tr1::Reference_Wrapper Useful
What Do the C and C++ Standards Say About Bit-Level Integer Representation and Manipulation
How to Specify Vc11 Lambda Calling Convention
Why Does C++ Allow Private Members to Be Modified Using This Approach
Why Openmp Under Ubuntu 12.04 Is Slower Than Serial Version
Std::Vector Calling Destructor Multiple Times During Push_Back
Is It Legal to Cast a Pointer to Array Reference Using Static_Cast in C++