How to combine std::bind(), variadic templates, and perfect forwarding? - c++

I want to invoke a method from another, through a third-party function; but both use variadic templates. For example:
void third_party(int n, std::function<void(int)> f)
{
f(n);
}
struct foo
{
template <typename... Args>
void invoke(int n, Args&&... args)
{
auto bound = std::bind(&foo::invoke_impl<Args...>, this,
std::placeholders::_1, std::forward<Args>(args)...);
third_party(n, bound);
}
template <typename... Args>
void invoke_impl(int, Args&&...)
{
}
};
foo f;
f.invoke(1, 2);
Problem is, I get a compilation error:
/usr/include/c++/4.7/functional:1206:35: error: cannot bind ‘int’ lvalue to ‘int&&’
I tried using a lambda, but maybe GCC 4.8 does not handle the syntax yet; here is what I tried:
auto bound = [this, &args...] (int k) { invoke_impl(k, std::foward<Args>(args)...); };
I get the following error:
error: expected ‘,’ before ‘...’ token
error: expected identifier before ‘...’ token
error: parameter packs not expanded with ‘...’:
note: ‘args’
From what I understand, the compiler wants to instantiate invoke_impl with type int&&, while I thought that using && in this case would preserve the actual argument type.
What am I doing wrong? Thanks,

Binding to &foo::invoke_impl<Args...> will create a bound function that takes an Args&& parameter, meaning an rvalue. The problem is that the parameter passed will be an lvalue because the argument is stored as a member function of some internal class.
To fix, utilize reference collapsing rules by changing &foo::invoke_impl<Args...> to &foo::invoke_impl<Args&...> so the member function will take an lvalue.
auto bound = std::bind(&foo::invoke_impl<Args&...>, this,
std::placeholders::_1, std::forward<Args>(args)...);
Here is a demo.

Related

"template argument deduction/substitution failed" error with function object with parameter pack

I'm trying to make a function that takes a variable number of parameters of any type, but even the simple example I made is getting an error
#include <iostream>
#include <functional>
template<class... Ts>
void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
{
function(parameters...);
}
void myFunc(const std::string& output)
{
std::cout << output << std::endl;
}
int main()
{
callFunction<const std::string&>(&myFunc, "Hello world");
return 0;
}
When I run the above code in Ideone, I get this error:
prog.cpp: In function ‘int main()’:
prog.cpp:17:57: error: no matching function for call to ‘callFunction(void (*)(const string&), const char [12])’
callFunction<const std::string&>(&myFunc, "Hello world");
^
prog.cpp:5:6: note: candidate: template<class ... Ts> void callFunction(const std::function<void(Ts ...)>&, Ts ...)
void callFunction(const std::function<void(Ts...)>& function, Ts... parameters)
^~~~~~~~~~~~
prog.cpp:5:6: note: template argument deduction/substitution failed:
prog.cpp:17:57: note: mismatched types ‘const std::function<void(Ts ...)>’ and ‘void (*)(const string&) {aka void (*)(const std::__cxx11::basic_string<char>&)}’
callFunction<const std::string&>(&myFunc, "Hello world");
A simple suggestion: receive the callable as a deduced typename, not as a std::function
I mean (adding also perfect forwarding)
template <typename F, typename ... Ts>
void callFunction(F const & func, Ts && ... pars)
{ func(std::forward<Ts>(pars)...); }
and, obviously, call it without explicating nothing
callFunction(&myFunc, "Hello world");
This as the additional vantage that avoid the conversion of the callable to a std::function.
Anyway, I see two problems in your code:
1) if you receive the functional as a std::function receiving a list ot arguments types (a variadic list in this case, but isn't important for this problem) as a list of argument of the same types, you have to be sure that the types in the two list match exactly.
This isn't your case because the function receive a std::string const & and you pass as argument a the string literal "Hello world" that is a char const [12] that is a different type.
When the types are to be deduced, this cause a compilation error because the compiler can't choose between the two types.
You could solve receiving two list of types
template <typename ... Ts1, typename Ts2>
void callFunction (std::function<void(Ts1...)> const & function,
Ts2 && ... parameters)
{ function(std::forward<Ts2>(parameters)...); }
but now we have the second problem
2) You pass a pointer function (&myFunc) where callFunction() wait for a std::function.
We have a chicken-egg problem because &myFunc can be converted to a std::function but isn't a std::function.
So the compiler can't deduce the Ts... list of types from &myFunc because isn't a std::function and can't convert &myFunc to a std::function because doesn't know the Ts... type list.
I see that you have explicated the first type in the Ts... list, but isn't enough because the Ts... list is a variadic one so the compiler doesn't know that there is only a type in the Ts... list.
A simple solution to this problem is pass the function as a simple deduced F type.
Otherwise, if you have written callFunction() with two templates types lists, you can pass a std::function to the function
std::function<void(std::string const &)> f{&myFunc};
callFunction(f, "Hello world");
but I don't think is a satisfactory solution.

