How to use boost::asio::deadline_timer with Qt? - c++

How could using boost::asio::deadline_timer with Qt ? I am new to Boost C++ library and try documentation examples in separate project and it work. But i could not using it in my project which use Qt for GUI.
Example of my problem
Note:
I Need a long interval and QTimer could not work with a long interval.

Based on an answer to your previous question it's very easy to implement your own deadline timer using Qt only (that seems to be what you'd prefer)...
class deadline_timer: public QObject {
using super = QObject;
Q_OBJECT;
public:
using deadline_id = uint_least64_t;
signals:
void timeout(deadline_id, QDateTime) const;
public:
explicit deadline_timer (int resolution_milliseconds = 1000, QObject *parent = nullptr)
: super(parent)
{
m_timer.setInterval(resolution_milliseconds);
QObject::connect(&m_timer, &QTimer::timeout, this, &deadline_timer::handle_timeout);
}
/*
* Adds a new deadline and returns associated id. When the deadline is
* reached the timeout signal will be emitted with the corresponding id
* and time.
*/
deadline_id start (const QDateTime &deadline)
{
m_deadlines[deadline].insert(++s_id);
if (!m_timer.isActive()) {
m_timer.start();
}
return s_id;
}
void stop ()
{
m_timer.stop();
}
private:
void handle_timeout ()
{
auto now = QDateTime::currentDateTime();
for (auto i = m_deadlines.begin(); i != m_deadlines.end() && now >= i->first; i = m_deadlines.begin()) {
for (const auto &j: i->second) {
emit timeout(j, i->first);
}
m_deadlines.erase(i);
}
if (m_deadlines.empty()) {
m_timer.stop();
}
}
static deadline_id s_id;
QTimer m_timer;
std::map<QDateTime, std::set<deadline_id>> m_deadlines;
};
deadline_timer::deadline_id deadline_timer::s_id = 0;
The use as...
deadline_timer dt;
QObject::connect(&dt, &deadline_timer::timeout,
[](deadline_timer::deadline_id id, QDateTime deadline)
{
std::cout << "\ndeadline " << id << " passed\n";
});
auto id = dt.start(QDateTime::currentDateTime().addYears(1));

Related

Why are emitted signals not received in multithreading?

I have a problem, that remaining sent signals are not received after calling quit on QThread object.
The scenario contains 2 additional threads (QThread and std::thread) and the main execution thread. Let's call the QThread Q, the std::thread T and the main thread M.
In M I create Q, the Receiver-object R "living" in Q and the Sender-object S. Also a std::thread T is created executing a bunch if emits with S.
class Sender : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Sent;
Sender()
{
}
public slots:
signals:
void signal(int i);
public:
void send(int i)
{
m_Sent.emplace_back(i);
emit signal(i);
}
};
class Receiver : public QObject
{
Q_OBJECT;
public:
std::vector<int> m_Received;
Receiver()
{
}
void Connect(Sender* s)
{
connect(s, &Sender::signal, this, &Receiver::slot, Qt::QueuedConnection);
}
void Disconnect(Sender* s)
{
disconnect(s, &Sender::signal, this, &Receiver::slot);
}
public slots:
void slot(int i)
{
m_Received.emplace_back(i);
}
};
void main(int argc, char** argv)
{
QApplication app(argc, argv);
qint64 random_seed = QDateTime::currentMSecsSinceEpoch();
std::cout << "Setting random seed " << random_seed << "\n";
std::srand(random_seed);
std::unique_ptr<Receiver> R(new Receiver);
std::unique_ptr<Sender> S(new Sender);
auto actions = [&S]() {
int i = 0;
std::chrono::steady_clock::time_point current =
std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point finish =
current + std::chrono::milliseconds(100);
while (current < finish)
{
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_for(std::chrono::microseconds(std::rand()%1000));
S->send(i++);
std::this_thread::sleep_until(current + std::chrono::milliseconds(17));
current = std::chrono::steady_clock::now();
}
};
std::unique_ptr<QThread> Q(new QThread());
R->moveToThread(Q.get());
R->Connect(S.get());
Q->start();
std::thread T(actions);
T.join();
// approach 1:
QMetaObject::invokeMethod(Q.get(), "quit", Qt::QueuedConnection);
Q->wait(); // never returns
// approach 2:
Q->quit();
Q->wait(); // missing events
std::cout << "Sent: ";
for(auto v : S->m_Sent)
{
std::cout << v << " ";
}
std::cout << std::endl;
std::cout << "Received: ";
for(auto v : R->m_Received)
{
std::cout << v << " ";
}
std::cout << std::endl;
}
I'm working on Windows with VS2013 and Qt 5.5.1. I tested it with kind of counter in R to track received signals. While debugging I went through all emits so all should be inserted to event loop in Q. After Q.wait() the counter for the slots do not correspond to the emitted signals. I would have expected the event loop with remaining input events was handled by Q.quit() or Q.wait() but seems not so, It's always that there is a cut of "event-stream" from a certain point onward. I tried now for 4 days going through Qt-Docu and several other stuff found by google, but no proposal worked so far.
I am not 100% sure since the documentation is not crystal clear, but what makes you think that the even loop is processing all pending events before exiting ? My assumption would be that there is a check "should I exit" at every loop and that it can discard some pending events when the exit flag is set.
In order to summarize the discussion below, I would suggest to add a new signal that you emit from wherever you want (say for instance from the std::thread once you have emitted everything you wanted) that would get into the QThread event loop queue and be connected to the QThread quit method so that the thread exits when being processed.
You can also avoid defining a new signal if you want.
Your code would look like (not tested):
Sender S = new Sender();
QThread Q = new QThread();
Receiver R = new Receiver();
R->moveToThread(Q);
connect(S, &Sender::signal, R, &Receiver::slot, Qt::QueuedConnection);
Q->start();
while(!Q.isRunning())
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::thread T([&S](){
emit S->signal(); // only an example, several other connects are used too
})
T.join();
QMetaObject::invokeMethod(Q, "quit",
Qt::QueuedConnection);
Q.wait();

