I tried to develop a pattern for this situation and I've come up with this code:
template<typename Functor>
class FinallyGuard
{
public:
FinallyGuard(Functor f) : _functor(std::move(f)), _active(true) {}
FinallyGuard(FinallyGuard&& other) : _functor(std::move(other._functor)), active(other.active) { other.active = false; }
~FinallyGuard() { if (_active) _functor(); }
void setActive(bool active = true) { _active = active; }
bool isActive() const { return _active; }
FinallyGuard& operator=(FinallyGuard&&) = delete;
FinallyGuard& operator=(const FinallyGuard&) = delete;
FinallyGuard() = delete;
private:
Functor _functor;
bool _active;
};
template<typename F> FinallyGuard<typename std::decay<F>::type> finally(F&& f) { return{ std::forward<F>(f) }; }
struct MessageWithTypeInfo : public Message
{
MessageWithTypeInfo(const String &messageType) : _messageType(messageType) {}
const String& getMessageType() const { return _messageType; }
MessageWithTypeInfo() = delete;
MessageWithTypeInfo(const MessageWithTypeInfo &) = delete;
MessageWithTypeInfo& operator= (const MessageWithTypeInfo &) = delete;
private:
String _messageType;
};
struct ThreadCompletionMessage : public MessageWithTypeInfo
{
ThreadCompletionMessage(const String& messageType, const Thread& thread) : MessageWithTypeInfo(messageType), _thread(thread) {}
const Thread& getThread() const { return _thread; }
ThreadCompletionMessage() = delete;
ThreadCompletionMessage(const MessageWithTypeInfo &) = delete;
ThreadCompletionMessage& operator= (const MessageWithTypeInfo &) = delete;
private:
const Thread &_thread;
};
class MyBackgroundWorker : public Thread
{
public:
MyBackgroundWorker(MessageListener* listener = nullptr, const String& name = String::empty) : Thread(name), _listener(listener){}
~MyBackgroundWorker() { delete _exceptionPtr; }
const std::exception *getException() const { return _exceptionPtr; }
bool isCancelled() const { return _cancelled; }
protected:
std::exception *_exceptionPtr{ nullptr };
bool _cancelled{ false };
MessageListener *_listener{ nullptr };
void run() override
{
auto threadExitGuard{ finally([&] { triggerThreadExitNotify(); }) };
try
{
std::cout << "Hello from thread!" << std::endl;
if (threadShouldExit()) { _cancelled = true; return; }
throw std::exception("exception");
}
catch (const std::exception& e) { _exceptionPtr = new std::exception(e); }
}
void triggerThreadExitNotify()
{
if (isCancelled())
std::cout << "thread was cancelled..." << std::endl;
else
{
if (getException() == nullptr)
std::cout << "result is ready to use..." << std::endl;
else
std::cout << "thread exited with exception: '" << getException()->what() << "'" << std::endl;
}
if (_listener != nullptr)
_listener->postMessage(new ThreadCompletionMessage("MyBackgroundWorker", *this));
}
};
Julian, I want to know is this the right way to use JUCE with threads?