8

I have got a dispatch function which executes a given lambda in a main thread. For the sake of this question, suppose it looks like the following:

void dispatch(const std::function<void()>& fn) {
    fn();
}

I need to load a new object in a new thread without interrupting the main thread. So I do the following: 1) start a new thread and create a new unique pointer inside the thread, 2) call dispatch and propagate the new unique pointer where it belongs.

std::unique_ptr<std::string> foo; // nullptr

// do the loading in a new thread:
std::thread t([&](){
    // in the new thread, load new value "Blah" and store it temporarily
    auto bar = std::make_unique<std::string>("Blah");
    dispatch([bar2 = std::move(bar), &foo]() mutable {
        foo = std::move(bar2); // propagate the loaded value to foo
    });
});
t.join(); // for the sake of this example

std::cout << "foo = " << *foo << std::endl; // this should say: foo = Blah

Run example online: http://cpp.sh/5zjvm

This code does not compile because the inner lambda in dispatch is mutable and so does not fit into dispatch(const std::function<void()>& fn) which requires a const&.

The lambda, however, needs to be mutable because it needs to call std::move on the unique pointers.

This code could be fixed for example by changing dispatch to:

template <typename Fn>
void dispatch(Fn fn) {
    fn();
}

Unfortunately, the dispatch function is an API of a library and I cannot change it.

Is there a way out of this problem without getting rid of unique pointers?

TomsonTom
  • 742
  • 7
  • 22
  • 1
    Why don't you just do `foo = std::make_unique("Blah");` inside the thread instead of making `dispatch` the thing that populates `foo`? – NathanOliver Oct 26 '18 at 18:34
  • That is a good idea, but I need to prevent race conditions in a real-time rendering application. There is a main thread rendering `foo` (in reality, `foo` is a geometry object, not `std::string`) and I cannot change the value of `foo` in the middle of the rendering. I need to dispatch the new `foo` to the main thread so that it is populated after a previous frame finished being rendered and before a new frame starts being rendered. – TomsonTom Oct 26 '18 at 18:42

1 Answers1

9

No, that isn't your problem.

Your problem is that your lambda cannot be copied, as it has a unique ptr captured by value in it.

std::function<Sig> type erases down to

  1. Invoke with Sig

  2. Destroy

  3. Copy (and sometimes move)

  4. Cast-back-to-original-type

Your lambda cannot be copied, so cannot be stored in a std::function.

The lazy-coder's solution is:

    dispatch([bar2 = std::make_shared<decltype(bar)>(std::move(bar)), &foo]() mutable {
        foo = std::move(*bar2);
    });

where we shove the non-copyable state into a shared_ptr.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Oh, my bad, you are right, the problem is not the lambda being `mutable`! I have updated the title of my question a little bit so it at least makes sense. So your solution is based on storing the `unique_ptr` inside a `shared_ptr`, which can be copied. Did I understand it correctly? Does the `std::move(*bar2)` prevent unnecessary copying of the `bar` object? Are there are problems with this solution? Thanks! – TomsonTom Oct 26 '18 at 19:00
  • @TomsonTom I don't understand the question. Are you asking if `std::move` copies? Sure there are problems, your `function` shares its `bar2` state with all copies of itself. `dispatch` should really take either a move-only function type-erasure object, or if it is synchronous it should take a function-view type-erasure object. But you have to code around bad APIs some time. – Yakk - Adam Nevraumont Oct 26 '18 at 19:06
  • Thanks, I have decided to accept your answer as it helped me understand the problems. I am using [asio](https://github.com/chriskohlhoff/asio) and I have just found out that this is [a common problem](https://stackoverflow.com/questions/17211263/how-to-trick-boostasio-to-allow-move-only-handlers/) and people are doing [nasty tricks](https://stackoverflow.com/questions/17211263/how-to-trick-boostasio-to-allow-move-only-handlers/22891509#22891509) to compile their codes with unique pointers. – TomsonTom Oct 26 '18 at 19:24