Boost logo

Boost :

From: Jesse Jones (jesjones_at_[hidden])
Date: 2001-05-09 19:58:14


At 4:27 PM +0300 5/9/01, Peter Dimov wrote:
>From: "Jesse Jones" <jesjones_at_[hidden]>
>
>> At 3:15 PM +0300 5/8/01, Peter Dimov wrote:
>
>> >class Timer
>> >{
>> >public:
>> > Timer(function<void> const & onTick, int interval);
>> >};
>>
>> I like this better than the cookie approach, but it's still not
>> quite as clean as my original approach. And, as I mentioned, timers
>
>I'd argue that this is a cleaner approach. An active timer is a resource,
>and a resource should be represented by an object.

The part that I don't like is that it requires touching the class
declaration whenever you want to add a timer to a class. Of course,
this isn't a huge deal, especially when you consider that it will be
easier to write exception safe code that way if the timer exists for
the lifetime of the object.

> > are the easy case. The harder case is handling menu commands. I don't
>> want 46 cookies floating around and I don't want 46 helper objects
>> either. :-)
>
>46 cookies are a pain because of the resource management issues, but 46
>helper objects do not present the same problem. They manage themselves. You
>only have to control their lifetime - which you do in the original design
>anyway by using explicit 'Add' and 'Remove' functions. Constructors and
>destructors do a better job.

Menu command handlers are generally only installed upon
activation/deactivation events or gain/lose keyboard focus events so
it's not quite that simple. I suppose clients could do something like
this:

class MyControl {
protected:
    virtual void OnActivate();
    virtual void OnActivate();

private:
    struct MyCommands {
       MyCommands(MyControl* owner);
    private:
       XCommandHandler mCommand1;
       XCommandHandler mCommand2;
       XCommandHandler mCommand3;
    };
    friend struct MyCommands;

private:
    MyCommands* mCommands;
};

MyControl ::MyCommands::MyCommands(MyControl* owner) :
    mCommand1(L"cmd1", bind(owner, &MyControl::DoCommand1)),
    mCommand1(L"cmd2", bind(owner, &MyControl::DoCommand2)),
    mCommand1(L"cmd3", bind(owner, &MyControl::DoCommand3))
{
}

void MyControl::OnActivate()
{
    ASSERT(mCommands == nil);
   
    mCommands = new MyCommands;
}

void MyControl::OnActivate()
{
    ASSERT(mCommands != nil);
   
    delete mCommands;
    mCommands = nil;
}

Now contrast this with what I'm doing now:

class MyControl {
protected:
    virtual void OnActivate();
    virtual void OnActivate();
};

void MyControl::OnActivate()
{
    IMenuHandlerPtr h(L"Application");
    h->RegisterCommand(L"cmd1", bind(this, &MyControl::DoCommand1));
    h->RegisterCommand(L"cmd2", bind(this, &MyControl::DoCommand2));
    h->RegisterCommand(L"cmd3", bind(this, &MyControl::DoCommand3));
}

void MyControl::OnActivate()
{
    IMenuHandlerPtr h(L"Application");
    h->UnRegisterCommand(bind(this, &MyControl::DoCommand1));
    h->UnRegisterCommand(bind(this, &MyControl::DoCommand2));
    h->UnRegisterCommand(bind(this, &MyControl::DoCommand3));
}

This is a lot simpler for clients. The only downside is that if an
exception is thrown while we're registering the commands the app will
be left in a bad state. But how likely is this? The only exceptions
that will be thrown are due to out of memory errors and this is just
not going to happen on any modern OS: the VM will grind to a halt
long before such piddly allocations fail.

   -- Jesse


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk