Results 1 to 6 of 6

Thread: Combining Event Loops

  1. #1
    Join Date
    Jun 2009
    Posts
    4
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Combining Event Loops

    I'm building a Qt client for the open-source client/server 4X strategy game Thousand Parsec. I'm however stuck at a dead end. Basically, the client interfaces with the server via a C++ protocol layer that facilitates client/server communication. The protocol's documentation is available here.

    Now my problem is that the protocol requires you to create a subclass of the virtual EventLoop class (link) in your client. There is an example SimpleEventLoop used for console clients on the same link. I'm having difficulty figuring out how I can design my own event loop subclass that handles the protocol's events while hooking into the Qt application at the same time. My research has lead me to believe QAbstractEventDispatcher is the Qt class I want to use but the documentation seems pretty slim and I'm not exactly sure how I would go about doing this.

    Does anyone else have experience linking external event loops with a Qt application? I've also found this example on the Qt page but it wasn't very helpful - or at least I didn't really understand it.

    Thanks!

  2. #2
    Join Date
    Dec 2006
    Posts
    849
    Thanks
    6
    Thanked 163 Times in 151 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Combining Event Loops

    you can go two directions afaik:

    1) hook up the eventloop with Qt's - if Qt is built against the glib event loop, see e.g. http://labs.trolltech.com/blogs/2006/02/24/qt-and-glib/

    2) avoid the external event loop: from looking at the url you provided, it might work out to write a Qt-based subclass of the TPProto::EventLoop, somehow along the following (untried) lines

    Qt Code:
    1. class QtTPProtoEventLoop : public QObject, public TPProto::EventLoop
    2. {
    3. Q_OBJECT
    4.  
    5. struct sock_info
    6. {
    7. sock_info(TPProto::TPSocket *sock, QSocketNotifier *sn)
    8. : sock(sock), sn(sn) {}
    9. TPProto::TPSocket *sock;
    10. };
    11. QHash<int,sock_info> read_notifiers_;
    12. QHash<int,sock_info> write_notifiers_;
    13.  
    14. public:
    15. void listenForSocketRead (TPProto::TPSocket *sock)
    16. {
    17. int fd = sock->getFileDescriptor();
    18. QSocketNotifier sn = new QSocketNotifier(fd, QSocketNotifier::Read, this);
    19. notifiers_[fd] = sock_info(sock, sn);
    20. connect(sn, SIGNAL(activated(int)), SLOT(activated_read(int)));
    21. }
    22. void listenForSocketWrite (TPProto::TPSocket *sock)
    23. {
    24. int fd = sock->getFileDescriptor();
    25. if (!write_notifiers_.contains(fd))
    26. {
    27. QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Write, this);
    28. notifiers_[fd] = sock_info(sock, sn);
    29. }
    30. // (re-) connect "singleshot" see below
    31. sock_info si = write_notifiers_[fd];
    32. connect(si.sn, SIGNAL(activated(int)), SLOT(activated_read(int)));
    33. }
    34. TPProto::TimerConnection setTimer(uint32_t interval, const TPProto::TimerSignal::slot_type &callback)
    35. {
    36. // store the callback
    37. // setup a QTimer (singleshot after interval)
    38. // have a slot call the callback
    39. // no idea what a TimerConnection is
    40.  
    41. }
    42.  
    43. public Q_SLOTS:
    44. void activated_read(int fd)
    45. {
    46. read_notifiers_[fd].sock->readyToRead();
    47. }
    48. void activated_write(int fd)
    49. {
    50. // tpproto docs say: single shot: disconnect
    51. sock_info si = write_notifiers_[fd];
    52. si.sn->disconnect(this);
    53. si.sock->readyToWrite();
    54. }
    55.  
    56. };
    To copy to clipboard, switch view to plain text mode 

    (Take care: any number of typos and copy/paste errors possible...)

    Tell us if you manage to get it done along this way (or another).

    HTH

  3. #3
    Join Date
    Jun 2009
    Posts
    4
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Combining Event Loops

    Wow! Thanks for the amazingly detailed reply. I think your use of SocketNotifiers is the right idea. I'm going to try to build on what you did there and see how it works.

    One of the Parsec mentors also linked to me the avahi project which contains an Qt implementation of their eventloop allowing Qt applications to easily plug into the framework. The ideas are similar and they do it for GTK as well. Check out http://avahi.org/download/doxygen/ and look for avahi-qt.

    Thanks again caduel - I'll let you know how it works out.

  4. #4
    Join Date
    Jun 2009
    Posts
    4
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Combining Event Loops

    Hello again,

    So I've tried to put this together but I'm still having some trouble. I took the advice you gave me in the private message as well Caduel but I still can't get a working compile. Here's my code. It is more or less similar to the suggestions you made with some twists.

    It basically throws up a huge piles of errors about the QHash objects. I think the way I'm using them is wrong. I'm not quite sure but I dont think it likes that I'm trying to create a QHash<QTimer* timer, boost::signal> object. I also tried the typedef suggestions you made Caduel but they didn't seem to work either. They created similiar errors. Any ideas? Anything you see wrong with this code?

    For reference these types used throughout the class are:
    typedef boost::signal<void (void)> TimerSignal;
    typedef boost::signals::connection TimerConnection;

    HEADER
    Qt Code:
    1. #ifndef PARSEKEVENTLOOP_H
    2. #define PARSEKEVENTLOOP_H
    3.  
    4. #include <QObject>
    5. #include <QTimer>
    6. #include <QHash>
    7. #include <QSocketNotifier>
    8. #include <tpproto/eventloop.h>
    9.  
    10. //#include <boost/function.hpp>
    11. //typedef TPProto::TimerSignal::slot_type Callback;
    12. //typedef boost::function<void(void)> Callback;
    13.  
    14. class ParsekEventLoop : public QObject, public TPProto::EventLoop
    15. {
    16. Q_OBJECT
    17.  
    18. public:
    19. ParsekEventLoop();
    20. virtual ~ParsekEventLoop();
    21. virtual void listenForSocketRead(TPProto::TPSocket *sock);
    22. virtual void listenForSocketWrite(TPProto::TPSocket *sock);
    23. virtual TPProto::TimerConnection setTimer(uint32_t interval, const TPProto::TimerSignal::slot_type &callback);
    24. public Q_SLOTS:
    25. void activated_read(int fd);
    26. void activated_write(int fd);
    27. void timeout();
    28. private:
    29. //TIMERS AND CALLBACK STUFF
    30. QHash<QTimer*,TPProto::TimerSignal> timers;
    31.  
    32. //SOCKET STUFF
    33. struct ParsekSocket //covert to dedicated class later??
    34. {
    35. ParsekSocket(TPProto::TPSocket *socket, QSocketNotifier *notifier) : socket(socket), notifier(notifier) {}
    36. TPProto::TPSocket *socket;
    37. QSocketNotifier *notifier;
    38. };
    39. QHash<int,ParsekSocket> readset;
    40. QHash<int,ParsekSocket> writeset;
    41.  
    42. };
    43. #endif
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. #include <tpproto/tpsocket.h>
    2. #include <parsekeventloop.h>
    3.  
    4. ParsekEventLoop::ParsekEventLoop()
    5. {
    6. //initialize QHash objects?
    7. }
    8.  
    9. ParsekEventLoop::~ParsekEventLoop()
    10. {
    11. readset.clear();
    12. writeset.clear();
    13. timers.clear();
    14. }
    15.  
    16. void ParsekEventLoop::listenForSocketRead(TPProto::TPSocket *sock)
    17. {
    18. int fd = sock->getFileDescriptor();
    19. QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
    20. //readset[fd] = ParsekSocket(sock,notifier);
    21. readset.insert(fd,ParsekSocket(sock,notifier));
    22. connect(notifier,SIGNAL(activated(int)),SLOT(activated_read(int)));
    23. }
    24.  
    25. void ParsekEventLoop::listenForSocketWrite(TPProto::TPSocket *sock)
    26. {
    27. int fd = sock->getFileDescriptor();
    28. if (!writeset.contains(fd))
    29. {
    30. QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Write, this);
    31. //writeset[fd] = ParsekSocket(sock,notifier);
    32. writeset.insert(fd,ParsekSocket(sock,notifier));
    33. }
    34. connect(writeset[fd].notifier, SIGNAL(activated(int)), SLOT(activated_write(int)));
    35. }
    36.  
    37.  
    38. TPProto::TimerConnection ParsekEventLoop::setTimer(uint32_t interval, const TPProto::TimerSignal::slot_type &callback)
    39. {
    40. TPProto::TimerSignal *signal = new TPProto::TimerSignal();
    41. TPProto::TimerConnection tc = signal->connect(callback);
    42.  
    43. QTimer *timer = new QTimer(this);
    44. //timers[timer] = signal;
    45. timers.insert(timer,signal);
    46. connect(timer,SIGNAL(timeout()),SLOT(timeout()));
    47. timer->setSingleShot(true);
    48. timer->start(interval*1000);
    49.  
    50. return tc;
    51. }
    52.  
    53. void ParsekEventLoop::activated_read(int fd)
    54. {
    55. readset[fd].socket->readyToRead();
    56. }
    57.  
    58. void ParsekEventLoop::activated_write(int fd)
    59. {
    60. writeset[fd].notifier->disconnect(this);
    61. writeset[fd].socket->readyToSend();
    62. }
    63.  
    64. void ParsekEventLoop::timeout()
    65. {
    66. QTimer *timeout = static_cast<QTimer*>(sender());
    67. TPProto::TimerSignal *signal = new TPProto::TimerSignal();
    68. signal = timers[timeout];
    69. (*signal)();
    70. }
    To copy to clipboard, switch view to plain text mode 

    BUILD ERRORS
    /home/marwan/Documents/parsek/src/parsekeventloop.cpp: In member function ‘virtual TPProto::TimerConnection ParsekEventLoop::setTimer(uint32_t, const boost::slot<boost::function<void ()()> >&)’:
    /home/marwan/Documents/parsek/src/parsekeventloop.cpp:50: error: no matching function for call to ‘QHash<QTimer*, boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> > >::insert(QTimer*&, TPProto::TimerSignal*&)’
    /usr/include/QtCore/qhash.h:730: note: candidates are: typename QHash<Key, T>::iterator QHash<Key, T>::insert(const Key&, const T&) [with Key = QTimer*, T = boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >]
    /home/marwan/Documents/parsek/src/parsekeventloop.cpp: In member function ‘void ParsekEventLoop::timeout()’:
    /home/marwan/Documents/parsek/src/parsekeventloop.cpp:73: error: cannot convert ‘boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >’ to ‘TPProto::TimerSignal*’ in assignment
    /usr/include/QtCore/qhash.h: In member function ‘T& QHash<Key, T>:perator[](const Key&) [with Key = int, T = ParsekEventLoop::ParsekSocket]’:
    /home/marwan/Documents/parsek/src/parsekeventloop.cpp:39: instantiated from here
    /usr/include/QtCore/qhash.h:723: error: no matching function for call to ‘ParsekEventLoop::ParsekSocket::ParsekSocket ()’
    /home/marwan/Documents/parsek/src/parsekeventloop.h:54: note: candidates are: ParsekEventLoop::ParsekSocket::ParsekSocket(TPProt o::TPSocket*, QSocketNotifier*)
    /home/marwan/Documents/parsek/src/parsekeventloop.h:53: note: ParsekEventLoop::ParsekSocket::ParsekSocket(const ParsekEventLoop::ParsekSocket&)
    /usr/include/boost/noncopyable.hpp: In copy constructor ‘boost::signals::detail::signal_base::signal _base(const boost::signals::detail::signal_base&)’:
    /usr/include/boost/signals/detail/signal_base.hpp:119: instantiated from ‘QHashNode<Key, T>::QHashNode(const Key&, const T&) [with Key = QTimer*, T = boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >]’
    /usr/include/QtCore/qhash.h:515: instantiated from ‘typename QHash<Key, T>::Node* QHash<Key, T>::createNode(uint, const Key&, const T&, QHashNode<Key, T>**) [with Key = QTimer*, T = boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >]’
    /usr/include/QtCore/qhash.h:723: instantiated from ‘T& QHash<Key, T>:perator[](const Key&) [with Key = QTimer*, T = boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >]’
    /home/marwan/Documents/parsek/src/parsekeventloop.cpp:73: instantiated from here
    /usr/include/boost/noncopyable.hpp:27: error: ‘boost::noncopyable_::noncopyable::noncopyab le(const boost::noncopyable_::noncopyable&)’ is private
    /usr/include/boost/signals/detail/signal_base.hpp:119: error: within this context
    /usr/include/boost/signals/signal_template.hpp: In copy constructor ‘boost::signal0<void, boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >::signal0(const boost::signal0<void, boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >&)’:
    /usr/include/boost/signals/signal_template.hpp:142: instantiated from ‘QHashNode<Key, T>::QHashNode(const Key&, const T&) [with Key = QTimer*, T = boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >]’
    /usr/include/QtCore/qhash.h:515: instantiated from ‘typename QHash<Key, T>::Node* QHash<Key, T>::createNode(uint, const Key&, const T&, QHashNode<Key, T>**) [with Key = QTimer*, T = boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >]’
    /usr/include/QtCore/qhash.h:723: instantiated from ‘T& QHash<Key, T>:perator[](const Key&) [with Key = QTimer*, T = boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >]’
    /home/marwan/Documents/parsek/src/parsekeventloop.cpp:73: instantiated from here
    /usr/include/boost/signals/signal_template.hpp:142: note: synthesized method ‘boost::signals::detail::signal_base::signal _base(const boost::signals::detail::signal_base&)’ first required here
    /usr/include/boost/signal.hpp: In copy constructor ‘boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >::signal(const boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >&)’:
    /usr/include/boost/signal.hpp:335: note: synthesized method ‘boost::signal0<void, boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >::signal0(const boost::signal0<void, boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >&)’ first required here
    /usr/include/QtCore/qhash.h: In constructor ‘QHashNode<Key, T>::QHashNode(const Key&, const T&) [with Key = QTimer*, T = boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >]’:
    /usr/include/QtCore/qhash.h:213: note: synthesized method ‘boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >::signal(const boost::signal<void ()(), boost::last_value<void>, int, std::less<int>, boost::function<void ()()> >&)’ first required here

  5. #5
    Join Date
    Dec 2006
    Posts
    849
    Thanks
    6
    Thanked 163 Times in 151 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Combining Event Loops

    ok, ... to get that working, you need

    i) add
    Qt Code:
    1. ParsekSocket() : socket(0), notifier(0) {}
    To copy to clipboard, switch view to plain text mode 
    to your ParsekSocket class (only classes with a default constructor can be put in containers).

    ii) be sure to have the line
    Qt Code:
    1. CONFIG += no_keywords // for Boost.Signals
    To copy to clipboard, switch view to plain text mode 
    and use the macros Q_SLOTS etc (you did that)

    iii) yes, error messages for templated boost code are always lots of fun...
    I have attached a compilable version of the code.
    (Boost.Signals are not copyable, so you need to store them by pointer (don't forget the cleanup...). You do indeed need to create a TimerSignal in order to return a TimerConnection. You need to find out if/when you can release/destroy that signal.)

    Please note:
    * I have not cared about "cleanup" of no longer needed Timer signals
    (maybe you need to iterate now and then over the container and remove (and delete) unconnected signals...)
    Be sure to read up on the lifetime of those objects, maybe ask in the thousandparsek forum regarding that
    * in the class's destructor you need to cleanup all that too

    iv) to your questions:
    * no, hashes do not need to be initialized (they are empty by default)
    * it is not necessary to clear() containers in a destructor: the containers are deleted anyway
    * it IS necessary to release the "pointed to" objects: if you store pointers the objects pointed to are not deleted (and their destructors not called)
    * and, do not "just" add a destructor to your ParsekSocket class, you'd get interesting effects (when accessing the container)... see http://www.artima.com/cppsource/bigtwo.html
    * an option would be to use boost::shared_ptr in such cases

    HTH
    Attached Files Attached Files

  6. #6
    Join Date
    Jun 2009
    Posts
    4
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Combining Event Loops

    i) Doh - that was a silly thing to miss.

    ii) Yes my initial problems with Boost lead me to find the compatibility problems with Boost and Qt together. As you said, the CONFIG += no_keywords is required in the qmake configuration file.

    Just for reference, since I am using Cmake, the way to do this is to add
    Qt Code:
    1. add_definitions(-DQT_NO_KEYWORDS)
    To copy to clipboard, switch view to plain text mode 
    to your CMakelists.txt. Like you said, you need to use the uppercase Qmacros afterwards.

    iii+iv) I'll see how to best handle the cleanup. First thing is that the signal can be removed after being called so I added
    Qt Code:
    1. timers.remove(timeout);
    To copy to clipboard, switch view to plain text mode 
    to the timeout() function where (*signal)() is called. I believe this should remove the need to later clean up the signals from the timers hash. Thanks for the tips and the link regarding proper destructor cleanup. I think I need to pull out my copy of Meyer's Effective C++ and re-educate myself about a couple of topics.

    v) Another thing that was required of me to get a proper compile was to add
    Qt Code:
    1. #define BOOST_NO_EXCEPTIONS
    2. #include <boost/throw_exception.hpp>
    3. void boost::throw_exception(std::exception const & e){
    4. //do nothing
    5. }
    To copy to clipboard, switch view to plain text mode 
    Without this Boost was complaining about not finding a proper exception handler in the case that the call to (*signal)() is empty, or something along those lines. Adding the above code remedied the situation so if anyone sees an error similar to one below, try something similar.
    Qt Code:
    1. undefined reference to `boost::throw_exception(std::exception const&)'
    To copy to clipboard, switch view to plain text mode 

Similar Threads

  1. Replies: 4
    Last Post: 19th February 2009, 11:10
  2. Custom event gets not propagated to the top level widget
    By nightghost in forum Qt Programming
    Replies: 0
    Last Post: 29th January 2009, 09:06
  3. Event propagation direction
    By spraff in forum Qt Programming
    Replies: 0
    Last Post: 6th December 2008, 21:03
  4. Qt event queue overloading?
    By gct in forum Qt Programming
    Replies: 3
    Last Post: 17th March 2008, 18:39
  5. Workload in a QThread blocks main application's event loop ?
    By 0xBulbizarre in forum Qt Programming
    Replies: 14
    Last Post: 9th April 2006, 21:55

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.