cannot bind non-const lvalue reference of type &X to an rvalue of type X

I wanna modify this threadpool implementation http://roar11.com/2016/01/a-platform-independent-thread-pool-using-c14/ to remove future return. My functions does not need to return anything but I'm having trouble modifying the submit function to do that.
template <class Func, class... Args>
void submit(Func &&func, Args &&... args)
{
auto boundTask = std::bind(std::forward<Func>(func),
std::forward<Args>(args)...);
m_workQueue.push(std::make_unique<ThreadTask<decltype(boundTask)>>(std::move(boundTask)));
}
Which I use it like this
pool_.submit([](int i){std::cout << i << std::endl;}, 1);
But it give me the following error
./include/concurrency/ThreadPool.h: In instantiation of ‘bool
conc::ThreadPool::submit(Func&&, Args&& ...) [with Func =
zia::ZiaServer::startService()::<lambda(int)>; Args = {int}]’:
src/ZiaServer.cpp:16:65: required from here
./include/concurrency/ThreadPool.h:85:9: error: cannot bind non-const
lvalue reference of type ‘std::unique_ptr<conc::IThreadTask>&’ to an
rvalue of type ‘std::unique_ptr<conc::IThreadTask>’
tasks_.push(std::make_unique<ThreadTask<decltype(boundTask)>>
(std::move(boundTask)));
Any idea of what's going on ?

Compiler error when passing rvalue reference through variadic templates

There is a requirement where I need to pass an rvalue from 1 function to another function via variadic template. To avoid real code complexity, minimal example is below using int:
void Third (int&& a)
{}
template<typename... Args>
void Second (Args&&... args) {
Third(args...);
}
void First (int&& a) {
Second(std::move(a)); // error: cannot bind ‘int’ lvalue to ‘int&&’
Third(std::move(a)); // OK
}
int main () {
First(0);
}
First(0) is called properly. If I invoke Third(int&&) directly then it works fine using std::move(). But calling Second(Args&&...) results in:
error: cannot bind ‘int’ lvalue to ‘int&&’
Third(args...); ^
note: initializing argument 1 of ‘void Third(int&&)’
void Third (int&& a)
What is the correct way to achieve the successful compilation for Second(Args&&...)?
FYI: In real code the Second(Args&&...) is mix of lvalues, rvalues and rvalue references. Hence if I use:
Third(std::move(args...));
it works. But when there are mix of arguments, it has problems.
You have to use std::forward:
template<typename... intrgs>
void Second (intrgs&&... args) {
Third(std::forward<intrgs>(args)...);
}
To preserve the rvalue-ness, you have to move or forward the parameter(s)
template<typename... intrgs>
void Second (intrgs&&... args) {
Third(std::forward<intrgs>(args)...);
}

Implementing a function that perfect-forwards to std::thread

