I'm trying to construct a work queue of functions that need to be executed by one thread and can be fed by many threads. To accomplish this, I was planning on using the boost::packaged_task and boost::unique_future. The idea would be you would do:
Foo value = queue.add(myFunc).get();
which would block, until the function is executed. So queue.add(...) takes in a boost::function, and returns a boost::unique_future. Internally it then creates a boost::packaged_task using the boost::function for its constructor.
The problem I'm running into is that boost::function<...> won't be the same every time. Specifically, the return value for it will change (the functions, however, will never take any parameters). Thus, I have to have an add function that looks something like:
template <typename ResultType>
boost::unique_future<ResultType> add(boost::function<ResultType ()> f) {
boost::packaged_task<boost::function<ResultType ()> > task(f);
queue.push_back(task);
return task.get_future();
}
Okay, that doesn't seem too bad, but then I ran into the problem of how to define 'queue'. I think I have no choice but to use boost::any, since the types will not be constant:
std::list<boost::any> queue; // note: I'm not concerned with thread-safety yet
But then I run into a problem when I try to implement my executeSingle (takes just a single item off the queue to execute):
void executeSingle() {
boost::any value = queue.back();
boost::packaged_task<?> task = boost::packaged_task<?>(boost::move(value));
// actually execute task
task();
queue.pop_back();
}
The '?' denote what I'm unsure about. I can't call executeSingle with a template, as it's called from a separate thread. I tried using boost::any, but I get the error:
conversion from 'boost::any' to non-scalar type boost::detail::thread_move_t<boost:thread>' requested.
The funny part is, I actually don't care about the return type of packaged_task at this point, I just want to execute it, but I can figure out the template details.
Any insight would be greatly appreciated!
You should store
boost::function<void()>
's. Note thatboost::packaged_task<R>::operator()
doesn't return anything; it populates the associatedboost::future
. In fact, even if it returned something you could still useboost::function<void()>
since you'd still have no interest in the returned value: all you care about is to callqueue.back()()
. If this were the caseboost::function<void()>::operator()
would take care of discarding the returned value for you.As a minor note, you might want to change the signature of your
add
method to be templated on a generic typeFunctor
rather than aboost::function
, and useboost::result_of
to get the result type forboost::packaged_task
.My suggestion as a whole:
EDIT
How to take care of move-semantics inside
queue::add
where
dereference_functor
could be:You could also substitute the
bind
expression for the much clearerwhich also doesn't require a custom functor. However if there are multiple overloads of
task_type::operator()
this might need disambiguation; the code could also break if a future change in the Boost.Thread introduce an overload.