Project

General

Profile

SimpleChat made easy » History » Version 6

Ana Evans, 07/26/2013 08:21 PM

1 2 Omer Katz
h1. SimpleChat made easy - Part 1
2 1 Omer Katz
3 2 Omer Katz
Currently the situation with Wt's Comet is that you have to track the sessions you want to push data to yourself.
4
I have written some classes that help dealing with this issue and simplifies building the simple chat example.
5 3 Omer Katz
We start with coding a new application type that is derived from Wt application, a Wt application that uses my comet example should use this application class instead of the normal WApplication class.
6 2 Omer Katz
7
h2. CometApplication.hpp
8
9
<pre>
10
#ifndef CometApplication_HPP
11
#define CometApplication_HPP
12
13
// <STL>
14
#include <list>
15
#include <string>
16
#include <map>
17
// </STL>
18
19
// <Boost>
20
#include <boost/unordered_map.hpp>
21
// </Boost>
22
23
// <Wt>
24
#include <Wt/WApplication>
25
#include <Wt/WWidget>
26
// </Wt>
27
28
namespace UI
29
{
30
	namespace Widgets
31
	{
32
                // Forward declerations
33
		class CometWidget;
34
35
		template <class>
36
		class CometWidgetFactory;
37
	}
38
39
	namespace Application
40
	{
41
		class CometApplication : public Wt::WApplication
42
		{
43
		public:
44
			CometApplication(const Wt::WEnvironment &env); // Ctor, passes enviorment variables to WApplication
45
			~CometApplication(); // Dtor
46
47
			friend class Widgets::CometWidget; // Befriending with CometWidget so it can access the widget map
48
49
			template <class>
50
			friend class Widgets::CometWidgetFactory; // Same here
51
		private:
52
			typedef std::pair<int, Wt::WWidget *> WidgetEntryType;
53
			typedef boost::unordered_multimap<CometApplication *, WidgetEntryType > WidgetMapType;
54
			typedef boost::unordered_multimap<CometApplication *, WidgetEntryType >::iterator WidgetMapIteratorType;
55
			typedef std::pair<WidgetMapIteratorType, WidgetMapIteratorType> WidgetRangeType;
56
57
			static WidgetMapType widgets;
58
59
			// Pushes a new widget to the current session with group id and the widget itself
60
			inline void registerWidget(unsigned long int id, Wt::WWidget *w)
61
			{
62 4 Omer Katz
				boost::mutex::scoped_lock  lock(CometMutex);
63
64 2 Omer Katz
				widgets.insert(WidgetMapType::value_type(this, WidgetEntryType(id, w)));
65
			}
66
67
			// Searches for the widget of the group id for the current session and erases it
68
			inline void unregisterWidget(unsigned long int id)
69
			{
70 4 Omer Katz
				boost::mutex::scoped_lock  lock(CometMutex);
71
72 2 Omer Katz
				WidgetRangeType range = widgets.equal_range(this);
73
74
				for ( WidgetMapIteratorType iter = range.first; iter != range.second; ++iter )
75
				{
76
					if ( iter->second.first == id )
77
					{
78
						widgets.erase(iter);
79
						break;
80
					}
81
				}
82
			}
83
84
			// Searches for a widget in the current session, casts it to the right widget type and returns it
85
			template <class T>
86
			inline T *getWidget(unsigned long int id)
87
			{
88 4 Omer Katz
				boost::mutex::scoped_lock  lock(CometMutex);
89
90 2 Omer Katz
				WidgetRangeType range = widgets.equal_range(this);
91
92
				for ( WidgetMapIteratorType iter = range.first; iter != range.second; ++iter )
93
					if ( iter->second.first == id )
94
						return dynamic_cast<T *>(iter->second.second);
95
96
				return NULL;
97
			}
98 4 Omer Katz
99 5 Omer Katz
			static boost::mutex CometMutex;
100 2 Omer Katz
		};
101
	}
102
}
103
104
#endif
105
</pre>
106
107
h2. CometApplication.cpp
108
109
<pre>
110
#include "CometApplication.hpp"
111
112
namespace UI
113
{
114
	namespace Application
115
	{
116
		CometApplication::CometApplication(const Wt::WEnvironment &env)
117
		: WApplication(env)
118
		{
119
			enableUpdates(); // Enables ajax-push for the current session
120
		}
121
122
		CometApplication::~CometApplication()
123 1 Omer Katz
		{
124 5 Omer Katz
			boost::mutex::scoped_lock  lock(CometMutex);
125
126 2 Omer Katz
			if ( widgets.find(this) != widgets.end() ) // Erases the current session from the widget map
127
				widgets.erase(this);
128
		}
129
130 1 Omer Katz
		CometApplication::WidgetMapType CometApplication::widgets;
131 5 Omer Katz
		CometApplication::boost::mutex CometMutex;
132 2 Omer Katz
	}
133
}
134
</pre>
135
136
h3. Code Review
137
138
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.
139
# 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.
140
# 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.
141
142 1 Omer Katz
As you can see the CometApplication instance only refers to *this*, which means only to the current session.
143 3 Omer Katz
I have befriended all other classes because registerWidget, unregisterWidget and getWidget should only be accessed by those classes.
144
This doesn't indicate that my code has a design problem in my opinion.
145
Sometimes you actually have to use class friendship.
146
It makes sense that the end user will not control anything related to registering/unregistering widgets as you will see.
147
148
149
After that we create a factory which will instanciate a widget for each session.
150
Here the magic is introduced into the system.
151
152
h2. CometWidgetFactory.hpp
153
154
<pre>
155
#ifndef CometWidgetFactory_HPP
156
#define CometWidgetFactory_HPP
157
158
#include <Wt/WApplication>
159
#include <Wt/WWidget>
160
161 4 Omer Katz
#include <boost/thread/mutex.hpp>
162
163 3 Omer Katz
#include "CometApplication.hpp"
164
165
namespace UI
166
{
167
	namespace Widgets
168
	{
169
		// CometWidgetFactory creates a widget of type WidgetType, WidgetType is derived from CometWidget
170
		template <class WidgetType>
171
		class CometWidgetFactory
172
		{
173
		private:
174
			static unsigned long int idCounter; // Counts how many ids there are
175
176
			unsigned long int id; // Group id
177
		public:
178
			CometWidgetFactory()
179
			: id(idCounter)
180
			{
181
				idCounter++; // New factory instance has been created.
182
			}
183
184
			~CometWidgetFactory()
185
			{
186
				idCounter--; // New factory instance has been destroyed
187
			}
188
189
			void create()
190
			{
191
				WidgetType *w = new WidgetType; // Creates a new widget
192
				w->id = id; // Assigns the group id
193
194
				// Registers the widget to the current session
195
				dynamic_cast<UI::Application::CometApplication *>(wApp)->registerWidget(id, w); 
196
			}
197
198
			void destroy()
199
			{
200
				// Unregisters the widget from the current session
201
				dynamic_cast<UI::Application::CometApplication *>(wApp)->unregisterWidget(id);
202
			}
203
204
			// Those operator overloadings make the factory "point" to the current widget we're refering to in the current session
205
			WidgetType *operator->()
206
			{
207
				return dynamic_cast<UI::Application::CometApplication *>(wApp)->getWidget<WidgetType>(id);
208
			}
209
210
			operator Wt::WWidget *()
211
			{
212
				return dynamic_cast<UI::Application::CometApplication *>(wApp)->getWidget<WidgetType>(id);
213
			}
214
		};
215
216
		template <class T>
217
		unsigned long int CometWidgetFactory<T>::idCounter = 0;
218
	}
219
}
220
221
#endif
222
</pre>
223
224
h3. Code Review
225
226
This class looks very simple but it has a trick.
227
If you declare it as static on your target widget it will create a group id for a certain type of widgets for all sessions.
228
For example:
229
<pre>
230
class someWidget : public CometWidget<Wt::LineEdit>
231
{
232
//...
233
};
234
235
class MyPage : Wt::WContainer
236
{
237
private:
238
  static CometWidgetFactory<someWidget> myWidget;
239
public:
240
  MyPage()
241
  {
242
    myWidget.create(); // Creates a widget on the same group id
243
  }
244
245
  ~MyPage()
246
  {
247
    myWidget.destroy();
248
  }
249
};
250
</pre>
251
252
For each session a someWidget will be created and you can access the widget on the current session.
253
254
h2. CometWidget.hpp
255
256
<pre>
257
#ifndef CometWidget_HPP
258
#define CometWidget_HPP
259
260
#include <string>
261
262
#include <boost/thread/mutex.hpp>
263
264
#include <Wt/WContainerWidget>
265
266
#include "CometApplication.hpp"
267
268
namespace UI
269
{
270
    namespace Widgets
271
    {
272
        template <class WidgetType>
273
        class CometWidget : public WidgetType
274
        {
275
        protected:
276
            virtual void updateOthers()
277
            {
278
		boost::mutex::scoped_lock  lock(CometMutex);
279
280
                for ( Application::CometApplication::WidgetMapIteratorType iter = Application::CometApplication::widgets.begin(); iter != Application::CometApplication::widgets.end(); ++iter )
281
                {
282
                    Application::CometApplication::WidgetRangeType range = Application::CometApplication::widgets.equal_range(iter->first);
283
284
                    for ( Application::CometApplication::WidgetMapIteratorType iter2 = range.first; iter2 != range.second; ++iter2 )
285
                    {
286
                        if ( iter2->second.first == id )
287
                        {
288
                            Wt::WApplication::UpdateLock uiLock = iter->first->getUpdateLock();
289
                            dynamic_cast<CometWidget *>(iter2->second.second)->updateWidget();
290
                            iter->first->triggerUpdate();
291
                        }
292
                    }
293
                }
294
            }
295
296
            virtual void updateWidget() = 0; // TBD: Convert this to a signal/slot instead of a virtual function
297
298
            template <class>
299
            friend class CometWidgetFactory;
300
        private:
301
            unsigned long int id; // Group ID
302
303
            boost::mutex CometMutex;
304
        };
305
    }
306
}
307
308
#endif
309
</pre>
310
311
h3. Code Review
312
313
updateOthers() updates each widget in the widget group and it should be called whenever a change was made in the widget.
314
updateWidget should be a slot that will be connected to signals that update the widget itself.
315
316
In the next part of the article I will demonstrate how to re-write the SimpleChat example very simply.
317 6 Ana Evans
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
http://depuyhiprecallnewscenter.webs.com/
346
http://depuyhipdevicerecall.blogspot.com/
347
http://hipfractures.spruz.com/
348
http://zithromaxheartlawsuit.webs.com/