Receiving LCM messages via signals/slots in C++ with Qt?

In my application, I am receiving messages from LCM (Lightweight Communications and Marshalling) that contain data for multiple consumers within the application. I imagined this working with the LCM handler as a singleton so that there is one instance that each class could use. For example, each consumer class would have:
QObject::connect(LCMHandler::getInstance(), SIGNAL(messageReceived()),
this, SLOT(processMessage()));
Where lcmhandler.h is:
class LCMHandler : public QObject
{
Q_OBJECT
public:
static LCMHandler* getInstance();
LCMHandler();
~LCMHandler() {}
void handleMessage(const lcm::ReceiveBuffer* rbuf,
const std::string &chan,
const example::example_t *msg);
signals:
void messageReceived();
private:
static LCMReceiver* _instance;
};
And lcmhandler.cpp is:
LCMHandler* LCMHandler::_instance = 0;
LCMHandler::LCMHandler()
{
lcm::LCM lcm;
if(lcm.good())
{
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
while(0 == lcm.handle());
} else {
std::cerr << "LCM Error" << std::endl;
}
}
LCMHandler* LCMHandler::getInstance() {
if (!_instance) {
_instance = new LCMHandler();
}
return _instance;
}
void LCMHandler::handleMessage(const lcm::ReceiveBuffer *rbuf,
const std::string &chan,
const hlelcm::transponder_t *msg)
{
std::cout << "Received message on channel " << chan.c_str() << std::endl;
emit messageReceived();
}
The application successfully prints "Received message on channel..." repeatedly; however, nothing else is executed, including code in the consumer class's processMessage(), presumably because the application gets stuck looping on handleMessage(...) and never executes the signal/slot procedure (or refreshes the UI components). So, if the implementation of processMessage() is:
void Consumer::processMessage() {
std::cout << "Message received" << std::endl;
}
It never executes, while handleMessage(...) loops infinitely. Similarly, the Qt UI never loads because handleMessage is busy looping.
What is the best way to handle the incoming messages? Should I refrain from using a singleton for LCMHandler? What do I need to change to make this implementation work?
Move the contents of your LCM constructor to another function:
LCMHandler::beginCommunication()
{
lcm::LCM lcm;
if(lcm.good())
{
//QObject base class call.
moveToThread( &_myLocalQThread );
_myLocalThread.start();
lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);
_isActive = true;
// This is blocking, we don't want it to interfere with
// the QApplication loop
while(0 == lcm.handle());
}
else
{
std::cerr << "LCM Error" << std::endl;
}
_isActive = false;
}
Then something along these lines to allow your LCM loop to happen in another thread.
auto lcmHandler = LCMHandler::getInstance();
// I like to be explicit about the Qt::QueuedConnection. Default behavior should be thread safe, though.
connect( lcmHandler, &LCMHandler::messageReceived,
this, &Consumer::processMessage, Qt::QueuedConnection );
// Add a std::atomic< bool > _isActive to LCMHandler
if( not lcmHandler.isActive() )
{
lcmHandler.beginCommunication();
}
And then make sure to properly close your QThread in the destructor.
LCMHandler::~LCMHandler()
{
_myLocalQThread.quit();
_myLocalQThread.wait();
}

