I am implementing a work stealing algorithm and am writing a generic function wrapper that takes a promise as one of the variadic arguments to the wrapper template. I want to create tasks with these function wrappers and get each node to communicate to dependent nodes using promises. Each node maintains a list of dependent nodes and promises/futures. Each node can run by checking if all futures have been set. promises can vary depending on the job the function wrapper is doing returning different objects. If a single algorithm can be broken into separate operations like read message and decode message, perform checks on object, return result of all checks, each of these actions will return a different promise (object, boolean, result).
The book, C++ Concurrency in Action, has a function wrapper implementation however, that doesn't handle this use case. In other references online, I've seen hardcoded references to promises like std::promise which is only one type.
Can someone advise how I can write a wrapper to achieve the following...
void add(int a, int b, std::promise<int>&& prms)
{
int res = a + b;
try {
prms.set_value(res);
}
catch(...)
{
prms.set_exception(std::current_exception());
}
}
int main()
{
std::promise<int> prms;
std::future<int> fut = prms.get_future();
FunctionWrapper myFunctor(a, 10, 20, std::move(prms));
// add the functor to the queue and it will be retrieved by a thread
// that executes the task. since i have the future, i can pass it to the
// dependent worknode
}
I tried writing the code like below...but was facing difficulties in getting this to work.
#ifndef FUNCTIONWRAPPER_HPP
#define FUNCTIONWRAPPER_HPP
template<typename F, typename R, typename... Args>
class FunctionWrapper
{
class implbase
{
public:
virtual ~implbase();
virtual R execute(Args...)=0;
};
class impl : public implbase
{
public:
impl(F&& f) : func(std::move(f)) {}
virtual R execute(Args... args) { return func(args...); }
private:
F func;
};
std::shared_ptr<impl> internalFunc;
public:
FunctionWrapper(F&& f) : internalFunc(0)
{
internalFunc = new impl<F, R, Args...>(f);
}
FunctionWrapper(const FunctionWrapper& other)
: internalFunc(std::move(other.internalFunc))
{}
~FunctionWrapper()
{
if(internalFunc)
delete internalFunc;
}
R operator()(Args... args)
{
return internalFunc->execute(args...);
}
void swap(FunctionWrapper& other)
{
impl<R, Args...>* tmp = internalFunc;
internalFunc = other.internalFunc;
other.internalFunc = tmp;
}
FunctionWrapper& operator=(const FunctionWrapper& other)
{
FunctionWrapper(other).swap(*this);
return *this;
}
FunctionWrapper& operator=(const F& f)
{
FunctionWrapper(f).swap(*this);
return *this;
}
};
#endif // FUNCTIONWRAPPER_HPP
C++11 has a wrapper for doing just this! It is called packaged_task.
What it does, is it wraps a callable object (function objects, lambdas, function pointers, bind expressions, etc...) and provides you a future via the get_future() method that matches the return type of the function passed in.
Consider the following example:
#include <thread>
#include <future>
#include <functional>
#include <iostream>
using namespace std;
int add(int a, int b)
{
return a + b;
}
int main()
{
// Create a std::packaged_task and grab the future out of it.
packaged_task<int()> myTask(bind(add, 10, 20));
future<int> myFuture = myTask.get_future();
// Here, is where you would queue up the task in your example.
// I'll launch it on another thread just to demonstrate how.
thread myThread(std::move(myTask));
myThread.detach();
// myFuture.get() will block until the task completes.
// ...or throw if the task throws an exception.
cout << "The result is: " << myFuture.get() << endl;
return 0;
}
As you can see, rather than passing in a promise, we are counting on the packaged_task to create the promise and give us the future.
Also, using a bind expression has allowed us to effectively hand arguments to the task to hold onto until it is called.
Using packaged_task also puts the burden pushing exceptions through the future into the hands of the packaged_task. That way, your functions do not need to call set_exception(). They only have to return or throw.
Related
I'm working on a code where I can bind events and callbacks to react to those events, the interface looks like this:
void on_close();
struct S
{
void the_app_is_closing();
};
S s;
Events::Register(app::CLOSE, on_close);
Events::Register(app::CLOSE, s, &S::the_app_is_closing);
...
...
if (/* something happens */)
Events::Broadcast(app::CLOSE);
Internally it keeps a container which associates an enum value identifying an event with all the functions expected to react to that event. Those functions are kept into an object which can hold free functions or member functions and feeds the functions through a template function (apply) that forwards the parameters:
class callback
{
struct base {};
template <typename ... params_pack>
struct callable : public base
{
callable(void(*a_function)(params_pack ...)) :
m_call{a_function}
{}
template <typename listener_t>
callable(listener_t &a_listener, void(listener_t:: *a_function)(params_pack ...)) :
m_call{[&a_listener, &a_function](params_pack ... a_argument)
{
(a_listener.*a_function)(a_argument ...);
}}
{}
std::function<void(params_pack ...)> m_call;
};
template <typename ... params_pack>
auto build(void(*a_function)(params_pack ...))
{
return std::make_unique<callable<params_pack ...>>(a_function);
}
template <typename listener_t, typename ... params_pack>
auto build(listener_t &a_listener, void(listener_t:: *a_function)(params_pack ...))
{
return std::make_unique<callable<params_pack ...>>(a_listener, a_function);
}
std::unique_ptr<base> m_function{nullptr};
public:
template <typename function_t>
callback(function_t a_function) :
m_function{build(a_function)}
{}
template <typename listener_t, typename function_t>
callback(listener_t &a_listener, function_t a_function) :
m_function{build(a_listener, a_function)}
{}
template <typename ... params_pack>
void apply(params_pack ... a_argument) const
{
if (auto &call = *static_cast<callable<params_pack ...> *>(m_function.get());
std::is_invocable_v<decltype(call.m_call), params_pack ...>)
{
call.m_call(a_argument ...);
}
}
};
I have an important bug on that apply function that can be reproduced with this code:
void string_parameter(const std::string &s) { std::cout << s << '\n'; }
void long_parameter(long l) { std::cout << l << '\n'; }
int main()
{
callback l(long_parameter);
callback s(string_parameter);
l.apply(123);
s.apply("Test");
return 0;
}
Even if you can call string_parameter directly with a literal string and long_parameter directly with a literal integer, doing the call through callback::apply messes everything up. I know why it is happening:
I'm static_casting callback::callable<const std::string &> to callback::callable<const char *>.
Then the callable::m_call which underlying type is std::function<const std::string &> thinks it is std::function<const char *>.
The callable::m_call receives a literal string but is reinterpreted as std::string during the std::function call, creating the mess.
Same story with long and int.
The solution would be to save the parameter pack used on construction in order to use it inside apply:
template <typename function_t>
callback(function_t a_function) :
m_function{build(a_function)}
{ PARAMETERS = function_t.parameters } // ???
template <typename listener_t, typename function_t>
callback(listener_t &a_listener, function_t a_function) :
m_function{build(a_listener, a_function)}
{ PARAMETERS = function_t.parameters } // ???
...
...
template <typename ... params_pack>
void apply(params_pack ... a_argument) const
{
// Saved parameters --> vvvvvvvvvvvvvv
if (auto &call = *static_cast<callable<PARAMETERS ...> *>(m_function.get());
std::is_invocable_v<decltype(call.m_call), params_pack ...>)
{
call.m_call(a_argument ...);
}
}
But I don't know if this is even possible. Any advise?
Thanks!
tl;dr:
Completely abstracting away the signature of the function AND still calling it in a type-safe way is impossible in C++
A type-based event system could be a good alternative
1. Why it's impossible to do what you're asking for
1.1 How Type-Erasure works
Type-erasure is fundamentally based on polymorphism. By defining a set of methods that all objects we want to store have in common (the interface) we don't need to know the actual type we're dealing with.
There is no way to do type-erasure without involving polymorphism.
For example, a very crude implementation of std::function could look like this:
template<class RetVal, class... Args>
class function {
public:
template<class U>
function(U u) : ptr(new impl<U>(u)) {}
~function() { delete ptr; }
RetVal operator()(Args... args) {
return ptr->call(args...);
}
private:
struct base {
virtual ~base() = default;
virtual RetVal call(Args... args) = 0;
};
template<class T>
struct impl : base {
impl(T t): t(t) {}
RetVal call(Args... args) override {
return t(args...);
}
private:
T t;
};
base* ptr;
};
template<class RetVal, class... Args>
class function<RetVal(Args...)> : public function<RetVal, Args...> {};
godbolt example
This is how std::function accomplishes to store any function object that is compatible with it's signature - it declares an interface (base) that will be used by all function objects (impl).
The interface only consists of 2 functions in this case:
The destructor (we need to know how to properly cleanup the function object)
The call() function (for invoking the actual function)
Sidenote 1: A real std::function implementation would need a couple more interface functions, e.g. for copying / moving the callable
Sidenote 2: Your existing implementation has a small bug: struct base MUST have a virtual destructor, otherwise the destructor of struct callable would never be called, resulting in undefined behaviour.
1.2 How your callable would need to work
What you want is an object that completely erases both the function object AND the parameters that you pass.
But what should your interface then look like?
struct base {
virtual ~base() = default;
virtual ??? call(???); // how should this work?
};
This is the underlying problem you're facing - it's impossible to define an interface for your callable - because you don't know what the arguments are gonna be.
This is what #Yakk - Adam Nevraumont implied with "non-uniform" objects - there is no definition of call() that can handle all potential function types.
1.3 Options
So at that point you basically have two options:
Don't erase the function type (like #Yakk - Adam Nevraumont suggested)
Sacrifice compile-time type safety and maintainability by creating an interface that can deal with arbitrary function types
The latter option is what your code currently uses - either the function parameters match or your code has undefined behaviour.
A few other ways to implement it that don't rely on undefined behaviour could be:
Add an interface function for each possible argument combination
struct base {
/* ... */
// All possible ways a `callable` could potentially be invoked
virtual void call(int val0) { throw std::exception("invalid call"); };
virtual void call(std::string val0) { throw std::exception("invalid call"); };
virtual void call(const char* val0) { throw std::exception("invalid call"); };
virtual void call(int val0, std::string val1) { throw std::exception("invalid call"); };
virtual void call(int val0, const char* val1) { throw std::exception("invalid call"); };
// etc...
}
// then implement the ones that are sensible
struct callable<std::string> : public base {
/* ... */
void call(std::string val0) override { /* ... */ }
void call(const char* val0) override { /* ... */ }
}
This obviously gets out of hand rather quickly.
"Accept anything" interface
struct base {
/* ... */
virtual void call(std::any* arr, int length);
};
// then implement the ones that are sensible
struct callable<std::string> : public base {
/* ... */
void call(std::any* arr, int length) override {
if(length != 1) throw new std::exception("invalid arg count");
// will throw if first argument is not a std::string
std::string& value = std::any_cast<std::string&>(arr[0]);
/* ... */
}
};
A bit better, but still looses compile-time type safety.
1.4 Conclusion
Compile-time type-safety with type-erasure is only possible if there is an uniform interface for all possible objects.
It is technically possible to type-erase non-uniform objects, but if you do that you'll loose compile-time type-safety (and need to do those checks at runtime instead)
2. Another Approach: Type-Based Event System
I'd like to propose a different way to handle the events that allows you to have arbitrary events without having to hard-code them into your Events class.
2.1 Basic Functionality
The main idea of this implementation is to have a class for each event you'd want to have that contains the parameters for the given event, e.g.:
struct AppClosingEvent {
const std::string message;
const int exitCode;
};
struct BananaPeeledEvent {
const std::shared_ptr<Banana> banana;
const std::shared_ptr<Person> peeler;
};
// etc...
This would then allow you to use the type of the event struct as a key for your event listeners.
A very simple implementation of this event system could look like this: (ignoring unregistration for now)
class EventBus {
private:
using EventMap = std::multimap<std::type_index, std::function<void(void*)>>;
// Adds an event listener for a specific event
template<class EvtCls, class Callable>
requires std::is_invocable_v<Callable, EvtCls&>
inline void Register(Callable&& callable) {
callbacks.emplace(
typeid(EvtCls),
[cb = std::forward<Callable>(callable)](void* evt) {
cb(*static_cast<EvtCls*>(evt));
}
);
}
// Broadcasts the given event to all registered event listeners
template<class EvtCls>
inline void Broadcast(EvtCls& evt) {
auto [first, last] = callbacks.equal_range(typeid(EvtCls));
for(auto it = first; it != last; ++it)
(it->second)(&evt);
}
private:
EventMap callbacks;
};
Register() takes a callable object that needs to be invocable with the given event type. Then it type-erases the callable so we can store it as a std::function<void(void*>
Broadcast(evt) looks up all event listeners that are registered based on the type of the event object and calls them.
Example Usage would look like this:
EventBus bus;
bus.Register<AppClosingEvent>([](AppClosingEvent& evt) {
std::cout << "App is closing! Message: " << evt.message << std::endl;
});
bus.Register<BananaPeeledEvent>([](BananaPeeledEvent& evt) {
// TODO: Handle banana peeling
});
AppClosingEvent evt{"Shutting down", 0};
bus.Broadcast(evt);
By using the type of the event as the key both Register() and Broadcast() are completely type-safe - it's impossible to register a function with incompatible function arguments.
Additionally the EventBus class doesn't need to know anything about the events it'll handle - adding a new event is as simple as defining a new class with the members you need for your event.
2.2 Adding the ability to unregister an event listener
I chose to use a multimap in this case because they guarantee to not invalidate iterators, unless the element the iterator points to itself gets removed from the multimap - which allows us to use a multimap iterator as the registration token for the event handler.
Full implementation: godbolt example
/*
EventBus - allows you to register listeners for arbitrary events via `.Register()`
and then later invoke all registered listeners for an event type with `.Broadcast()`.
Events are passed as lvalues, to allow event handlers to interact with the event, if required.
*/
class EventBus {
private:
using EventMap = std::multimap<std::type_index, std::function<void(void*)>>;
public:
/*
Represents a registered event handler on the EventBus.
Works a lot like std::unique_ptr (it is movable but not copyable)
Will automatically unregister the associated event handler on destruction.
You can call `.disconnect()` to unregister the event handler manually.
*/
class Connection {
private:
friend class EventBus;
// Internal constructor used by EventBus::Register
inline Connection(EventBus& bus, EventMap::iterator it) : bus(&bus), it(it) { }
public:
inline Connection() : bus(nullptr), it() {}
// not copyable
inline Connection(Connection const&) = delete;
inline Connection& operator=(Connection const&) = delete;
// but movable
inline Connection(Connection&& other)
: bus(other.bus), it(other.it) {
other.detach();
}
inline Connection& operator=(Connection&& other) {
if(this != &other) {
disconnect();
bus = other.bus;
it = other.it;
other.detach();
}
return *this;
}
inline ~Connection() {
disconnect();
}
// Allows to manually unregister the associated event handler
inline void disconnect() {
if(bus) {
bus->callbacks.erase(it);
detach();
}
}
// Releases the associated event handler without unregistering
// Warning: After calling this method it becomes impossible to unregister
// the associated event handler.
inline void detach() {
bus = nullptr;
it = {};
}
private:
EventBus* bus;
EventMap::iterator it;
};
// Adds an event listener for a specific event
template<class EvtCls, class Callable>
requires std::is_invocable_v<Callable, EvtCls&>
inline Connection Register(Callable&& callable) {
auto it = callbacks.emplace(
typeid(EvtCls),
[cb = std::forward<Callable>(callable)](void* evt) {
cb(*static_cast<EvtCls*>(evt));
}
);
return { *this, it };
}
// Broadcasts the given event to all registered event listeners
template<class EvtCls>
inline void Broadcast(EvtCls& evt) {
auto [first, last] = callbacks.equal_range(typeid(EvtCls));
for(auto it = first; it != last;)
(it++)->second(&evt);
}
private:
EventMap callbacks;
};
With this you can easily register listeners and unregister them later (e.g. if the class they're bound to gets destructed)
Example:
struct DispenseNachosEvent {};
struct DispenseCheeseEvent {};
class NachoMachine {
public:
NachoMachine(EventBus& bus) {
// register using std::bind
nachoEvent = bus.Register<DispenseNachosEvent>(
std::bind(
&NachoMachine::OnDispenseNachos,
this,
std::placeholders::_1
)
);
// register with lambda
cheeseEvent = bus.Register<DispenseCheeseEvent>(
[&](DispenseCheeseEvent& evt) {
OnDispenseCheese(evt);
}
);
}
// Default destructor will automatically
// disconnect both event listeners
private:
void OnDispenseNachos(DispenseNachosEvent&) {
std::cout << "Dispensing Nachos..." << std::endl;
}
void OnDispenseCheese(DispenseCheeseEvent&) {
std::cout << "Dispensing Cheese..." << std::endl;
}
private:
EventBus::Connection nachoEvent;
EventBus::Connection cheeseEvent;
};
2.3 Other benefits
If you want you can also allow the event handlers to modify the event object - e.g. cancel it - which allows you to return state to the piece of code that called Broadcast()
Example:
struct CancelableExampleEvent {
inline void Cancel() { isCancelled = true; }
inline bool IsCancelled() { return isCancelled; }
CancelableExampleEvent(std::string message) : message(message) {}
const std::string message;
private:
bool isCancelled = false;
};
// Usage:
CancelableExampleEvent evt;
bus.Broadcast(evt);
if(!evt.IsCancelled()) {
// TODO: Do something
}
Event Handlers can remove themselves - this is usually tricky to implement due to iterators being invalidated, but with multimaps it's rather easy to implement:
template<class EvtCls>
inline void Broadcast(EvtCls& evt) {
auto [first, last] = callbacks.equal_range(typeid(EvtCls));
for(auto it = first; it != last;)
(it++)->second(&evt);
}
By incrementing it before calling the function we make sure that it remains valid, even if the event handler chooses to unregister itself as part of its callback.
e.g. this would work:
EventBus::Connection con;
con = bus.Register<SomeEvent>([&con](SomeEvent&){
std::cout << "Received event once!" << std::endl;
con.disconnect();
});
2.4 Try it online!
Here's a godbolt that contains the entire code of this post to try it out.
This is your problem:
class callback
it should be
template<class...Args>
class callback
because you have to think about what happens when the types do not match
void string_parameter(const std::string &s) { std::cout << s << '\n'; }
void long_parameter(long l) { std::cout << l << '\n'; }
callback<long> l(long_parameter);
callback<std::string> s(string_parameter);
l.apply(123);
s.apply("Test");
which works flawlessly.
Now you run into the problem of a central enum for all callbacks.
Events::Register(app::CLOSE, on_close);
Events::Register(app::CLOSE, s, &S::the_app_is_closing);
The problem is that all use of app::CLOSE must know what the signature of the callback must be. The code registering it must know, and the code invoking the callback must know.
Your design, however, carefully forgets this fact, and forces type unsafety at both ends. Then you add so,e template code in the middle to ferry types around... which even if it did work, would be work for no good reason.
template<app::event e>
void Events::Register(event_sig<e>* pf);
template<app::event e, class T>
void Events::Register(T* pt, event_mem_sig<T,e>* pf);
template<app::event e, class...Ts>
void Event::Broadcast(Ts&&....ts);
here we have a more sensible API. The event type is compile time value, so we can do type checking, and store the event callbacks in a type safe list.
...
Now, if you have a reasonably bounded number of events (ie, not 1000s of which under 1% are subscribed to), an even simpler solution is to make an event queue an actual object, instead of an enum and traits.
using token=std::shared_ptr<void>;
template<class...Args>
struct broadcaster {
size_t broadcast(Ts...ts)const;
token subscribe(std::function<void(Ts...)>);
void unsafe_subscribe(void(*)(Ts...));
// IMPLEMENTATION
};
now your code becomes
struct Events {
broadcaster<> appClosing;
};
Events g_events;
struct S
{
void the_app_is_closing();
token listening;
};
S s;
s.listening=g_events.appClosing.subscribe(&s, &S::the_app_is_closing);
g_events.appClosing.unsafe_subscribe(on_close);
g_events.appClosing.broadcast();
The types of the arguments are now tied to the appClosing object, so it is checked at both sibscription and at broadcast, conversion is done automatically.
Here each broadcaster maintains its own listener queue (hence bit above about "1000s of event types most unused). Extra work can be done to reduce the queue storage and share it, but that should onlh be done if you need it. And you probably won't.
The enum solution seems like it reduces duplication, but uniform lists of things with non uniform types are often a sign your list shoudln't be uniform.
Members of a struct are a fine way to list non uniform things. Having them be generated from a template means there isn't code writing duplication. And identical signature broadcasters will share binary implementations, somit isn't inefficient.
This question might be naïve, if so please forgive me as I am learning.
Please consider the following C++-like pseudo code:
#include <iostream>
#include <string>
#include <functional>
struct A{
bool doSomething(int in){
std::cout<<"SOMETHING WAS DONE THAT DAY: "<<in<<std::endl;
return true;
}
};
struct B{
std::function executor;
void setExecutor(std::function f){
executor=f;
}
bool doSomethingLater(int in){
return executor(in);
}
};
static bool yoloFunction(int in){
std::cout<<"LIVING LA VIDA LOCA: "<<in<<std::endl;
return false;
}
int main()
{
A myExecutor;
B lazyAss;
// Storing member function for later invocation
lazyAss.setExecutor(&myExecutor::doSomething);
bool result_1 = lazyAss.doSomethingLater(1337);
std::cout<<"The first result is "<<result_1<<std::endl;
// Storing regular function for later invocation
lazyAss.setExecutor(&yoloFunction);
bool result_3 = lazyAss.doSomethingLater(42);
std::cout<<"The second result is "<<result_2<<std::endl;
// Storing lambda function for later invocation
lazyAss.setExecutor([=](int in){
std::cout<<"P- P- P- POKERFACE!: "<<in<<std::endl;
return true;
});
bool result_3 = lazyAss.doSomethingLater(666);
std::cout<<"The third result is "<<result_3<<std::endl;
}
It should illustrate what I am trying to accomplish, namely to store a callback of some kind for later invocation.
So how would I go about doing this in modern C++ in the simplest way possible, without using boost or other non-standard dependencies?
What options and caveats are there?
Things to note are that the signature of the callback is known in advance and will not change.
Also the callback I want to store may be of different forms like stand alone function, member function or lambda.
Modify struct B like this:
struct B {
std::function<bool(int)> executor;
void setExecutor(std::function<bool(int)> f) {
executor = f;
}
bool doSomethingLater(int in) {
return executor(in);
}
};
In main(), use a lambda to pass the member function as parameter like this:
lazyAss.setExecutor([&](int i)->bool { return myExecutor.doSomething(i); });
I have three methods and all of them returns a string, I want to run all of them using QtConcurrent and get their return into a single list or something like that. QtConcurrent::mapped is ideal as it returns an iterator but I can only run one method at time.
In JavaScript there's promise.all([method_a, method_b, method_c]), it will automatically merge their return into a single result (iterator).
How to do that in Qt?
Since you have several methods to call, you can pass them as a sequence of functors as the first argument to QtConcurrent::mapped. The mapping functor would be an apply functor that takes a functor representing the method call and returns the result of invoking it.
First, let's have our class:
// https://github.com/KubaO/stackoverflown/tree/master/questions/concurrent-combine-49802153
#include <QtConcurrent>
#include <functional>
#include <initializer_list>
#include <type_traits>
class Cls {
public:
QString method1() const { return QStringLiteral("10"); }
QString method2() const { return QStringLiteral("20"); }
QString method3() const { return QStringLiteral("30"); }
};
The apply_t functor invokes the method passed to it as an argument:
template <class Method> struct apply_t {
using result_type = typename std::result_of_t<Method()>;
auto operator()(Method method) {
return method();
}
};
Let's make it convenient to make such applicators from the type of a sequence of functors to be called:
template <class Sequence, class A = apply_t<typename std::decay_t<Sequence>::value_type>>
A make_apply(Sequence &&) { return {}; }
For convenience, we'll also have a vector generator in the spirit of e.g. make_unique, etc.:
template <class T> QVector<T> make_vector(std::initializer_list<T> init) {
return {init};
}
Then, the problem becomes fairly simple. First, we make a vector of bound methods that will be called. Then we pass the methods to call, as well as the applicator to operate on them, to QtConcurrent::mapped. The results() gives a list of all results of the method calls, in sequence.
int main() {
Cls obj;
auto const methods = make_vector({
std::bind(&Cls::method1, &obj),
std::bind(&Cls::method2, &obj),
std::bind(&Cls::method3, &obj)
});
QFuture<QString> result =
QtConcurrent::mapped(methods, make_apply(methods));
Q_ASSERT((result.results() == QStringList{"10", "20", "30"}));
}
Instead of making a custom apply_t class, we could use a lambda, wrapped to provide the result_type member type that QtConcurrent::mapped expects. See this answer for details of wrapping the lambda. The rest of this answer provides examples of such wrapping.
Since there's no built in way to do that, you can work out a class on your own to keep futures together and return a collection of results when all tasks completed. The only limitation, here, is due to the strongly typed nature of c++: each future returned by QtConcurrent::run holds the called function result, the type of which is given at compile time, as the QFuture template parameter. What if the return types of the called functions differ from each other? In the example I provide, they all return the same type, but I think one could use QVariant for that mean and get away with it.
In a promise.h:
#ifndef PROMISE_H
#define PROMISE_H
#include <QtConcurrent/QtConcurrentRun>
#include <QFutureWatcher>
class PromiseInterface
{
public:
virtual ~PromiseInterface() = default;
virtual void finished(int id) = 0;
};
class Watcher : public QObject
{
Q_OBJECT
int _id;
PromiseInterface * _promise;
public slots:
void finished()
{
_promise->finished(_id);
deleteLater();
}
public:
Watcher(int id, PromiseInterface * promise)
: _id(id),
_promise(promise)
{}
};
template <typename T>
class Promise : public PromiseInterface
{
friend class Watcher;
void finished(int id) override
{
_resolved++;
_results[id] = _watchers[id]->result();
delete _watchers[id];
if(_resolved == _results.size())
{
if(_callback != nullptr)
{
_callback(_results);
}
}
}
QList<QFutureWatcher<T> *> _watchers;
QVector<T> _results;
void (*_callback)(QVector<T>);
int _resolved;
public:
Promise(QList<QFuture<T>> futures)
{
_resolved = 0;
_callback = nullptr;
_results.resize(futures.size());
int i=0;
for(auto f : futures)
{
QFutureWatcher<T> * watcher = new QFutureWatcher<T>();
watcher->setFuture(f);
QObject::connect(watcher, &QFutureWatcher<T>::finished, new Watcher(i++, this), &Watcher::finished);
_watchers.append(watcher);
}
}
void then(void (*callback)(QVector<T>)) { _callback = callback; }
};
#endif // PROMISE_H
The Promise class is a class template with a single template parameter that matches the one of QFuture. The observed futures are passed in the constructor, while the then method accepts a the completion callback as its only argument.
The Watcher class provides a slot to catch QFutureWatcher::finished' signals. Each instance knows the promise object through a pointer to itsPromiseInterfaceand will callfinished` from the slot, passing in the id of the future that completed.
When all futures finished, the callback function is called with the vector of results passed in.
In a very simple usage example, we can execute this function concurrently:
#include <unistd.h>
int f(int r) { sleep(1); return r;}
and pass this callback to the promise then:
void callback(QVector<int> results)
{
qDebug() << results;
}
Our main:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QList<QFuture<int>> futures = {
QtConcurrent::run(&f, 1),
QtConcurrent::run(&f, 2),
QtConcurrent::run(&f, 3)
};
Promise<int> promise(futures);
promise.then(callback);
return a.exec();
}
After about a second, this is the expected output:
QVector(1, 2, 3)
Just in case one wonders why I put in three classes, instead of making Promise extend QObject directly, and implement the finished slot itself: Qt is not letting me do that. When the Q_OBJECT macro is added to a class template, an explicit compiler error will prompt: Template classes not supported by Q_OBJECT.
The method you're looking for is QFuture::results():
QList<T> QFuture::results() const
Returns all results from the future. If the results are not
immediately available, this function will block and wait for them to
become available.
Expanding from Qt's own QtConcurrent::mapped example:
QImage scaled(const QImage &image)
{
return image.scaled(100, 100);
}
QList<QImage> images = ...;
QList<QImage> thumbnails = QtConcurrent::mapped(images, scaled).results();
I know AsyncFuture is a c++ library that converts calls into a QFuture type and uses it like a Promise object in Javascript (Combine multiple futures with different type into a single future object). unfortunately i never used it! but there are details on this reference Qt Blog Multithreaded Programming with Future & Promise
I want to implement a manager that stores callbacks to member functions of polymorphic classes using C++11. The issue is that I am not sure how to handle the case where the object that the member belongs to gets deleted or should be deleted and I want to make the interface as simple as possible.
So I thought of the following: Store a std::weak_ptr to the object as well as a std::function to the member.
The following seems to work:
class MyBase {
public:
MyBase() {}
virtual ~MyBase() {}
};
//--------------------------------------------------
class MyClass : public MyBase {
public:
MyClass() : MyBase() {}
void myDouble(double val) const { std::cout << "Value is: " << val << std::endl; }
};
//--------------------------------------------------
Class Manager {
public:
void setFunction(std::weak_ptr<MyBase> base, std::function<void(double)> func) {
m_function.first = base;
m_function.second = func;
}
private:
std::pair<std::weak_ptr<MyBase>, std::function<void(double)>> m_function;
};
To use this:
Manager db;
std::shared_ptr<MyClass> myClass = std::make_shared<MyClass>();
db.setFunction(myClass, std::bind(&MyClass::myDouble, myClass, std::placeholders::_1));
Now I want to hide the std::bind part from the user, so that he only needs to call:
db.setFunction(myClass, &MyClass::myDouble);
So I want to get almost the following working in my manager function:
void setFunc2(std::weak_ptr<MyBase> base, std::function<void(double)> func) {
m_function.first = base;
m_function.second = std::bind(func, base, std::placeholders::_1);
}
But the above gives errors:
error: no match for 'operator=' (operand types are 'std::function<void(double)>' and
'std::_Bind_helper<false, std::function<void(double)>&, std::weak_ptr<MyBase>&, const std::_Placeholder<1>&>::type {aka std::_Bind<std::function<void(double)>(std::weak_ptr<MyBase>, std::_Placeholder<1>)>}')
m_function.second = std::bind(func, base, std::placeholders::_1);
Is there a better way to do this, or perhaps a way to get this working?
Something interesting that I notice. If I use the std::shared_ptr the use_count() gets incremented with the call to std::bind in the original code. Thus I can not manually reset/destroy the object unless I unset the member on my manager. Where is this behaviour documented, I normally use cppreference?
I have looked at the following question but can't seem to get it working for my problem: How can I use polymorphism with std::function?
Template setFunction so that you can accept pointer-to-member-of-derived, and don't have to write 12 overloads for the combinations of cv/ref qualifiers.
template<class D, class D2, class F>
void setFunction(const std::shared_ptr<D> &sp, F D2::* member) {
// optionally static_assert that D2 is a base of D.
m_function.first = sp;
m_function.second = std::bind(member, sp.get(), std::placeholders::_1);
}
Obviously you need to make sure you lock() m_function.first before calling m_function.second.
Alternatively, just use a lambda that captures both the weak_ptr and the member function pointer:
std::function<void(double)> m_function;
template<class D, class D2, class F>
void setFunction(const std::shared_ptr<D> &sp, F D2::* member) {
std::weak_ptr<D> wp = sp;
m_function = [wp, member](double d) {
if(auto sp = wp.lock()){
((*sp).*member)(d);
}
else {
// handle pointer no longer valid case.
}
};
}
I like decoupling my listener/broadcaster from the implementation of the listener.
This means I cannot place requirements on the listener. It cannot require the listener be allocated in a particular way.
The easiest method I have found is to have the broadcaster return a token whose lifetime determines the lifetime of the connection.
using token = std::shared_ptr<void>;
template<class...Args>
struct broadcaster {
using target = std::function<void(Args...)>;
using wp_target = std::weak_ptr<target>;
using sp_target = std::shared_ptr<target>;
static sp_target wrap_target( target t ) {
return std::make_shared<target>(std::move(t));
};
token start_to_listen( target f ) {
auto t = wrap_target(std::move(f));
targets.push_back(t);
return t;
}
void broadcast( Args... args ) {
targets.erase(
std::remove_if( targets.begin(), targets.end(),
[&]( wp_target t )->bool { return t.expired(); }
),
targets.end()
);
auto targets_copy = targets; // in case targets is modified by listeners
for (auto wp : targets_copy) {
if (auto sp = wp.lock()) {
(*sp)(args...);
}
}
}
std::vector<wp_target> targets;
};
this forces people who register listeners to keep std::shared_ptr<void> around.
We can even make it fancier, where the destruction of the last shared_ptr<void> actually removes the listener from the list immediately. But the above lazy deregistration seems to work reasonably well in my experience, and it is relatively easy to make it multi-thread friendly. (one serious problem is what happens when a broadcast event removes or adds things to the list of listeners: adapting the above for it to work is nice and easy with the rule that listeners added when broadcasting do not get the broadcast, and listeners removed during broadcasting do not get the broadcast. Listeners removed concurrently during broadcast can get the broadcast in most of my implementations... That gets expensive to avoid.)
We could instead decouple it differently. The listener could pass a std::function and a std::weak_ptr separately to the broadcaster, who stores both and only calls the std::function if the std::weak_ptr is valid.
I like Yakk's approach. Here's an updated version that fixes a few compile issues (e.g. cannot name function 'register'). It also adds a rm_callback method for clients to easily remove their registration without forcing their registration token to go out of scope or knowing the internals. I didn't like scanning the list every time an event was broadcast so I added a deleter on the shared pointer which does the cleanup task. All new bugs introduced or inefficiencies are mine. The alert reader should be aware of threading issues when modifying the list while broadcasting...
using token = std::shared_ptr<void>;
template<class...Args>
struct broadcaster {
using target = std::function<void(Args...)>;
using wp_target = std::weak_ptr<target>;
using sp_target = std::shared_ptr<target>;
token add_callback(target f) {
sp_target t(new target(std::move(f)), [&](target*obj) { delete obj; cleanup(); });
targets.push_back(t);
return t;
}
static void rm_callback(token& t)
{
t.reset();
}
void cleanup()
{
targets.erase(
std::remove_if(targets.begin(), targets.end(),
[](wp_target t) { return t.expired(); }
),
targets.end()
);
}
void broadcast(Args... args) {
for (auto wp : targets) {
if (auto sp = wp.lock()) {
(*sp)(args...);
}
}
}
std::vector<wp_target> targets;
};
// declare event taking a string arg
broadcaster<std::string> myEvent;
I have a functioning C++ callback function, triggered by a user 'mouse down' event. (The IDE is VS2010.)
With each call, I'd like to increment a simple count variable that is local to the callback's scope. Simply put, what is the 'best practices' way to do this?
Thanks in advance for any opinions or directives.
Replace your callback function with a functor - they can store state. An example functor:
#include <iostream>
#include <memory>
class Functor
{
private:
std::shared_ptr<int> m_count;
public:
Functor()
: m_count(new int(0))
{}
void operator()()
{
++(*m_count);
// do other stuff...
}
int count() const
{
return *m_count;
}
};
template <typename F>
void f(F callback)
{
// do stuff
callback();
// do other stuff
}
int main()
{
Functor callback;
f(callback);
f(callback);
std::cout << callback.count(); // prints 2
return 0;
}
Note the use of a shared_ptr inside the functor - this is because f has a local copy of the functor (note the pass-by-value) and you want that copy to share its int with the functor to which you have access. Note also that f has to take its argument by value, since you want to support all callables, and not just functors.