Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

C++ Delegates and Borland's C++ Builder Event Handling - Part II

4.67/5 (7 votes)
26 Nov 2009CPOL5 min read 47K  
Describes how Borland smartly used its keyword __closure to handle events

Introduction

Welcome to Part II of our CPP Delegates article.

In Part_II we are going to illustrate how Borland's C++ Builder handles Windows Events using its proprietary keyword "__closure" and the delegation model instead of Inheritance as happens in MFC library and Win32 API.

Background

I assume that the reader has read Part 1 of this article. Also I assume that he/she has intermediate to advanced knowledge of Borland's C++ Builder IDE.

Part II - Using Closures to Handle Events

What are Delegates?

When you want another person to replace you in a job, this is called delegating this person. We may see a manager of some company delegates one of the experienced employees to replace him for a period of time when he is not available.

In the programming world, this happens by asking some function in some class to do some job that our class cannot do. The most sensitive example for this model is the “Event Handling”. When you press a button, a message is sent to the operating system (namely Microsoft Windows) declaring that you have clicked your mouse's left button. This is called Event. The job that your software does as a response to this message or event is called Event Handling. The function which is responsible for handling this event (i.e. the code that is actually executed when you press a button) is called the Event Handler.

Borland’s Unique Way to Handle Events in C++

Borland C++ Builder uses closures to implement the delegation model and hence the “Event Handling story”. This happens by declaring a closure that can point to a sort of methods in a form like this:

  • C++
    __property OnClick; // declared as __published in class TButton in stdctrls.hpp
  • C++
    __property Classes::TNotifyEvent OnClick = {read=FOnClick, write=FOnClick, 
    stored=IsOnClickStored}; 	// declared as protected in 
    			//class TControl in controls.hpp
  • C++
    Classes::TNotifyEvent FOnClick; // declared as private in TControl in controls.hpp
  • C++
    typedef void __fastcall (__closure *TNotifyEvent)
    (System::TObject* Sender); 	// declared as global in namespace 
    			// Classes in classes.hpp

To understand what this is, let’s explain it from bottom to up:

  • First of all, we have a closure type that can point to a function that takes a parameter of type TObject* and returns nothing, this should be the “Event Handler” that is going to be written by some programmer in some class. This closure type has assigned an alias called TNotifyEvent using typedef keyword.
  • This alias is used to declare a private closure in TControl class to handle OnClick event.
  • Since it is private, we cannot call it or assign a value to it, so Borland used its own method to change private data as if they are public. This happens using __property keyword. Intuitively this property should be declared as protected/public/__published not private as well.
  • All above points are the infrastructure to the class that you are expected to use, which is TButton class. Hence we should raise the access level of our property (OnClick closure) to be public, but this will allow us to assign values to it in code only. What if we want to assign this value using our RAD Environment (the IDE)! Here is another new keyword added by Borland to the language. It is __published keyword. In short, it gives you an access level equal to public keyword, but it allows you to assign values using a few mouse clicks on Borland’s IDE with no need to add a single line to code.

That is it. To see what happens when you click a button, let’s reverse what we said.

Assume you have written some code in some function that finishes a certain job, and you named your function as OnClickHandler.
Using the IDE, we may assign this function address to our closure (OnClick) which is a member of class TButton.

But, wait, OnClick is not a variable in itself. It is just a gate to allow to you assign values to or retrieve them from real members (usually private) using assignment operator (l-value/r-value) instead of using old-fashioned Set/Get methods. Our real closure that is going to be assigned this address is FOnClick closure.
This assignment is called delegation.

TButton objects are programmed to capture Click-Messages (events) sent by your operating system and then call FOnClick closure as a response to this message (event), but did TButton class handled the event? No, the real handler is your function which is written somewhere in some class. That is TButton delegates your function to replace it in handling such events. By this way, each object of type TButton has its own handler. This will not be the case if we defined the handler in the TButton class, since it will be general for all TButton objects. This means that all buttons would have the same job.

You may wonder, what if we used inheritance and virtual functions to implement a specific handler to each TButton object. You are right. This is one solution by declaring a handler as a virtual function that has no body, and then you have to inherit from this class every time you want to add a button to your form, moreover you have to redefine your virtual function to suit your desired job.

Assume you are expected to program a scientific calculator that has more than 30 different buttons with very different jobs. Using Windows API, Borland’s OWL, or Microsoft’s MFC you have to do more than 30 inheritances. But with Borland’s C++ Builder, you have to make more than 30 assignment statements. Or even use mouse clicks in the IDE.

Redefining your virtual function is equal to defining your handlers, and there is no way to avoid this. Borland’s products are not smart enough to expect your button jobs.

Final Words

Just imagine your source-code file that contains 30 inheritances, and another that contains 30 assignments if any.

If you understood this article, you may be able to understand Charlie Calvert’s statement in his Borland C++ Builder Unleashed book.

“Delegation is an alternative to inheritance. It's a trick to allow you to receive the same benefits as inheritance but with less work.”

Thank you Borland.

About the Code

Code in part I and class traces in part II are tested against Borland's C++ Builder 6.0 Enterprise Edition.

Windows XP service pack 3.0 platform.

History

  • 26th November, 2009: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)