3

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.

Zoe
  • 27,060
  • 21
  • 118
  • 148
atimin
  • 499
  • 4
  • 11
  • You should read syntax for lambda. `[](){}` declares a lambda. You forgot to write `()` in your declaration. – ALX23z Dec 29 '21 at 13:14
  • 1
    @ALX23z The `()` for an empty parameter list are optional in a lambda. – user17732522 Dec 29 '21 at 13:16
  • @user17732522 it is ok. I found it in the standard. I think it is the purpose of std::packaged_task to move it into some loop or thread to execute in parallel but be able to get the result in the main thread. – atimin Dec 29 '21 at 13:38

1 Answers1

3

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?

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • Thanks, you're right! Only one question more, how can I declare the mutable lambda explicitly. My Deffer method expects immutable lambda - void Deffer(std::function&& task) and it is not compiling now. – atimin Dec 29 '21 at 13:34
  • Unfortunately not, so you can't compile: std::function f = []() mutable {}; But with auto ofc, it is working. My problem is that i have an interface and I don't know so far the proper signature. But it may be another topic. – atimin Dec 29 '21 at 13:44
  • @flipback I see now what the issue is. I updated the answer. Unfortunately your approach will not work. – user17732522 Dec 29 '21 at 13:47