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

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.

Related

Why std::is_invocable<std::decay_t<void(int&)>, std::decay_t<int>>::value is false?

std::is_invocable<std::decay_t<void(int&)>, std::decay_t<int>>::value
evaluates to false.
But
void(int&)
decays to
void*(int&)
and
int
to
int
and I can use std::invoke like this:
void f(int&);
...
auto* fp = f;
int i = 0;
std::invoke(fp, i);
I bumped into this when I was looking at std::thread constructor:
template<typename _Callable, typename... _Args,
typename = _Require<__not_same<_Callable>>>
explicit
thread(_Callable&& __f, _Args&&... __args)
{
static_assert( __is_invocable<typename decay<_Callable>::type,
typename decay<_Args>::type...>::value,
"std::thread arguments must be invocable after conversion to rvalues"
);
I understand why passing references to std::thread's constructor is disouraged (we still can use std::ref()) but I cannot understand why void *(int&) cannot be invoked with int.
std::is_invocable<..., int> attempts to use an int rvalue as the argument. It's equivalent to std::is_invocable<..., int &&>.
Use std::is_invocable<..., int &>.
std::is_invocable
...Formally, determines whether INVOKE(declval<Fn>(), declval<ArgTypes>()...) is well formed...
Even if we ignore what INVOKE is, you can see that is_invocable is defined using std::declval<ArgType>(), which returns ArgType &&.
ArgType && is, well, an rvalue reference. Unless ArgType is an lvalue reference, in which case ArgType && is equivalent to ArgType and is an lvalue reference (according to the reference collapsing rules).

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 ?

std::bind on a generic lambda - auto type deduction

Consider the following code:
#include <iostream>
#include <functional>
int main() {
auto run = [](auto&& f, auto&& arg) {
f(std::forward<decltype(arg)>(arg));
};
auto foo = [](int &x) {};
int var;
auto run_foo = std::bind(run, foo, var);
run_foo();
return 0;
}
Which gives the following compilation error when compiled with clang:
$ clang++ -std=c++14 my_test.cpp
my_test.cpp:6:9: error: no matching function for call to object of type 'const (lambda at my_test.cpp:8:16)'
f(std::forward<decltype(arg)>(arg));
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:998:14: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const (lambda at my_test.cpp:8:16) &, const int &>' requested here
= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:1003:2: note: in instantiation of default argument for 'operator()<>' required here
operator()(_Args&&... __args) const
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
my_test.cpp:11:12: note: while substituting deduced template arguments into function template 'operator()' [with _Args = <>, _Result = (no value)]
run_foo();
^
my_test.cpp:8:16: note: candidate function not viable: 1st argument ('const int') would lose const qualifier
auto foo = [](int &x) {};
^
my_test.cpp:8:16: note: conversion candidate of type 'void (*)(int &)'
1 error generated.
Why is arg deduced to be const int& instead of just int&?
std::bind documentation says:
Given an object g obtained from an earlier call to bind, when it is
invoked in a function call expression g(u1, u2, ... uM), an invocation
of the stored object takes place, as if by std::invoke(fd,
std::forward(v1), std::forward(v2), ...,
std::forward(vN)), where fd is a value of type std::decay_t the
values and types of the bound arguments v1, v2, ..., vN are determined
as specified below.
...
Otherwise, the
ordinary stored argument arg is passed to the invokable object as
lvalue argument: the argument vn in the std::invoke call above is
simply arg and the corresponding type Vn is T cv &, where cv is the
same cv-qualification as that of g.
But in this case, run_foo is cv-unqualified. What am I missing?
MWE:
#include <functional>
int main() {
int i;
std::bind([] (auto& x) {x = 1;}, i)();
}
[func.bind]/(10.4) states that the cv-qualifiers of the argument passed to the lambda are those of the argument to bind, augmented by the cv-qualifiers of the call wrapper; but there are none, and thus a non-const int should be passed in.
Both libc++ and libstdc++ fail to resolve the call. For libc++, reported as #32856, libstdc++ as #80564. The main problem is that both libraries infer the return type in the signature somehow, looking like this for libstdc++:
// Call as const
template<typename... _Args, typename _Result
= decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0),
typename add_const<_Functor>::type&>::type>()(
_Mu<_Bound_args>()( std::declval<const _Bound_args&>(),
std::declval<tuple<_Args...>&>() )... ) )>
_Result operator()(_Args&&... __args) const
During template argument deduction as necessitated by overload resolution, the default template argument will be instantiated, which causes a hard error due to our ill-formed assignment inside the closure.
This can be fixed by perhaps a deduced placeholder: remove _Result and its default argument entirely, and declare the return type as decltype(auto). This way, we also get rid of SFINAE which influences overload resolution and thereby induces incorrect behaviour:
#include <functional>
#include <type_traits>
struct A {
template <typename T>
std::enable_if_t<std::is_const<T>{}> operator()(T&) const;
};
int main() {
int i;
std::bind(A{}, i)();
}
This should not compile—as explained above, the argument passed to A::operator() should be non-const because i and the forwarding call wrapper are. However, again, this compiles under libc++ and libstdc++, because their operator()s fall back on const versions after the non-const ones fail under SFINAE.

