Project

General

Profile

Actions

SimpleChat made easy » History » Revision 2

« Previous | Revision 2/12 (diff) | Next »
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

#ifndef CometApplication_HPP
#define CometApplication_HPP

//
#include
#include
#include
//

//
#include
//

//
#include
#include
//

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

h2. CometApplication.cpp

#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;
}

}

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.

Updated by Omer Katz almost 15 years ago · 2 revisions