I am trying to write a wrapper around std::thread:
#include <thread>
#include <iostream>
struct A {};
template <typename F, typename... Args>
void lifted_lambda_1(void *m, F &&entrypoint, Args&&... args) {
std::cout << "I will do something with the void * " << m << std::endl;
entrypoint(std::forward<Args>(args)...);
}
template <typename F, typename... Args>
void make_thread(void *p, F &&f, Args && ... args) {
std::thread(lifted_lambda_1<typename std::decay<F>::type, Args...>, p, std::forward<F>(f), std::forward<Args>(args)...).detach();
}
int main() {
A a;
make_thread(nullptr, [](A x){}, a);
}
But when I compile it I get an error:
In file included from /usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/thread:39:0,
from bubu.cpp:1:
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/functional: In instantiation of ‘struct std::_Bind_simple<void (*(void*, main()::__lambda0, A))(void*, main()::__lambda0&&, A&)>’:
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/thread:137:47: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void*, main()::__lambda0&&, A&); _Args = {void*&, main()::__lambda0, A&}]’
bubu.cpp:15:132: required from ‘void make_thread(void*, F&&, Args&& ...) [with F = main()::__lambda0; Args = {A&}]’
bubu.cpp:20:38: required from here
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<void (*(void*, main()::__lambda0, A))(void*, main()::__lambda0&&, A&)>’
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<void (*(void*, main()::__lambda0, A))(void*, main()::__lambda0&&, A&)>’
_M_invoke(_Index_tuple<_Indices...>)
What is the reason for this error? How do I fix it?
std::thread will always decay its arguments (for the reasons given at the link in one of the comments above). You can use reference_wrapper to protect references, so that arguments can be passed by lvalue reference.
To make that work with both lvalue and rvalue arguments you need a wrapper function which will wrap lvalues in reference_wrapper but allow rvalues to be copied (or moved) and forwarded as rvalues. This will not be "perfect" forwarding, because rvalues will be copied, not forwarded as rvalue references, so the target function gets called with new objects.
So you can use something like this to conditionally wrap lvalues but just forward rvalues:
template<typename T>
std::reference_wrapper<std::remove_reference_t<T>>
wrap(T& t) { return std::ref(t); }
template<typename T>
T&&
wrap(typename std::remove_reference<T>::type&& t)
{ return std::move(t); }
(remove_reference is used on the second overload so that T is in a non-deduced context, and so that the argument is not a forwarding reference).
Then use that for the arguments to the thread constructor:
std::thread(lifted_lambda_1<typename std::decay<F>::type, Args...>, p,
std::forward<F>(f), wrap<Args>(args)...).detach();
/*^^^^^^^^^^^^*/
However, doing this brings back all the problems that std::thread tries to avoid by copying its arguments! You must ensure that any lvalues passed to make_thread will not go out of scope before the thread finishes running. Since you are detaching the thread, that is very difficult to do in general. You must be very careful when using this function.
Potentially you could write your own class template that behaves like reference_wrapper that protects rvalue references, to avoid the new objects being created, but then you must also be careful that the rvalue arguments to the thread function do not go out of scope before the thread finishes running (and if they are rvalues there is a high probability that they are temporaries which will not outlive the call that creates the new thread!)
Here be dragons.

Constructing class with member tuple containing rvalue references

When trying to construct a class which is supposed to hold a tuple created by calling std::forward_as_tuple I ran into the following error when compiling with clang(187537) and libc++:
/usr/include/c++/v1/tuple:329:11: error: rvalue reference to type 'int' cannot
bind to lvalue of type 'int'
: value(__t.get())
^ ~~~~~~~~~
/usr/include/c++/v1/tuple:447:8: note: in instantiation of member function
'std::__1::__tuple_leaf<0, int &&, false>::__tuple_leaf' requested here
struct __tuple_impl<__tuple_indices<_Indx...>, _Tp...>
^
tuple.cpp:31:5: note: in instantiation of function template specialization
'make_foo2<int>' requested here
make_foo2(1 + 1);
^
In file included from tuple.cpp:2:
/usr/include/c++/v1/tuple:330:10: error: static_assert failed "Can not copy a
tuple with rvalue reference member"
{static_assert(!is_rvalue_reference<_Hp>::value, "Can not copy ...
I was able to work around the above error by declaring the return type differently, but, from my understanding, it should have the same semantics so I would not expect it to stop the error. In the below code make_foo is the workaround which does not error out and make_foo2 causes the above error. I am able to successfully compile both versions using gcc 4.8.1 and the version of clang at coliru.
#include <utility>
#include <tuple>
template<class Tuple>
struct foo
{
Tuple t;
foo(Tuple &&t) : t(std::move(t)) { }
};
template<class... Args>
using ForwardedTuple = decltype(std::forward_as_tuple(std::forward<Args>(std::declval<Args>())...));
template<class... Args>
foo<ForwardedTuple<Args...>> make_foo(Args&&... args)
{
return {std::forward_as_tuple(std::forward<Args>(args)...)};
}
template<class... Args>
auto make_foo2(Args&& ...args) ->
decltype(foo<decltype(std::forward_as_tuple(std::forward<Args>(args)...))>(std::forward_as_tuple(std::forward<Args>(args)...)))
{
return foo<decltype(std::forward_as_tuple(std::forward<Args>(args)...))>(std::forward_as_tuple(std::forward<Args>(args)...));
}
int main()
{
make_foo(1 + 1);
make_foo2(1 + 1);
}
What is the difference between the above make_foo functions and is make_foo2 incorrect?
Thanks.
Looks like you return foo<> from make_foo2. But foo doesn't have move constructor generated (Compiler won't generate it). Therefore copy constructor is called and compilation fails because of that.