Project

General

Profile

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