SimpleChat made easy » History » Revision 2
Revision 1 (Omer Katz, 03/22/2010 10:11 PM) → Revision 2/12 (Omer Katz, 03/23/2010 01:23 AM)
h1. SimpleChat made easy - Part 1 Currently the situation with Wt's Comet is that you have to track the sessions you want to push data to yourself. I have written some classes that help dealing with this issue and simplifies building the simple chat example. We start writing a new application type that is derived from Wt application: h2. CometApplication.hpp <pre> #ifndef CometApplication_HPP #define CometApplication_HPP // <STL> #include <list> #include <string> #include <map> // </STL> // <Boost> #include <boost/unordered_map.hpp> // </Boost> // <Wt> #include <Wt/WApplication> #include <Wt/WWidget> // </Wt> namespace UI { namespace Widgets { // Forward declerations class CometWidget; template <class> class CometWidgetFactory; } namespace Application { class CometApplication : public Wt::WApplication { public: CometApplication(const Wt::WEnvironment &env); // Ctor, passes enviorment variables to WApplication ~CometApplication(); // Dtor friend class Widgets::CometWidget; // Befriending with CometWidget so it can access the widget map template <class> friend class Widgets::CometWidgetFactory; // Same here private: typedef std::pair<int, Wt::WWidget *> WidgetEntryType; typedef boost::unordered_multimap<CometApplication *, WidgetEntryType > WidgetMapType; typedef boost::unordered_multimap<CometApplication *, WidgetEntryType >::iterator WidgetMapIteratorType; typedef std::pair<WidgetMapIteratorType, WidgetMapIteratorType> WidgetRangeType; static WidgetMapType widgets; // Pushes a new widget to the current session with group id and the widget itself inline void registerWidget(unsigned long int id, Wt::WWidget *w) { widgets.insert(WidgetMapType::value_type(this, WidgetEntryType(id, w))); } // Searches for the widget of the group id for the current session and erases it inline void unregisterWidget(unsigned long int id) { WidgetRangeType range = widgets.equal_range(this); for ( WidgetMapIteratorType iter = range.first; iter != range.second; ++iter ) { if ( iter->second.first == id ) { widgets.erase(iter); break; } } } // Searches for a widget in the current session, casts it to the right widget type and returns it template <class T> inline T *getWidget(unsigned long int id) { WidgetRangeType range = widgets.equal_range(this); for ( WidgetMapIteratorType iter = range.first; iter != range.second; ++iter ) if ( iter->second.first == id ) return dynamic_cast<T *>(iter->second.second); return NULL; } }; } } #endif </pre> h2. CometApplication.cpp <pre> #include "CometApplication.hpp" namespace UI { namespace Application { CometApplication::CometApplication(const Wt::WEnvironment &env) : WApplication(env) { enableUpdates(); // Enables ajax-push for the current session } CometApplication::~CometApplication() { if ( widgets.find(this) != widgets.end() ) // Erases the current session from the widget map widgets.erase(this); } CometApplication::WidgetMapType CometApplication::widgets; } } </pre> h3. Code Review I have created an unordered multimap that might look a little strange to you guys but I'm going to elaborate about it in a minute. # The multimap key is the pointer to the current session which is represented by CometApplication, because as you guys already know, a session is an application instance. That way I can access the current session's widgets using *this* or iterate through all sessions. # The value of the multimap is a pair of a group id and a widget. Different widgets in different sessions with the same group id can be accessed this way. As you can see the CometApplication instance only refers to *this*, which means only to the current session. I have befriended all other classes because registerWidget, unregisterWidget and getWidget should only be accessed by those classes. This doesn't indicate that my code has a design problem in my opinion. Sometimes you actually have to use class friendship. It also makes sense that the end user will not control anything related to registering/unregistering widgets as you will see. TBD