t e m p o r a l 
 d o o r w a y 

Distinguishing Event Handlers With Identical Signatures

 

Introduction

An occasional problem arises with event handlers. Imagine the following two declarations:

typedef void __fastcall (__closure *TEvent1)(TEventHandlerTest *OneSender);
typedef void __fastcall (__closure *TEvent2)(TEventHandlerTest *TwoSender);

When you double-click in the object inspector to create the handlers, you get:

void __fastcall TForm1::EventHandlerTest1Event1(TEventHandlerTest *OneSender)
void __fastcall TForm1::EventHandlerTest1Event2(TEventHandlerTest *OneSender)

Obviously, the IDE is generating the header from the signature. When it looks for the parameter names, it takes the names from the first matching signature instead of the actual closure definition. Whether or not this is a bug (and in my opinion it is, since the information to properly resolve this should be available), there is a need for a way to deal with this.

The Solution

The most desirable solution might be to use a typedef to alter the signature. For instance:

typedef TEventHandlerTest THandler1;
typedef TEventHandlerTest THandler2;

typedef void __fastcall (__closure *TEvent1)(THandler1 *OneSender);
typedef void __fastcall (__closure *TEvent2)(THandler2 *TwoSender);

Unfortunately, this simple solution does not alter the signature, and the original problem is unsolved.

The second most desirable solution, almost as simple, is as shown in the following component header:

      class IgnoreMe1{}; // Empty class used as place marker
      class IgnoreMe2{};

class PACKAGE TEventHandlerTest : public TComponent
{
   public:

      typedef void __fastcall (__closure *TEvent1)(TEventHandlerTest *OneSender,IgnoreMe1 theIgnoreMe1);
      typedef void __fastcall (__closure *TEvent2)(TEventHandlerTest *TwoSender,IgnoreMe2 theIgnoreMe2);

   private:

      TEvent1 myEvent1;
      TEvent2 myEvent2;

   protected:

        void __fastcall DoEvent1(void);

   public:

      __fastcall TEventHandlerTest(TComponent* Owner);

   __published:

      __property TEvent1 Event1 = {read=myEvent1,write=myEvent1};
      __property TEvent2 Event2 = {read=myEvent2,write=myEvent2};
};


Obviously, this does require a litle special work from the component writer. For instance:

void __fastcall DoEvent1(void);

must be written as

void __fastcall TEventHandlerTest::DoEvent1(void)
{
   if (myEvent1 != NULL) myEvent1(this,IgnoreMe1());
};

And, of course, the resulting handler has a parameter to ignore.

void __fastcall TForm1::EventHandlerTest1Event1(TEventHandlerTest *OneSender, IgnoreMe1 theIgnoreMe1)

void __fastcall TForm1::EventHandlerTest1Event2(TEventHandlerTest *TwoSender, IgnoreMe2 theIgnoreMe2)

Still - maybe it's worth it to you...

Extending The Example

So long as the placeholder class is somewhere in the parameter list, the names will be preserved. Thus, any number of parameters can be used and their names will be preserved.

You can create a common header file to support this across many components:

#ifndef IgnoreEmptyClassesForEventHandlerH
#define IgnoreEmptyClassesForEventHandlerH      

      class IgnoreMe1{}; // Empty class used as place marker
      class IgnoreMe2{};
      class IgnoreMe3{};

      class IgnoreMe4{}; // etc.

#endif

Then #include this file anywhere you need it and you'll be able to consistently deal with this problem.

Copyright © 2004 by Mark Cashman (unless otherwise indicated), All Rights Reserved