Failed to compile because of incompatible cv-qualifiers

I have two template method
template <typename T, typename Ret, typename ...Args>
Ret apply(T* object, Ret(T::*method)(Args...), Args&& ...args) {
return (object->*method)(std::forward(args)...);
};
template <typename T, typename Ret, typename ...Args>
Ret apply(T* object, Ret(T::*method)(Args...) const, Args&& ...args) {
return (object->*method)(std::forward(args)...);
};
My purpose is apply member method of class T on these args
this is my test code:
int main() {
using map_type = std::map<std::string, int>;
map_type map;
map.insert(std::make_pair("a", 1));
std::cout << "Map size: " << apply(&map, &map_type::size) << std::endl; //this code work
apply(&map, &map_type::insert, std::make_pair("a", 1)); //failed to compile
return 0;
}
This is compiler error message:
test.cpp: In function ‘int main()’:
test.cpp:61:58: error: no matching function for call to ‘apply(map_type*, <unresolved overloaded function type>, std::pair<const char*, int>)’
apply(&map, &map_type::insert, std::make_pair("a", 1));
^
test.cpp:11:5: note: candidate: template<class T, class Ret, class ... Args> Ret apply(T*, Ret (T::*)(Args ...), Args&& ...)
Ret apply(T* object, Ret(T::*method)(Args...), Args&& ...args) {
^~~~~
test.cpp:11:5: note: template argument deduction/substitution failed:
test.cpp:61:58: note: couldn't deduce template parameter ‘Ret’
apply(&map, &map_type::insert, std::make_pair("a", 1));
std::map::insert is an overloaded function. You cannot take its address unless you explicitly specify the overload you're interested about - how else would the compiler know?
The easiest way to solve your problem is to have apply accept an arbitrary function object and wrap your call to insert in a generic lambda.
template <typename F, typename ...Args>
decltype(auto) apply(F f, Args&& ...args) {
return f(std::forward<Args>(args)...);
};
Usage:
::apply([&](auto&&... xs) -> decltype(auto)
{
return map.insert(std::forward<decltype(xs)>(xs)...);
}, std::make_pair("a", 1));
live wandbox example
The additional syntactic boilerplate is unfortunately impossible to avoid. This might change in the future, see:
N3617 aimed to solve this issue by introducing a "lift" operator.
P0119 by A. Sutton solves the problem in a different way by allowing overload sets to basically generate the "wrapper lambda" for you when passed as arguments.
I'm not sure if overloaded member functions are supported in the above proposals though.
You can alternatively use your original solution by explicitly specifying the overload you're intersted in on the caller side:
::apply<map_type, std::pair<typename map_type::iterator, bool>,
std::pair<const char* const, int>>(
&map, &map_type::insert<std::pair<const char* const, int>>,
std::make_pair("a", 1));
As you can see it's not very pretty. It can be probably improved with some better template argument deduction, but not by much.

Why std::bind() doesn't work with constants in this scenario?

Let me start with explaining what I try to accomplish. I need to create a type-erased functor (using templates and virtual functions) that would be able to "emplace" a new object in the storage of message queue for the RTOS I'm developing. Such "trickery" is required, because I want most of message queue's code to be non-templated, with only the parts that really need the type info implemented as such type-erased functors. This is a project for embedded microcontrollers (*), so please assume that I just cannot make whole message queue with a template, because ROM space is not unlimited in such environment.
I already have functors that can "copy-construct" and "move-construct" the object into the queue's storage (for "push" operations) and I also have a functor that can "swap" the object out of the queue's storage (for "pop" operations). To have a complete set I need a functor that will be able to "emplace" the object into the queue's storage.
So here is the minimum example that exhibits the problem I'm facing with creating it. Do note that this is a simplified scenario, which doesn't show much of the boiler plate (there are no classes, no inheritance and so on), but the error is exactly the same, as the root cause is probably the same too. Also please note, that the use of std::bind() (or a similar mechanism that would NOT use dynamic allocation) is essential to my use case.
#include <functional>
template<typename T, typename... Args>
void emplacer(Args&&... args)
{
T value {std::forward<Args>(args)...};
}
template<typename T, typename... Args>
void emplace(Args&&... args)
{
auto boundFunction = std::bind(emplacer<T, Args...>,
std::forward<Args>(args)...);
boundFunction();
}
int main()
{
int i = 42;
emplace<int>(i); // <---- works fine
emplace<int>(42); // <---- doesn't work...
}
When compiled on PC with g++ -std=c++11 test.cpp the first instantiation (the one using a variable) compiles with no problems, but the second one (which uses a constant 42 directly) throws this error messages:
test.cpp: In instantiation of ‘void emplace(Args&& ...) [with T = int; Args = {int}]’:
test.cpp:21:17: required from here
test.cpp:13:16: error: no match for call to ‘(std::_Bind<void (*(int))(int&&)>) ()’
boundFunction();
^
In file included from test.cpp:1:0:
/usr/include/c++/4.9.2/functional:1248:11: note: candidates are:
class _Bind<_Functor(_Bound_args...)>
^
/usr/include/c++/4.9.2/functional:1319:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args)
^
/usr/include/c++/4.9.2/functional:1319:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1315:37: error: cannot bind ‘int’ lvalue to ‘int&&’
= decltype( std::declval<_Functor>()(
^
/usr/include/c++/4.9.2/functional:1333:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) const
^
/usr/include/c++/4.9.2/functional:1333:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1329:53: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘const int’
typename add_const<_Functor>::type>::type>()(
^
/usr/include/c++/4.9.2/functional:1347:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) volatile
^
/usr/include/c++/4.9.2/functional:1347:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1343:70: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘volatile int’
typename add_volatile<_Functor>::type>::type>()(
^
/usr/include/c++/4.9.2/functional:1361:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) const volatile
^
/usr/include/c++/4.9.2/functional:1361:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1357:64: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘const volatile int’
typename add_cv<_Functor>::type>::type>()(
I tried looking for inspiration in other places, but Intel's TBB library which has a similar code (concurent_queue) with similar functionality (there's an emplace function) is actually no emplace at all - it constructs the object instantly and just "moves" it into the queue...
Any idea what's wrong with the code above? I suppose it's something really small, but I just cannot solve that myself...
(*) - https://github.com/DISTORTEC/distortos
You've already had an explanation of how that is just how std::bind works (it turns everything into an lvalue), and to use a lambda instead. However, that is not exactly trivial. Lambdas can capture by value, or by reference. You sort of need a mix of both: rvalue references should be assumed to possibly reference temporaries, so should be captured by value, with move semantics. (Note: that does mean that the original object gets moved from before the lambda gets invoked.) Lvalue references should be captured by reference, for probably obvious reasons.
One way to make this work is to manually put the captured arguments in a tuple of lvalue reference types and non-reference types, and unpack when you want to invoke the function:
template <typename T>
struct remove_rvalue_reference {
typedef T type;
};
template <typename T>
struct remove_rvalue_reference<T &&> {
typedef T type;
};
template <typename T>
using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type;
template <typename F, typename...T, std::size_t...I>
decltype(auto) invoke_helper(F&&f, std::tuple<T...>&&t,
std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::move(t))...);
}
template <typename F, typename...T>
decltype(auto) invoke(F&&f, std::tuple<T...>&&t) {
return invoke_helper<F, T...>(std::forward<F>(f), std::move(t),
std::make_index_sequence<sizeof...(T)>());
}
template<typename T, typename... Args>
void emplacer(Args&&... args) {
T{std::forward<Args>(args)...};
}
template<typename T, typename...Args>
void emplace(Args&&...args)
{
auto boundFunction =
[args=std::tuple<remove_rvalue_reference_t<Args>...>{
std::forward<Args>(args)...}]() mutable {
invoke(emplacer<T, Args...>, std::move(args));
};
boundFunction();
}
When calling emplace with args T1 &, T2 &&, the args will be captured in a tuple<T1 &, T2>. The tuple gets unpacked (thanks to #Johannes Schaub - litb for the basic idea) when finally invoking the function.
The lambda needs to be mutable, to allow that captured tuple to be moved from when invoking the function.
This uses several C++14 features. Most of these can be avoided, but I don't see how to do this without the ability to specify an initialiser in the capture list: C++11 lambdas can only capture by reference (which would be reference to the local variable), or by value (which would make a copy). In C++11, I think that means the only way to do it is not use a lambda, but effectively re-create most of std::bind.
To expand on #T.C.'s comment, you can make the code work by changing the type of the created emplacer.
auto boundFunction = std::bind(emplacer<T, Args&...>,
std::forward<Args>(args)...);
Notice the & right after Args. The reason is you're passing an rvalue to the emplace function which in turn creates emplacer(int&&). std::bind however always passes an lvalue (because it comes from its internals). With the change in place, the signature changes to emplacer(int&) (after reference collapsing) which can bind to an lvalue.