Home > Blockchain >  Can't move std::packaged_task into a lambda
Can't move std::packaged_task into a lambda

Time:12-30

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?

  • Related