__closure

De RAD Studio
Aller à : navigation, rechercher

Remonter à Extensions de mots clés C++


Catégorie

Extensions des mots clés

Syntaxe

<type> ( __closure * <id> ) (<param list>);

Description

Deux types de pointeurs de fonctions peuvent être utilisés en C++. Le premier est un pointeur simple sur une fonction globale (pas un membre d'une classe). Avec les fonctions de membre de classe, un pointeur simple sur une méthode n'est pas suffisant pour effectuer un appel. Deux pointeurs sont nécessaires : un pour l'objet d'origine et le second pour l'adresse de la méthode.

Tous les événements des composants de la VCL sont déclarés en tant que clôtures (closures), afin de permettre aux méthodes d'une classe d'être appelées quand l'événement est déclenché. Le code suivant est un extrait de l'en-tête Classes.hpp, présentant la déclaration du TNotifyEvent, utilisé dans de nombreux événements de la VCL (par exemple, OnClick).

typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);

L'exemple suivant montre comment est déclaré un type pointeur de fonction globale normal :

typedef void (*PointerToAFunction)();

Il est utilisé comme suit :

/* Assigning a function to the function pointer */
PointerToAFunction a = SomeGlobalFunc;

Pour transformer ce type pointeur de fonction en pointeur de méthode, ajoutez simplement __closure comme suit :

typedef void (__closure *PointerToAFunction)();

Le pointeur de méthode est utilisé comme suit :

/* Assigning a method to the method pointer */
PointerToAFunction a = &(someObject.SomeMemberFunc);
Attention: Lorsque vous déclarez des clôtures avec des constantes et des pointeurs dans la section publiée d'une classe, utilisez un typedef pour le pointeur, comme ci-dessous :
  typedef int*
  typedef void _fastcall (_closure MyClosure)(const IntPtr piVal);

Exemples

Le mot clé __closure est utilisé pour déclarer un type spécial de pointeur sur une fonction membre. En C++ standard, le seul moyen d'obtenir un pointeur sur une fonction membre est d'utiliser le nom entièrement qualifié du membre, comme indiqué dans l'exemple suivant :

class base {
public:
    void func(int x) {
    };
};

typedef void(base::*pBaseMember)(int);

int main(int argc, char * argv[])
{
    base baseObject;
    pBaseMember m = &base::func; // Get pointer to member ‘func’.

    // Call ‘func’ through the pointer to member.
    (baseObject.*m)(17);
    return 0;
}

Cependant, vous ne pouvez pas assigner un pointeur sur un membre d'une classe dérivée à un pointeur sur un membre d'une classe de base. Cette règle (appelée contravariance) est illustrée dans l'exemple suivant :

class base {
public:
	void func(int x) {
	};
};

typedef void(base::*pBaseMember)(int);

class derived : public base {
public:
	void new_func(int i) {
	};
};

int main(int argc, char * argv[]) {
	derived derivedObject;
	pBaseMember m = &derived::new_func; // ILLEGAL
	return 0;
}

L'extension de mot clé __closure permet d'esquiver cette limitation, et davantage. En utilisant un closure, vous pouvez obtenir un pointeur sur une fonction membre pour un objet (par exemple, une instance particulière d'une classe). L'objet peut être quelconque, sans tenir compte de sa hiérarchie d'héritage. Le pointeur de l'objet est automatiquement utilisé lors de l'appel de la fonction membre via le closure. L'exemple suivant montre comment déclarer et utiliser un closure. Les classes de base et dérivée, fournies précédemment, sont supposées être définies.

class base {
public:
	void func(int x) {
	};
};

typedef void(base::*pBaseMember)(int);

class derived : public base {
public:
	void new_func(int i) {
	};
};

int main(int argc, char * argv[]) {
	derived derivedObject;
	void(__closure * derivedClosure)(int);

	// Get a pointer to the ‘new_func’ member.
	// Note the closure is associated with the
	// particular object, ‘derivedObject’.
	derivedClosure = derivedObject.new_func;

	derivedClosure(3); // Call ‘new_func’ through the closure.
	return 0;
}

Pour un exemple de code, voir TRegExReplace (C++).

Les closures fonctionnent également avec les pointeurs sur des objets, comme illustré dans cet exemple :

class base {
public:
	void func(int x) {
	};
};

typedef void(base::*pBaseMember)(int);

class derived : public base {
public:
	void new_func(int i) {
	};
};

// Closure with pointer to object
void func1(base * pObj) {
	// A closure taking an int argument and returning void.
	void(__closure * myClosure)(int);

	// Initialize the closure .
	myClosure = pObj->func;

	// Use the closure to call the member function.
	myClosure(1);
	return;
}

int main(int argc, char * argv[]) {
	derived derivedObject;
	void(__closure * derivedClosure)(int);

	derivedClosure = derivedObject.new_func; // Same as before...
	derivedClosure(3);

	// We can use pointers to initialize a closure, too.
	// We can also get a pointer to the ‘func’ member function
	// in the base class.
	func1(&derivedObject);
	return 0;
}

Attention, on transmet ici un pointeur à une instance de la classe dérivée, puis nous l'utilisons pour obtenir un pointeur sur une fonction membre de la classe de base, ce qui est interdit en C++ standard.

Les closures sont un élément clé de l'environnement C++Builder RAD Studio. Ils offrent la capacité d'assigner un gestionnaire d'événement dans l'inspecteur d'objets. Par exemple, un objet TButton a un événement appelé OnClick. Dans la classe TButton, l'événement OnClick est une propriété qui utilise l'extension de mot clé __closure dans sa déclaration. Le mot clé __closure permet d'assigner une fonction membre d'une autre classe (typiquement une fonction membre d'un objet TForm) à la propriété. Quand vous placez un objet TButton sur une fiche, puis créez un gestionnaire pour l'événement OnClick du bouton, C++Builder crée une fonction membre dans le parent TForm du bouton, et assigne cette fonction membre à l'événement OnClick de TButton. De cette façon, le gestionnaire d'événement est associé à cette instance particulière de TButton, et pas à une autre.

Voir aussi

Exemples de code