I want to defer the execution of a packaged task in a loop.
class ILoop {
public:
virtual void Deffer(std::function<void()>&& task) = 0;
void Deffer(std::function<void()>& task) = delete;
static void set_loop(ILoop*);
static ILoop& loop();
private:
static ILoop* loop_;
};
template <typename T>
struct LoopExecutor {
std::future<T> Commit(std::function<T()>&& task) {
std::packaged_task<T()> wrapper([t = std::move(task)] { return t(); });
std::future<T> future = wrapper.get_future();
ILoop::loop().Deffer([wrapper = std::move(wrapper)] { wrapper(); });
return future;
}
};
when I try to compile this
auto t = executor.Commit([] { return 100; });
I have compilation error:
[1/2] Building CXX object unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o
FAILED: unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o
/usr/bin/g -11 -I/home/atimin/.conan/data/fmt/8.0.1/_/_/package/20da98908a15523bbe9f086a5c57a4bde424a9c0/include -I/home/atimin/.conan/data/uwebsockets/20.8.0/_/_/package/c84c7dca9672f88b95bafb2f5754a22669d1bbe5/include -I/home/atimin/.conan/data/uwebsockets/20.8.0/_/_/package/c84c7dca9672f88b95bafb2f5754a22669d1bbe5/include/uWebSockets -I/home/atimin/.conan/data/nlohmann_json/3.9.1/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include -I/home/atimin/.conan/data/gtest/1.11.0/_/_/package/63868df56b76903d4ad40ecbd5b2e8238cee50c9/include -I/home/atimin/.conan/data/catch2/2.13.7/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include -I/home/atimin/.conan/data/zlib/1.2.11/_/_/package/be27726f9885116da1158027505be62e913cd585/include -I/home/atimin/.conan/data/usockets/0.8.1/_/_/package/f3766c5a18b2feee4dc2c3a94335008aaaea2543/include -I/home/atimin/Projects/flipback/reduct-storage/src -I/home/atimin/Projects/flipback/reduct-storage/cmake-build-debug-gcc11 -I/home/atimin/Projects/flipback/reduct-storage/unit_tests -g -DLIBUS_NO_SSL -std=gnu 20 -MD -MT unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o -MF unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o.d -o unit_tests/CMakeFiles/reduct_tests.dir/reduct/async/awaiters_test.cc.o -c /home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc
In file included from /home/atimin/Projects/flipback/reduct-storage/src/reduct/async/awaiters.h:10,
from /home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc:3:
/home/atimin/Projects/flipback/reduct-storage/src/reduct/async/executors.h: In instantiation of ‘std::future<_Res> reduct::async::LoopExecutor<T>::Commit(std::function<T()>&&) [with T = int]’:
/home/atimin/Projects/flipback/reduct-storage/src/reduct/async/awaiters.h:40:68: required from ‘reduct::async::Run<T, Executor>::Run(std::function<T()>&&) [with T = int; Executor = reduct::async::LoopExecutor<int>]’
/home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc:52:92: required from here
/home/atimin/Projects/flipback/reduct-storage/src/reduct/async/executors.h:19:66: error: no match for call to ‘(const std::packaged_task<int()>) ()’
19 | ILoop::loop().Deffer([wrapper = std::move(wrapper)] { wrapper(); });
| ~~~~~~~^~
In file included from /home/atimin/Projects/flipback/reduct-storage/src/reduct/async/awaiters.h:8,
from /home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc:3:
/usr/include/c /11/future:1577:7: note: candidate: ‘void std::packaged_task<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) [with _Res = int; _ArgTypes = {}]’ (near match)
1577 | operator()(_ArgTypes... __args)
| ^~~~~~~~
/usr/include/c /11/future:1577:7: note: passing ‘const std::packaged_task<int()>*’ as ‘this’ argument discards qualifiers
In file included from /usr/include/c /11/future:47,
from /home/atimin/Projects/flipback/reduct-storage/src/reduct/async/awaiters.h:8,
from /home/atimin/Projects/flipback/reduct-storage/unit_tests/reduct/async/awaiters_test.cc:3:
/usr/include/c /11/bits/std_function.h:414:9: error: ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = reduct::async::LoopExecutor<int>::Commit(std::function<int()>&&)::<lambda()>; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Res = void; _ArgTypes = {}]’, declared using local type ‘reduct::async::LoopExecutor<int>::Commit(std::function<int()>&&)::<lambda()>’, is used but never defined [-fpermissive]
414 | function(_Functor __f)
Actually, even a simpler code (no Defer function) is also not working:
auto t = [w = std::move(wrapper)] { w(); };
t();
On the other hand, it is ok:
std::thread t(std::move(wrapper));
I use GCC-11.2. Thanks in advance.
CodePudding user response:
By default a lambda's call operator is const
-qualified.
Inside the lambda's body the this
pointer to the lambda is therefore also const
-qualified and so is the member wrapper
.
std::packaged_task
does not have a const
-qualified operator()
, so it cannot be called.
You can make the lambda's operator()
non-const
-qualified by adding the mutable
keyword:
[wrapper = std::move(wrapper)]()mutable{ wrapper(); }
Note that currently the syntax for a lambda with a specifier does require the empty parameter list to be given as ()
.
However, you have a bigger issue: Storing a std::packaged_task
inside the lambda makes the lambda non-copyable. But std::function
requires the stored type to be copyable. You cannot use it for this use case, see also How to create an std::function from a move-capturing lambda expression?