How to delete boost io_service

My simplified question
I read this thread and I am trying to delete the io_service object. I do this
m_IO.stop();
m_IO.~io_service();
m_IO is an object of boost::asio::io_service. I found that my thread was blocked by m_IO.~io_service(); How can I delete io_service?
My Complete question
I am making a daily timer by using boost io_service and deadline timer. The problem is when I want to delete my daily timer, my thread will disappear when it try to delete boost io_service.
main.cpp
int main()
{
myDailyTimer* pTimer = new myDailyTimer;
// do something
delete pTimer;
return 0;
}
I set break points in myDailyTimer.cpp::int i = 0; and myDailyTimer.cpp::int j = 0; and main::return 0; My main thread can reach int i = 0;, My timer thread cannot reach int j = 0;, My main thread cannot reach return 0;.
I found the my main thread will disappear when it try to delete boost::asio::io_service object. How to solve this problem? Am I using boost::asio::io_service in a wrong way?
myDailyTimer.h
class myDailyTimerInterface
{
public:
myDailyTimerInterface(){}
~myDailyTimerInterface(){}
virtual void TimerCallback(int nTimerID) = 0;
};
class myDailyTimer :
public myThread
{
public:
boost::asio::io_service m_IO;
boost::asio::deadline_timer * m_pTimer;
tm m_tmSpecificTime;
std::string m_strSpecificTime;
int m_nTimerID;
myDailyTimerInterface* m_pParent;
public:
myDailyTimer();
~myDailyTimer();
void SetTime(tm strIN, int nID); // msec
void TimerCallback();
//Override
void ThreadMain();
protected:
std::string MakeStringSpecificTime();
void AddOneDay();
};
myDailyTimer.cpp
myDailyTimer::myDailyTimer()
{
m_pTimer = 0;
m_strSpecificTime = "";
}
myDailyTimer::~myDailyTimer()
{
EndThread();
if (m_pTimer != 0)
{
m_pTimer->cancel();
delete m_pTimer;
}
m_IO.stop();
m_IO.~io_service();
int i = 0;
i++;
}
void myDailyTimer::SetTime(tm tmIN, int nID) // msec
{
if (m_pTimer != 0)
{
m_pTimer->cancel();
delete m_pTimer;
}
m_tmSpecificTime = tmIN;
m_strSpecificTime = MakeStringSpecificTime();
m_nTimerID = nID;
m_pTimer = new boost::asio::deadline_timer(m_IO, boost::posix_time::time_from_string(m_strSpecificTime));
m_pTimer->async_wait(boost::bind(&myDailyTimer::TimerCallback, this));
myThread::Start();
}
std::string myDailyTimer::MakeStringSpecificTime()
{
time_t localTime;
localTime = mktime(&m_tmSpecificTime); // time is GMT local
struct tm * ptm = gmtime(&localTime); // convert time to GMT +0
char veccNextTime[64];
memset(veccNextTime, 0, sizeof(veccNextTime));
sprintf(veccNextTime, "%d-%02d-%02d %02d:%02d:%02d.000",
ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,
ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
std::string strTemp(veccNextTime);
return strTemp;
}
void myDailyTimer::AddOneDay()
{
m_tmSpecificTime.tm_mday += 1;
mktime(&m_tmSpecificTime); /* normalize result */
}
void myDailyTimer::TimerCallback()
{
if (m_pParent != 0)
m_pParent->TimerCallback(m_nTimerID);
//m_timer->expires_from_now(boost::posix_time::milliseconds(m_nTimerDuration));
AddOneDay();
m_strSpecificTime = MakeStringSpecificTime();
m_pTimer->expires_at(boost::posix_time::time_from_string(m_strSpecificTime));
m_pTimer->async_wait(boost::bind(&myDailyTimer::TimerCallback, this));
}
//Override
void myDailyTimer::ThreadMain()
{
while (!IsEndThread())
m_IO.run();
int j = 0;
j++;
}
As Dan MaĊĦek mentioned, explicitly calling the destructor isn't a good pattern here. The standard way to stop an io_service is to stop every "work" that is pending and then wait for io_service::run function to return. Also, to prevent the io_service::run function from returning prematurely, it is a good idea to create an instance of io_service::work object.
Hope you'll be able to modify this example to your use case:
namespace asio = boost::asio;
class MyTimer {
using Clock = std::chrono::steady_clock;
public:
MyTimer(Clock::duration duration)
: _work(_ios)
, _timer(_ios)
, _thread([this] { _ios.run(); })
{
_ios.post([this, duration] { start(duration); });
}
~MyTimer() {
_ios.post([this] { stop(); });
_thread.join();
}
private:
void start(Clock::duration duration) {
_timer.expires_from_now(duration);
_timer.async_wait([this](boost::system::error_code) {
// NOTE: Be careful here as this is run from inside
// the thread.
if (!_work) {
// Already stopped.
std::cout << "Stopped" << std::endl;
return;
}
std::cout << "Timer fired" << std::endl;
});
}
void stop() {
_work.reset();
_timer.cancel();
}
private:
asio::io_service _ios;
boost::optional<asio::io_service::work> _work;
asio::steady_timer _timer;
std::thread _thread;
};
int main() {
auto* my_timer = new MyTimer(std::chrono::seconds(1));
delete my_timer;
return 0;
}

print trace of signals/slots called

Is there a way to print the signals and slots being called?
I'm experiencing a weird deadlock in Qt which happens only in particular conditions across multiple threads, and I'd like to know the order of signals/slots being called.
Of course, for slots, I write the method body, and in the worst scenario, I can manually add a print out of the method. But the body of signals is generated automatically, so it is not possible, unless I write a custom moc, which seems like an overkill for this task...
If one leverages the built-in hooks, it's possible to automatically instrument all signals, and all slots connected using the Qt 4 connect syntax. Unfortunately, QtPrivate::QSlotObject doesn't implement these hooks: slots connected using the Qt 5 syntax need to be instrumented manually (e.g. by connecting a functor to them, or adding code to them).
Signal notifications can be relied on for connected signals. Objects with no signals, and some signals of objects with other connections, will not be reported. This is presumably what you want.
Thus:
// https://github.com/KubaO/stackoverflown/tree/master/questions/signal-spy-39597233
#include <QtCore>
#include <private/qobject_p.h>
int signalToMethodIndex(const QMetaObject * mo, int signal)
{
Q_ASSERT(signal >= 0);
for (int i = 0; i < mo->methodCount(); ++i) {
if (mo->method(i).methodType() == QMetaMethod::Signal) {
if (signal == 0) return i;
-- signal;
}
}
return -1;
}
class Spy {
static QThreadStorage<bool> entered;
static void signalBegin(QObject *caller, int signalIndex, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
auto index = signalToMethodIndex(caller->metaObject(), signalIndex);
if (index >= 0)
qDebug() << "SIGNAL" << caller << caller->metaObject()->method(index).methodSignature();
}
static void slotBegin(QObject *caller, int index, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
qDebug() << "SLOT" << caller << caller->metaObject()->method(index).methodSignature();
}
public:
static void start() {
QSignalSpyCallbackSet set{&signalBegin, &slotBegin, nullptr, nullptr};
qt_signal_spy_callback_set = set;
}
};
QThreadStorage<bool> Spy::entered;
struct Class : QObject {
Q_SIGNAL void aSignal();
Q_SLOT void aSlot() { qDebug() << "slot"; }
Q_OBJECT
};
int main(int argc, char ** argv) {
Spy::start();
QCoreApplication app{argc, argv};
Class obj;
QObject::connect(&obj, SIGNAL(aSignal()), &obj, SLOT(aSlot()));
obj.setObjectName("obj");
emit obj.aSignal();
}
#include "main.moc"
Output:
SIGNAL Class(0x7fff51901af0, name = "obj") "objectNameChanged(QString)"
SIGNAL Class(0x7fff51901af0, name = "obj") "aSignal()"
SLOT Class(0x7fff51901af0, name = "obj") "aSlot()"
slot

Will this event dispatcher / listener work?

I'm working on a program that will be significantly multi-threaded. I need to respond to events between objects and threads so I've come up with the following code.
#ifndef EVENT_H
#define EVENT_H
#include <vector>
#include <mutex>
#include <algorithm>
#include <memory>
/**
* Create the definition of a dispatcher for the event type (t)
*/
#define DISPATCHER(t) EventDispatcher<t>
/**
* Create the definition of a listener for event type (t)
*/
#define LISTENER(t) DISPATCHER(t)::EventListener
/**
* Dispatch a event (e) of type (t)
*/
#define DISPATCH(t, e) DISPATCHER(t)::dispatch(e);
/**
* Attach a listener (l) of type (t) to the dispatcher (d)
*/
#define LISTEN(t, d, l) ((DISPATCHER(t) *)(d))->addListener((l));
template <typename T>
class EventDispatcher {
public:
virtual ~EventDispatcher() {
std::unique_lock<std::recursive_mutex> uLock(lock);
// Prevent more listeners from being added.
isAlive = false;
// Remove all listeners.
while (listeners.begin() != listeners.end()) {
EventDispatcher<T>::EventListener *listener = *listeners.begin();
// Call remove listener so that the listener will be notified of the change.
removeListener(listener);
}
}
class EventListener {
friend EventDispatcher<T>;
public:
virtual ~EventListener() {
std::unique_lock<std::recursive_mutex> uLock(lock);
// Stop more dispatchers from connecting.
isAlive = false;
// Remove self from all dispatchers.
// Use while loop as removeListener will call removeDispatcher and modify dispatchers.
while (dispatchers.begin() != dispatchers.end()) {
EventDispatcher<T> *dispatcher = *dispatchers.begin();
dispatcher->removeListener(this);
}
}
protected:
/**
* Respond to an event.
* #param sender The dispatcher that sent the event.
* #param event The event that occurred.
*/
virtual void onEvent(EventDispatcher<T> *sender, std::shared_ptr<T> event) = 0;
private:
bool isAlive = true;
typedef std::vector<EventDispatcher<T> *> DispatcherList;
DispatcherList dispatchers;
std::recursive_mutex lock;
/**
* Add a reference to the dispatchers this listener is attached to.
* #param dispatcher The dispatcher that attached this listener.
* #return true if the listener is still alive.
*/
bool addDispatcher(EventDispatcher<T> *dispatcher) {
if (dispatcher == NULL) {
return false;
}
std::unique_lock<std::recursive_mutex> uLock(lock);
if (isAlive) {
if (std::find(dispatchers.begin(), dispatchers.end(), dispatcher) == dispatchers.end()) {
// This should only ever be called by the dispatcher so no need to call addListener.
dispatchers.push_back(dispatcher);
}
}
return isAlive;
}
/**
* Remove a reference to the dispatchers this listener is attached to.
* #param dispatcher The dispatcher that removed this listener.
*/
void removeDispatcher(EventDispatcher<T> *dispatcher) {
if (dispatcher == NULL) {
return;
}
std::unique_lock<std::recursive_mutex> uLock(lock);
typename DispatcherList::iterator itr = std::find(dispatchers.begin(), dispatchers.end(), dispatcher);
if (itr != dispatchers.end()) {
// This should only ever be called by the dispatcher so no need to call removeListener.
dispatchers.erase(itr);
}
}
};
public:
/**
* Add a listener to the dispatcher.
* #param listener The listener to add.
*/
void addListener(EventDispatcher<T>::EventListener *listener) {
if (listener == NULL) {
return;
}
std::unique_lock<std::recursive_mutex> uLock(lock);
if (isAlive) {
if (std::find(listeners.begin(), listeners.end(), listener) == listeners.end()) {
// Listener not in list, add it.
if (listener->addDispatcher(this)) {
// The listener was still alive so register it.
listeners.push_back(listener);
}
}
}
}
/**
* Remove a listener from the dispatcher.
* #param listener The listener to remove.
*/
void removeListener(EventDispatcher<T>::EventListener *listener) {
if (listener == NULL) {
return;
}
std::unique_lock<std::recursive_mutex> uLock(lock);
typename ListenerList::iterator itr = std::find(listeners.begin(), listeners.end(), listener);
if (itr != listeners.end()) {
listener->removeDispatcher(this);
listeners.erase(itr);
}
}
protected:
/**
* Dispatch an event to all listeners.
* #param event The event to dispatch.
* #note If the event is modifiable then not all listeners will necessarily get the exact same message.
* #note The event will be deleted before the function returns.
*/
void dispatch(T *event) {
std::shared_ptr<T> evt(event);
std::unique_lock<std::recursive_mutex> uLock(lock);
for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); iter++) {
(*iter)->onEvent(this, evt);
}
}
/**
* Dispatch an event to all listeners.
* #param event The event to dispatch.
* #note If the event is modifiable then not all listeners will necessarily get the exact same message.
*/
void dispatch(std::shared_ptr<T> event) {
std::unique_lock<std::recursive_mutex> uLock(lock);
for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); iter++) {
(*iter)->onEvent(this, event);
}
}
private:
bool isAlive = true;
typedef std::vector<EventListener *> ListenerList;
ListenerList listeners;
std::recursive_mutex lock;
};
#endif /* EVENT_H */
This is a simple test I've set up (not multi-threaded yet)
#include "include/Event.h"
#include <iostream>
class ActionEvent {
public:
ActionEvent(int id) : actionID(id) {
// actionID = id;
}
const int actionID;
};
class PropertyChangeEvent {
public:
int propertyID;
};
class testDispatcher : public DISPATCHER(ActionEvent), public DISPATCHER(PropertyChangeEvent) {
public:
void test() {
// dispatch(new ActionEvent(0)); // ambiguous function.
// dispatch(new PropertyChangeEvent()); // ambiguous function.
// EventDispatcher<ActionEvent>::dispatch(new ActionEvent(1)); // works but is long.
// EventDispatcher<ActionEvent>::dispatch(new ActionEvent(2)); // works but is long.
// EventDispatcher<PropertyChangeEvent>::dispatch(new PropertyChangeEvent()); // works but is long.
DISPATCH(ActionEvent, new ActionEvent(1)); // The dispatcher will make a shared pointer then delete the event.
DISPATCH(ActionEvent, std::shared_ptr<ActionEvent>(new ActionEvent(2)));
DISPATCH(PropertyChangeEvent, new PropertyChangeEvent());
}
};
class testListener :
public LISTENER(ActionEvent), public LISTENER(PropertyChangeEvent) {
int ID;
public:
testListener(testDispatcher *dispatcher, int id) {
ID = id;
// dispatcher->addListener(this); // ambiguous function.
// dispatcher->addListener((EventDispatcher<ActionEvent>::EventListener *) this); // ambiguous function.
// ((EventDispatcher<ActionEvent> *)dispatcher)->addListener(this); // works but is long.
LISTEN(ActionEvent, dispatcher, this);
if(id % 2) {
// Only respond to property change events on odd numbered listeners just to be different.
// dispatcher->addListener(this); // ambiguous function.
// ((EventDispatcher<PropertyChangeEvent> *)dispatcher)->addListener(this); // works but is long.
LISTEN(PropertyChangeEvent, dispatcher, this);
}
}
protected:
void onEvent(EventDispatcher<ActionEvent> *source, std::shared_ptr<ActionEvent> event) {
(void)source;
(void)event;
std::cout << ID << " ActionEvent " << event->actionID << std::endl;
// event->actionID += 5;
// std::cout << " set to " << event->actionID << std::endl;
}
void onEvent(EventDispatcher<PropertyChangeEvent> *source, std::shared_ptr<PropertyChangeEvent> event) {
(void)source;
(void)event;
std::cout << ID << "PropertyChangeEvent" << std::endl;
}
};
int main(int argc, char *argv[]) {
testDispatcher td();
testListener tl1(&td, 1);
testListener tl2(&td, 2);
testListener tl3(&td, 3);
testListener tl4(&td, 4);
td.test();
return 0;
}
I'd really like feedback on this.
Is there any way I can improve it?
Can I make the template easier to use? (better macros?)
Are their any possible gotchas I missed?