Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pretending packaged_task is copy constructable

I've been looking for a workaround to the problem of type-erasing a std::packaged_task using std::function.

What I wanted to do was something like this:

#include <future>
#include <functional>
#include <iostream>

namespace {
  std::function<void()> task;
  std::future<int> make(int val) {
    auto test = std::packaged_task<int()>([val](){
      return val;
    });

    auto fut = test.get_future();
    task = std::move(test);
    return fut;
  }
}

int main() {
   auto fut = make(100);
   task();
   std::cout << fut.get() << "\n";
}

it's succinct and avoids re-implementing a lot of mechanics myself. Unfortunately that isn't actually legal because std::packaged_task is move-only not copy constructable.

As a workaround I came up with the following, which implements things in terms of std::promise and a std::shared_ptr instead:

#include <future>
#include <functional>
#include <iostream>

namespace {
  std::function<void()> task;

  std::future<int> make(int val) {
    auto test = std::make_shared<std::promise<int>>();

    task = [test,val]() {
      test->set_value(val);
      test.reset(); // This is important
    };

    return test->get_future();
  }
}

int main() {
   auto fut = make(100);
   task();
   std::cout << fut.get() << "\n";
}

This "works for me", but is this actually correct code? Is there a nicer way to achieve the same net result?

(Note that the lifespan of the std::shared_ptr in the second example is important for my real code. Clearly as-is I will be taking steps to prevent calling the same std::function twice).

like image 616
Flexo Avatar asked Apr 22 '26 09:04

Flexo


1 Answers

Your problem seems to stem from type incompatabilities:

std::function<void()> task;  // Notice this is not a package_task

So why would you expect this to work?

task = std::move(test);  // when test is `std::packaged_task<int()>`

When I change task to the correct type it compiles as expected:

namespace {

  std::packaged_task<int()> task;          // Change this.

  std::future<int> make(int val) {
    auto test = std::packaged_task<int()>([val](){
      return val;
    });

    auto fut = test.get_future();
    task = std::move(test);              // This now compiles.
    return fut;
  }
}

Personally since type is important I would remove the auto from test.

namespace {

    std::packaged_task<int()> task;

    std::future<int> make(int val) {
        std::packaged_task<int()> test([val](){return val;});

        auto fut = test.get_future();
        task     = std::move(test);
        return fut;
    }
}
like image 114
Martin York Avatar answered Apr 24 '26 23:04

Martin York



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!