Part II.
Where it is told about closures and "functional containers" - data structures that can store pointers to functional objects of various types
std::function
While the functional paradigm proclaims that "everything is a function," in imperative programming we sometimes encounter the need to manipulate functions like with ordinary objects. Usually this happens by creating a pointer to the address of the procedure or method. However, in this case and others, high-level languages leave it up to us the use of the function pointer, the care of the accordance to the call signature, the passed parameters and other low-level pleasures. Naturally, if we intend to manipulate variables that store pointers to procedures or methods, functors and lambda functions, we inevitably run into headaches due to the different intrinsic nature of these objects. Meanwhile, we would like to think about our algorithm, and not to unravel the puzzles that the compiler tosses us. All of the above objects have only one, but extremely important, common property - they are callable. Is there a way to refer to them in a uniform style?
STL says: "Yes", and this way is the using of std::function.
Instances of std::function can store, copy, and invoke any callable target functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members |
cppreference says. This is what needed for our “functional” purposes - we can think of this (and use it) as a variable in the our “functional” world.
C++ is to this day is a strongly typed language. Together with std::function we can refuse to use arbitrary pointers for tricks with references to our functions, functors, member functions, and lambdas. In the world of function pointers, this is something like the legalization of types for such entities. We can define a template, declare a variable of a callable type and assign necessary values to them when we want.
Syntax
Class template std::function is a general-purpose polymorphic function wrapper.
template< class > class function;
or
template< class R, class... Args > class function<R(Args...)>
Example
Let's look at the example of a console application which generates a vector of integers and calculates a number of occurrences in a specified diapason.
We will use std::for_each and std::count_if algorithms from STL to сircumvent all elements (in particular, for print) and to count special condition.
The functor class Adder will be used to fill a vector with integer elements. A definition of its operator()method differs from functor Generator considered in one of the previous examples in Part I. Here we add vector elements ourselves in a for loop, and don’t use std::generate. In the operator of the functor an argument from the outside is given to calculate the new element. Thus, we do not need to store it inside.
class Adder {
...
public: void operator()(int val) { _vec->push_back(val); }
...
};
Pay attention to how we pass the vector to the inside of the constructor of the Adder functor, and store the pointer to it in a private field.
private: vector<int>* _vec;
...
Adder(vector<int>& vec) { _vec = &vec; };
We can not declare a vector variable directly in a private field because even if we pass the original vector by reference into the constructor, this field will be assigned a local copy, to which elements will be added.
...
Adder add(v);
...
Note that actions stored in the std::function variable can be of a completely different nature, matching by the required call signature only. We can combine heterogeneous actions in this role, and it is possible to think about applications when one can consider not the consistent act of the same operation to set of objects, but the impact of an ordered (or unordered!) set of operations to the same object.
Let's look at the example of a conveyor belt in terms of trash sorting, the subject is an employee or an automated machine performing such sorting. If we consider the assembly line of airplanes, it may be more convenient to consider the sequence of operations for each airplane separately. In fact, we are dealing with the product of sets of objects and operations, and this statement of the problem determines the angle of view on the situation. The std::function gives us the ability to naturally manipulate operations - it already smells like true functional programming!
Returning to our example we declare a variable for the abstract operation:
function<void(int)> operation;
We can assign to this variable functor Adder, which knows how to add a passed value as a new element into the vector and later re-assign it to the lambda expression that print all passed values.
Checking the condition requires passing a predicate. Now we are not limited in the choice of tools regarding what this predicate must be, and std::function gives us additional flexibility here.
Firstly, define a functor named Predicator that will store a border of ranges in its private fields and return result to check its operator().
Alternatively, we can declare the predicate as a lambda expression where checking the value passed an argument, while diapason borders are passed via capture scope. Looks rather natural, doesn’t it? We don’t want to remember border values every time while performing a check for some number. At the same time, we don’t use global variables in an uncontrolled manner.
In this code, you will see how a variable of type
function<bool(int)> predicate
and the assignment of lambda performed. Later we use the ability to assign this variable a functional object. However, we can define a lambda right at the location of calling count_if, if this makes the code visible and the following is done too.
#pragma hdrstop
#pragma argsused
#ifdef _WIN32
#include <tchar.h>
#else
typedef char _TCHAR;
#define _tmain main
#endif
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
class Adder {
private: vector<int>* _vec;
public: void operator()(int val)
{
_vec->push_back(val);
cout << val << " inserted" << endl;
}
public: Adder(vector<int>& vec) { _vec = &vec; };
};
class Predicator {
private:
int _a;
int _b;
public:
bool operator()(int n)
{
return (_a <= n) && (n < _b);
}
Predicator(int a, int b)
{
_a = a;
_b = b;
}
};
typedef void (*prfn)(int value);
prfn printfn = [](int value){ cout << value << " "; };
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v;
int m;
cout << "Enter array size: ";
cin >> m;
Adder add(v);
function<void(int)> prn = printfn;
function<void(int)> operation;
operation = add;
for (int val = 0; val < m; val++)
{
operation(val); /*add(val);*/
}
operation = prn;
cout << "Content of array: ";
for_each(v.begin(), v.end(), operation); /*prn*/
cout << endl;;
int lowerBound = 0, upperBound = 0;
cout << "Enter the bounds of the range: ";
cin >> lowerBound >> upperBound;
cout << "Number of elements in the interval [" << lowerBound << ", " << upperBound << ")" << endl;
int result;
//using of lambda-expression
result = count_if(v.begin(), v.end(),
[lowerBound, upperBound] (int _n)
{
return lowerBound <= _n && _n < upperBound;
}
);
cout << "Calculated with lambda-expression: " << result << endl;
//using of functor
Predicator prdobj = Predicator(lowerBound, upperBound);
result = count_if(v.begin(), v.end(), prdobj);
cout << "Calculated with functor: " << result << endl;
//std::function predicate based on the lambda-expression
function<bool(int)> predicate = [lowerBound, upperBound] (int _n)
{
return lowerBound <= _n && _n < upperBound;
}
;
result = count_if(v.begin(), v.end(), predicate);
cout << "Calculated with std::function predicate based on the lambda-expression: " << result << endl;
//std::function predicate based on the functional object
predicate = prdobj;
result = count_if(v.begin(), v.end(), predicate);
cout << "Calculated with std::function predicate based on the functional object: " << result << endl;
cin.ignore(2);
return EXIT_SUCCESS;
}
![]()
It is possible that these are far-fetched examples and you are not very impressed. That said, there is a case where it's not easy to do without using std::function.
What makes functional programming functional is that a function can return a function, and does so in many cases. Likewise, a lambda can generate a lambda. In addition, the role of closures, of course, is not to make code more concise although for imperative programming this is a serious argument.
Each lambda function has some context at the time of its call. In the body of a lambda, we have access to some variables of the calling code, accordingly to its capture-list. With the help of these variables we can define a new lambda function and return it as a result. After that, the calling code section can exit the scope, but the generated lambda function continues to live its life and can be called at any time!
At the same time, the variables that are “closed” in this way must be available all this time. Even after we forget about the origin of our lambda function! There are interesting effects, which can be very specific for each language that supports closures.
Let's try to solve the well-known problem of computing the number of elements of a vector that belong to a given interval using lambdas, but we will try to do it without global variables.
After the familiar code responsible for initializing the vector, we are faced with the definition of the lambda function:
auto lambdagen = [](bool including)->function<bool(int)>
This is exactly the definition that we could not make previously when we considered the possibility of generating a lambda function as a result! Note that the auto keyword here doesn’t define the type of the result of the lambda function, but the type of the variable that will store the reference to such a function. At the same time the lambda function will return a variable of a new type for us - function<bool(int)> and this type is the required predicate type!
Next, in the body of the lambdagen function we declare lowerBound and upperBound variables which represent the borders of the interval. Note, that they are local variables inside the body of the lambda-function only!
It’s assumed here that including the parameter of the lambdagen allows us to interpret the interval type as closed [lowerBound, upperBound] when it’s included and as open ]lowerBound, upperBound[ otherwise.
Our goal is that our lambda is able to generate another lambda accordingly to the options which could be used as std::count_if predicate:
return [lowerBound, upperBound, including](int val)->bool { ... }
You have already learned how to use the capture list, and you will not be surprised at the use of variables in the formula for calculating the value of the predicate:
((lowerBound < val)&&(val < upperBound))||(including && (val == lowerBound || val == upperBound))
What happens after we assign the result of the generated lambda function to an external predicate variable?
predicate = lambdagen(false);
lambdagen already returned its result and generated a lambda assigned to the predicate variable. In a functional language Haskell, we are dealing with currying and lazy execution, and it was not anything extraordinary. Here, however, we are dealing with C++ where the instructions are executed strictly sequentially. Thus, lambdagen internal scope no longer exists! What do you think, what variables will appear in this formula after the call:
result = count_if(v.begin(), v.end(), predicate);
It's amazing, but it works without error! The function is dead, but some of its local variables are still alive! This is called capturing (I would call these “ghost variables” - although Philip van der Decken was not a programmer, ha-ha!)
Incidentally, the closure allowed us to keep these ghost variables in the ghost state of the ghost object. Within the framework of functional programming, closures allow for simulating the passing of an internal state for subsequent processing.
Note that we actually have a parameterized generation of the lambda function. So we can receive a predicate for the closed interval case:
predicate = lambdagen(true);
Do not be offended if you have to enter the limits of the range twice—this is inevitable—because they are entered inside the generated lambda every time it’s called.
#pragma hdrstop
#pragma argsused
#ifdef _WIN32
#include <tchar.h>
#else
typedef char _TCHAR;
#define _tmain main
#endif
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int m;
cout << "Enter array size: ";
cin >> m;
vector<int> v(m);
int k = 0;
generate(v.begin(), v.end(), [&](){return k++;});
cout << "Content of array: ";
for_each(v.begin(), v.end(), [](int value){ cout << value << " "; });
cout << endl;
function<bool(int)> predicate;
auto lambdagen =
[](bool including)->function<bool(int)>
{
int lowerBound = 0, upperBound = 0;
cout << "Enter the bounds of the range: ";
cin >> lowerBound >> upperBound;
return [lowerBound, upperBound, including](int val)->bool
{
return ((lowerBound < val)&&(val < upperBound))||(including && (val == lowerBound || val == upperBound));
};
};
int result;
predicate = lambdagen(false);
result = count_if(v.begin(), v.end(), predicate);
cout << "Number of elements in the open interval: " << result << endl;
predicate = lambdagen(true);
result = count_if(v.begin(), v.end(), predicate);
cout << "Number of elements in the closed interval: " << result << endl;
cin.ignore(2);
return EXIT_SUCCESS;
}
![]()
C++ Builder __closure keyword
Our presentation could not be considered complete without mention of the keyword __closure, which appeared in Borland C++ Builder at the turn of the century, long before we could even dream of the functional features of C++ in a serious way.
A keyword ahead of its time
A function pointer allows make reference to a global function and call it when necessary. However, for Class member functions (methods) it is not sufficient because two pointers are required: one for the originating object and the second for the method address. There is a need to create "2-in-1" pointers to methods and the __closure keyword allows us perform that task.
The Role in the VCL
When building a UI, instead of manually creating each window element, you use a graphical designer and then just write the handler for the event you are interested in. How can this be implemented in practice? Since you use standard window elements, you need to override the behavior of such a component for a particular event, i.e. create its descendant, as it’s done in classic OOP.
Suppose we have 2 buttons. They are two instances of the same TButton class that have a different behaviors. The first one prints “Yes” when pressed, second one prints “No.” In the case of the traditional OOP approach with the absence of delegation, the solution would be to create classes TButtonYes and TButtonNo with a redefined behavior at the click event, then create one instance of each class. This is contrary to our intuition that there is a single class, and that there are the discrepant instances of that class only!
VCL opened up another pathway here offering an elegant solution. Instead of defining behavior in the class for each visual component in your application, just delegate the processing of the relevant event to the interested class. Therefore, the buttons always remain just buttons, regardless of what kind of work the handler of their click performs!
It sounds promising, but how do you implement it in practice if the VCL developer does not know which class will delegate the right to process the click? Note, “pure” C++ only had plain function pointers, not object-method pointers. At the same time the delegate here is the class method and it is the pointer to the class member-function that is required.
Look at the following definition:
typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);
This is the type of the event notification signature. Sender contains a pointer to the object in which the event occurred (because there are many buttons that can be presented on the form), in order for the delegate to have access to the properties and methods of the object. It represents a kind of feedback while TNotifyEvent is the declared type whose variable can contain a reference to a delegate. Thus, if a visual component has a property of TNotifyEvent type, it can be assigned a reference to a handler, which is a method of the interested class that will be executed as soon as the notification of the event that has occurred is received.
The declaration of the member-function-pointer with the __closure keyword allows to delegate the implementation of the member-function to any arbitrary class, rather than a descendant of a specific one.
Syntax
typedef <type> ( __closure * <id> ) (<param list>);
where
id - the identifier of the pointer to the function-member of some class
Examples
Let's compare what the VCL application would look like with and without __closure. To show this we will create a small demo project. Please, create an empty VCL Forms application as shown:
![]()
After it put TMemo component on the form.
First, we try to create two buttons which will print “Yes” or “No” when pressed. Our goal is to achieve this without using VCL event mechanisms (onClick property).
Nevertheless, we do not need to implement self-handling messages from the mouse. We have already a parent class TButton in our hands with implemented dynamic Click() method which called every time when left mouse key pressed. Normally, it perform some additional actions and call handler assigned to TButton::onClick property. But taking into account our obligations, we can override his standard behavior for each of the required cases (printing “Yes” and printing “No”) .
So we had to create two separate classes TButtonYes and TButtonNo with behavior accordingly to our task:
class TButtonYes : public TButton{
public: virtual __fastcall TButtonYes(TComponent* Owner) : TButton(Owner)
{
Caption = L"Yes";
}
void __fastcall printYes()
{
((TForm1*)(GetParentForm(this, true)))->Memo1->Lines->Add(L"Yes"); // this->Parent
}
DYNAMIC void __fastcall Click(void)
{
printYes();
}
};
class TButtonNo : public TButton{
public: virtual __fastcall TButtonNo(TComponent* Owner) : TButton(Owner)
{
Caption = L"No";
}
void __fastcall printNo()
{
((TForm1*)(GetParentForm(this, true)))->Memo1->Lines->Add(L"No");
}
DYNAMIC void __fastcall Click(void)
{
printNo();
}
};
As you can see, Click() method is overridden thus to call the member function print.. of the button class for processing instead of passing the execution to the delegate specified in the onClick property as doing by default. In addition, the size and location of the each button on the TForm1 form are described here, since we will create it dynamically at run-time.
Now we can declare pointers to these two buttons objects:
TButton *ButtonYes;
TButton *ButtonNo;
Next can double-click in the main form and put followed code in the constructor of the TForm1:
ButtonYes = new TButtonYes(this);
ButtonNo = new TButtonNo(this);
and (remember, our buttons are created in runtime!) determine the place that these components will occupy on the form:
ButtonYes->Top = 48;
ButtonYes->Left = 8;
ButtonYes->Parent = this;
ButtonNo->Top = 48;
ButtonNo->Left = 88;
ButtonNo->Parent = this;
Finally, you can run the application now and see the results.
Again: we did what we had to do within the framework of the concept of "pure" OOP, without resorting to the use of delegation. Quite a lot of code for the task of creating just two different buttons, isn’t it?
But You can use the OnClick property in a much more convenient way and __closure keyword allows for it. You can create new project, but to feel the difference let's continue working with this project.
Keep in mind! The following example may seem confusing if you just read it. But in order to understand how it works it will only need to compile it and try to click on the buttons. Our goal now is to demonstrate that the __closure keyword makes it easy to juggle with ease button handlers in runtime.
Suppose that we have two buttons located on the surface of our form. Each button click has its own handler. This handler will write an asterisk on the surface of the button, and will assign a special procedure as a handler of this button click (instead of himself).
This special procedure (implemented in a separate class) will compare the assigned button handlers and exchange them in places, but in the case where both buttons have the same handler (this exchange is meaningless), this procedure will return the assigned handlers to their original state.
Signatures on the surface of buttons are initialized by their names, this is done for better remembering by the user who launched the program for the first time, but does not press anything. These names change during operation and do not return to their original state but they receive the "Yes" and "No" captions.
Place two TButton’s onto the form (not at the same place where ButtonYes and ButtonNo appear, if you are continue previous example) and from the Events tab double-click on the ‘OnClick’ property field. Thus, TForm1::Button1Click and TForm1::Button2Click handlers templates will be created.
![]()
Type the following code for this handlers:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");
Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"
+L"\t Handler: ''TForm1::Button1Click''");
((TButton*) Sender)->OnClick = &delegate.ExternalClickHandler;
((TButton*) Sender)->Caption = L"*";
Form1->Memo1->Lines->Add(((TButton*) Sender)->Name+L"->OnClick property was now assigned to external handler");
Form1->Memo1->Lines->Add(L"----------------------------------------------------");
}
and
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");
Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"
+L"\t Handler: ''TForm1::Button2Click''");
((TButton*) Sender)->OnClick = &delegate.ExternalClickHandler;
((TButton*) Sender)->Caption = L"*";
Form1->Memo1->Lines->Add(((TButton*) Sender)->Name+L"->OnClick property was now assigned to external handler");
Form1->Memo1->Lines->Add(L"----------------------------------------------------");
}
If you open the .dfm file now you can right-click on the form and select “View as Text” or save project changes and open .dfm in any text viewer. You should see something like the following:
object Button1: TButton
Left = 8
Top = 16
...
OnClick = Button1Click
end
object Button2: TButton
Left = 104
Top = 16
...
OnClick = Button2Click
end
You can see that the OnClick handler is assigned to the TForm1 method automatically and it’s much easy now to create a derived class of button and override its behavior, isn’t it? But don’t be confused that you can only use VCL component methods as handlers. Let's create our own class, whose method can act as a delegate:
class Delegator{
public:
void _fastcall ExternalClickHandler(TObject *Sender)
{
Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");
Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"
+L"\t Handler: ''Delegator::ExternalClickHandler''");
if (Form1->Button1->OnClick == Form1->Button2->OnClick)
{
Form1->Memo1->Lines->Add(L"Restore click handlers...");
Form1->Button1->OnClick = Form1->Button1Click;
Form1->Button2->OnClick = Form1->Button2Click;
Form1->Button1->Caption = L"YES";
Form1->Button2->Caption = L"NO";
Form1->Memo1->Lines->Add(L"From Delegator: OnClick properties was restored to default Form handlers");
}
else
{
Form1->Memo1->Lines->Add(L"Exchange click handlers...");
TNotifyEvent event;
event = Form1->Button1->OnClick;
Form1->Button1->OnClick = Form1->Button2->OnClick;
Form1->Button2->OnClick = event;
UnicodeString buf = Form1->Button1->Caption;
Form1->Button1->Caption = Form1->Button2->Caption;
Form1->Button2->Caption = buf;
}
Form1->Memo1->Lines->Add(L"----------------------------------------------------");
}
};
and declare its instance:
Delegator delegate;
Our handlers operate with pointers to delegates in a completely arbitrary style. Let's try to understand the functionality of this code.
We have two handlers TForm1::Button1Click and TForm1::Button2Click which acts likely way. But notice that matching the names of the handlers to the names of the components whose events they process are not necessarily! Generally speaking, this restriction is dictated only by common sense, but if necessary we can manipulate it arbitrarily.
First, prints to the log the caption of the button that was pressed. Second, prints the Name of the button - this is the only thing that always remains in place. Third, external handler assigns to the button onClick property and finally button caption changes to the “*” symbol.
Now a few words about the external handler. It is a ExternalClickHandler method of the Delegator class. When called it prints the caption of the button that was pressed into the log too. Next it compare the handlers of the buttons Button1::OnClick and Button2::OnClick both. If it is the same handler (i.e. ExternalClickHandler because it is not called otherway) then both handlers was changed from initial and it restores the init values and captions. Else one button has external handler for onClick property and ExternalClickHandler exchange handlers (and captions) of the Button1::OnClick and Button2::OnClick.
In a sense, this task resembles the next chain of states:
![]()
Each state includes the values of the caption (signed on the button surface) and handler (in the rectangle from below) for each of the buttons Button1 (on the left) and Button2 (on the right). For compactness the handlers TForm1::Button1Click and TForm1::Button2Click are signed as Button1Click and Button2Click respectively, and the Delegator::ExternalClickHandler handler is designated as external.
Note that the chain of states is not closed, and the transitions shown in the following figure are possible from the lower state:
![]()
Thus, the second state diagram can be viewed as a continuation of the first, but if you initialize the Button1 and Button2 button captions as "YES" and "NO" respectively, the top diagram will turn into the bottom one. You can do this by changing the Caption property of both buttons at the design stage, or at runtime in the TForm1 constructor. I saved the default button names in order to remember the location of the components TForm1::Button1 and TForm1::Button2 (but note that this values can be exchanges and in some states Button1 has caption “Button2”).
It's not so difficult to understand if you play with this program yourself.
Now we can try to reproduce the following log:
Yes
No
========== Button1 click ==========
Sender: ''Button1'' Handler: ''TForm1::Button1Click''
Button1->OnClick property was now assigned to external handler
----------------------------------------------------
========== * click ==========
Sender: ''Button1'' Handler: ''Delegator::ExternalClickHandler''
Exchange click handlers...
----------------------------------------------------
========== Button2 click ==========
Sender: ''Button1'' Handler: ''TForm1::Button2Click''
Button1->OnClick property was now assigned to external handler
----------------------------------------------------
========== * click ==========
Sender: ''Button2'' Handler: ''Delegator::ExternalClickHandler''
Restore click handlers...
From Delegator: OnClick properties was restored to default Form handlers
----------------------------------------------------
========== NO click ==========
Sender: ''Button2'' Handler: ''TForm1::Button2Click''
Button2->OnClick property was now assigned to external handler
----------------------------------------------------
========== * click ==========
Sender: ''Button2'' Handler: ''Delegator::ExternalClickHandler''
Exchange click handlers...
----------------------------------------------------
========== YES click ==========
Sender: ''Button2'' Handler: ''TForm1::Button1Click''
Button2->OnClick property was now assigned to external handler
----------------------------------------------------
========== * click ==========
Sender: ''Button2'' Handler: ''Delegator::ExternalClickHandler''
Restore click handlers...
From Delegator: OnClick properties was restored to default Form handlers
----------------------------------------------------
First, we clicked created at run-time “Yes” and “No” buttons for which we has implemented the different behaviors using inheritance instead delegation mechanism, as you remember. Next, we can press a sequence of buttons corresponding to one of the paths in the state graph.
After it the main form of our application will looks as follows:
![]()
I'm forced to bring here the contents of the header file: this way in case something goes wrong - so that you could understand the cause of the problem.
Unit1.h:
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TMemo *Memo1;
TButton *Button1;
TButton *Button2;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
The full content of the Unit1.cpp source is:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
class TButtonYes : public TButton{
public: virtual __fastcall TButtonYes(TComponent* Owner) : TButton(Owner)
{
Caption = L"Yes";
}
void __fastcall printYes()
{
((TForm1*)(GetParentForm(this, true)))->Memo1->Lines->Add(L"Yes");
}
DYNAMIC void __fastcall Click(void)
{
printYes();
}
};
class TButtonNo : public TButton{
public: virtual __fastcall TButtonNo(TComponent* Owner) : TButton(Owner)
{
Caption = L"No";
}
void __fastcall printNo()
{
((TForm1*)(GetParentForm(this, true)))->Memo1->Lines->Add(L"No");
}
DYNAMIC void __fastcall Click(void)
{
printNo();
}
};
TButton *ButtonYes;
TButton *ButtonNo;
//----------------------------------------------------------------------
class Delegator{
public:
void _fastcall ExternalClickHandler(TObject *Sender)
{
Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");
Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"
+L"\t Handler: ''Delegator::ExternalClickHandler''");
if (Form1->Button1->OnClick == Form1->Button2->OnClick)
{
Form1->Memo1->Lines->Add(L"Restore click handlers...");
Form1->Button1->OnClick = Form1->Button1Click;
Form1->Button2->OnClick = Form1->Button2Click;
Form1->Button1->Caption = L"YES";
Form1->Button2->Caption = L"NO";
Form1->Memo1->Lines->Add(L"From Delegator: OnClick properties was restored to default Form handlers");
}
else
{
Form1->Memo1->Lines->Add(L"Exchange click handlers...");
TNotifyEvent event;
event = Form1->Button1->OnClick;
Form1->Button1->OnClick = Form1->Button2->OnClick;
Form1->Button2->OnClick = event;
UnicodeString buf = Form1->Button1->Caption;
Form1->Button1->Caption = Form1->Button2->Caption;
Form1->Button2->Caption = buf;
}
Form1->Memo1->Lines->Add(L"----------------------------------------------------");
}
};
Delegator delegate;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ButtonYes = new TButtonYes(this);
ButtonYes->Top = 48;
ButtonYes->Left = 8;
ButtonYes->Parent = this;//!(TForm1*)(Owner);
ButtonNo = new TButtonNo(this);
ButtonNo->Top = 48;
ButtonNo->Left = 88;
ButtonNo->Parent = this;//!(TForm1*)(Owner);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");
Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"
+L"\t Handler: ''TForm1::Button1Click''");
((TButton*) Sender)->OnClick = &delegate.ExternalClickHandler;
((TButton*) Sender)->Caption = L"*";
Form1->Memo1->Lines->Add(((TButton*) Sender)->Name+L"->OnClick property was now assigned to external handler");
Form1->Memo1->Lines->Add(L"----------------------------------------------------");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Form1->Memo1->Lines->Add(L"========== "+((TButton*) Sender)->Caption + L" click ==========");
Form1->Memo1->Lines->Add(L"Sender: ''"+((TButton*) Sender)->Name+L"''"
+L"\t Handler: ''TForm1::Button2Click''");
((TButton*) Sender)->OnClick = &delegate.ExternalClickHandler;
((TButton*) Sender)->Caption = L"*";
Form1->Memo1->Lines->Add(((TButton*) Sender)->Name+L"->OnClick property was now assigned to external handler");
Form1->Memo1->Lines->Add(L"----------------------------------------------------");
}
//---------------------------------------------------------------------------
In addition, you can compare the resulting file of your form with the following:
object Form1: TForm1
Left = 0
Top = 0
BorderStyle = bsSingle
Caption = 'Form1'
ClientHeight = 498
ClientWidth = 603
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 120
TextHeight = 16
object Memo1: TMemo
Left = 181
Top = 0
Width = 422
Height = 498
Align = alRight
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -10
Font.Name = 'Tahoma'
Font.Style = []
Lines.Strings = (
'')
ParentFont = False
ScrollBars = ssVertical
TabOrder = 0
end
object Button1: TButton
Left = 8
Top = 8
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 1
OnClick = Button1Click
end
object Button2: TButton
Left = 88
Top = 8
Width = 75
Height = 25
Caption = 'Button2'
TabOrder = 2
OnClick = Button2Click
end
end
After experimenting with this code, you will not think about VCL event properties as something mysterious. Despite that, it still may not obvious what role the __closure mechanism plays here since it is hidden inside the TNotifyEvent type definition. Perhaps its place is only inside VCL?
Let's assume that we again need to count the number of elements of a vector in a certain range. We have a hierarchy of classes representing the base interval Diapasoner, subtypes of this interval such as open OpenInterval, and closed ClosedInterval.
The Diapasoner class has a virtual CheckCondition member-function which always returns true and must be overridden in derived classes.
How can I use its member-function as predicate in the std::count_if algorithm?
We can try to assign a method address to a pointer to a global function, but this does not work of course:
bool (*predicate)(int);//predicate is a pointer to a global function
// predicate = &Diapasoner::CheckCondition;
// [bcc32c Error] File1.cpp(89): assigning to 'bool (*)(int)' from incompatible type 'bool (Diapasoner::*)(int)'
Next, we can create an object of the Diapasoner type and use its trivially defined CheckCondition method:
bool (Diapasoner::*conditfun)(int); //conditfun is a member-function pointer
conditfun = &Diapasoner::CheckCondition;
This type of pointer can be used for subclasses:
intervalObj = new ClosedInterval(lowerBound, upperBound);
conditfun = &Diapasoner::CheckCondition;
We cannot use this variable to point to member-functions declared in subclasses or in third-party classes:
conditfun = &ClosedInterval::CheckCondition;
compile-time error raised: “assigning to 'bool (Diapasoner::*)(int)' from incompatible type 'bool (ClosedInterval::*)(int)': different classes ('Diapasoner' vs 'ClosedInterval')”
OR
conditfun = &ClosedInterval::inClosed;
failed too due Diapasoner class have no inClosed method so conditfun cannot to point to it;
OR
bool (ClosedInterval::*conditfun_)(int); //declare conditfun_ as a member-function pointer to methods of the ClosedInterval class
...
conditfun_ = &OpenInterval::CheckCondition;
compile-time error raised: “assigning to 'bool (ClosedInterval::*)(int)' from incompatible type 'bool (OpenInterval::*)(int)': different classes ('ClosedInterval' vs 'OpenInterval')”
Naturally, all these methods did not work, because we must monitor not only the coincidence of the method call signature but also the compatibility of the classes to which these methods belong. I.e. the child-class possesses the necessary information about its ancestor, however, it can not know anything about the internal arrangement of its descendants, brothers or even more unrelated classes.
But, perhaps, I can use a pointer to the member function directly in the STL call? All I need is to have a declared pointer to the member function of my class
Unfortunately, everything turned out to be different from the ideal world. When I try to compile
result = count_if(v.begin(), v.end(), intervalObj->*conditfun );
I receive a rather strange "internal compiler error." I did not come up with an exit better than using a ordinary loop for counting:
result = 0;
for (int val: v)
{
(intervalObj->*conditfun)(val) ? result++ : result=result;
}
I want to show how __closure offers us a more common solution: it is type-compatible based on the method signature not the class type - unlike, say, std::bind. In fact, why think about the type of object if all I want to focus on its member function? It would be cool, if in some cases we could work on the method, not on the class type, which is how it can work as a delegate.
Let’s declare the next variable:
bool (__closure *predicateX)(int value);
Now we can assign to it the method of any class, but only if this method has the bool (int value) call signature:
predicateX = intervalObj->CheckCondition;
and in another place later reassign predicateX variable:
interval0Obj = (OpenInterval*)intervalObj;
predicateX = interval0Obj->inHalfOpenLeft;
And, of course, you are free to use it in std::algorithm
result = count_if(v.begin(), v.end(), predicateX);
The trick is that the predicateX variable now contains the pair (&intervalObj, &CheckCondition), so it's enough data to recover the member-function within their object inside count_if.
Note, if this statement is taken out of context you can not determine from the object which method will be called here. This is not a std::function, but without going beyond the boundaries of the OOP it provides the flexibility: you can save a single pointer to the class method along with the class itself, which saves the headache of determining a specific pointer for each class.
#pragma hdrstop
#include <iostream>
#include <vector>
#include <algorithm>
#pragma argsused
#ifdef _WIN32
#include <tchar.h>
#else
typedef char _TCHAR;
#define _tmain main
#endif
using namespace std;
class Diapasoner
{
protected:
int _a;
int _b;
public:
virtual bool CheckCondition(int n) { return true; };
Diapasoner(int a, int b)
{
_a = a;
_b = b;
}
};
class ClosedInterval : public Diapasoner{
public: ClosedInterval (int a, int b) : Diapasoner(a, b) {}
virtual bool CheckCondition(int n)
{
return inClosed(n);
}
bool inClosed(int n)
{
return (_a <= n) && (n <= _b);
}
};
class OpenInterval : public Diapasoner{
public: OpenInterval (int a, int b) : Diapasoner(a, b) {}
virtual bool CheckCondition(int n)
{
return inOpen(n);
}
bool inOpen(int n)
{
return (_a < n) && (n < _b);
}
bool inHalfOpenRight(int n)
{
return (_a <= n) && (n < _b);
}
bool inHalfOpenLeft(int n)
{
return (_a < n) && (n <= _b);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
int m;
cout << "Enter array size: ";
cin >> m;
vector<int> v;
for (int val = 0; val < m; v.push_back(val++));
cout << "Received array: ";
for_each(v.begin(), v.end(), [v](int value){ cout << value << " ";});
cout << endl;
int lowerBound = 0, upperBound = 0;
cout << "\nEnter the borders of the range: ";
cin >> lowerBound >> upperBound;
cout << endl;
int result = 0;
bool (*predicate)(int);
// predicate = &Diapasoner::CheckCondition;
// [bcc32c Error] File1.cpp(83): assigning to 'bool (*)(int)' from incompatible type 'bool (Diapasoner::*)(int)'
bool (Diapasoner::*conditfun)(int);
conditfun = &Diapasoner::CheckCondition;
Diapasoner *intervalObj = new Diapasoner(lowerBound, upperBound);
result = 0;
for (int val: v)
{
(intervalObj->*conditfun)(val) ? result++ : result=result;
}
cout << "Number of elements in diapason " << lowerBound << ".." << upperBound << " and outside" << endl;
cout << "Calculated with base class member-function pointer: " << result << endl;
cout << endl;
delete intervalObj;
//------------------------------------------------------------
intervalObj = new ClosedInterval(lowerBound, upperBound);
conditfun = &Diapasoner::CheckCondition;
result = 0;
for (int val: v)
{
(intervalObj->*conditfun)(val) ? result++ : result=result;
}
cout << "Number of elements in diapason [" << lowerBound << ", " << upperBound << "] and outside " << endl;
cout << "Calculated with derived class overrided member-function pointer: " << result << endl;
cout << endl;
delete intervalObj;
//------------------------------------------------------------
// try...
intervalObj = new ClosedInterval(lowerBound, upperBound);
// conditfun = &ClosedInterval::CheckCondition;
// [bcc32c Error] File1.cpp(115): assigning to 'bool (Diapasoner::*)(int)' from incompatible type 'bool (ClosedInterval::*)(int)': different classes ('Diapasoner' vs 'ClosedInterval')
// conditfun = &ClosedInterval::inClosed;
ClosedInterval* interval_Obj = new ClosedInterval(lowerBound, upperBound);
bool (ClosedInterval::*conditfun_)(int);
conditfun_ = &ClosedInterval::CheckCondition;
result = 0;
for (int val: v)
{
(interval_Obj->*conditfun_)(val) ? result++ : result=result;
}
cout << "Number of elements in diapason [" << lowerBound << ", " << upperBound << "]" << endl;
cout << "Calculated with ClosedInterval class member-function pointer: " << result << endl;
cout << endl;
delete interval_Obj;
//---------------------------------------------------
//try...
OpenInterval* interval0Obj = new OpenInterval(lowerBound, upperBound);
// conditfun_ = &OpenInterval::CheckCondition;
//[bcc32c Error] File1.cpp(137): assigning to 'bool (ClosedInterval::*)(int)' from incompatible type 'bool (OpenInterval::*)(int)': different classes ('ClosedInterval' vs 'OpenInterval')
//----------------------------------------------------
bool (__closure *predicateX)(int value);
intervalObj = new Diapasoner(lowerBound, upperBound);
predicateX = intervalObj->CheckCondition;
result = 0;
for (int val: v)
{
predicateX(val) ? result++ : result=result;
}
cout << "Number of elements in diapason " << lowerBound << ".." << upperBound << " and outside" << endl;
cout << "Calculated for base Diapasoner object with predicateX pointer (trivial): " << result << endl;
cout << endl;
delete intervalObj;
intervalObj = new ClosedInterval(lowerBound, upperBound);
predicateX = intervalObj->CheckCondition;
result = 0;
for (int val: v)
{
predicateX(val) ? result++ : result=result;
}
cout << "Number of elements in diapason [" << lowerBound << ", " << upperBound << "]" << endl;
cout << "Calculated for ClosedInterval object with predicateX pointer: " << result << endl;
cout << endl;
delete intervalObj;
intervalObj = new OpenInterval(lowerBound, upperBound);
predicateX = intervalObj->CheckCondition;
result = 0;
for (int val: v)
{
predicateX(val) ? result++ : result=result;
}
cout << "Number of elements in diapason (" << lowerBound << ", " << upperBound << ")" << endl;
cout << "Calculated for OpenInterval object with predicateX pointer: " << result << endl;
cout << endl;
interval0Obj = (OpenInterval*)intervalObj;
predicateX = interval0Obj->inHalfOpenLeft;
result = count_if(v.begin(), v.end(), predicateX);
cout << "Number of elements in half-open diapason (" << lowerBound << ", " << upperBound << "]" << endl;
cout << "Calculated in count_if for OpenInterval class with predicateX pointer: " << result << endl;
predicateX = interval0Obj->inHalfOpenRight;
result = count_if(v.begin(), v.end(), predicateX);
cout << "Number of elements in half-open diapason [" << lowerBound << ", " << upperBound << ")" << endl;
cout << "Calculated in count_if for OpenInterval class with predicateX pointer: " << result << endl;
cout << endl;
delete intervalObj;
std::cin.ignore(2);
return EXIT_SUCCESS;
}
![]()
Perhaps, the presentation of this material seemed somewhat vague to you, and examples were far-fetched. Indeed, you can use VCL, and without even thinking about the details of the implementation of TNotifyEvent , and for storing of the function-members, we now have std::function. Nevertheless, if you want to feel the essence of the problem of working with pointers to class methods, I recommend you read Mohammad Nasim’s article where this material is presented very talently and succinctly: “C++ Delegates and Borland's C++ Builder Event Handling” -- Part I and Part II.
Fun fact: Borland was forced to use a functional style approach in C++Builder VCL by introducing the use of a __closure keyword to create the infrastructure of a classical object-oriented language (such as C++) many years before the functional elements were included in standard! Something similar (in conceptual sense) is happening now with the possibility of using functors and lambda expressions together with the algorithms of STL. This suggests that although you can never write programs purely in the functional paradigm using C++ (because it’s an imperative language and not intended for such a thing) functional elements can enhance the capabilities of object-oriented languages and embellish them.
Today it gives us a powerful tool for manipulating member-functions to care only about the correspondence of their signatures, and not their owner classes. Nevertheless, in modern C ++, there are mechanisms for manipulating the signature of the call, just as in the functional paradigm the function itself is an object of influence, and soon we will see it.