test_request: un parser di comandi

Prima di descrivere le funzionalità della classe CLB_messenger, esponiamo le caratteristiche della classe test_request, usata per il parsing ed il trasferimento di comandi fra gli oggetti.

Abbiamo già indirettamente incontrato questa classe discutendo di OPERATION_CALL, avendola utilizzata nella dichiarazione della macro   __register_global_object.  Ne daremo ora una descrizione dettagliata.

Possiamo immaginare test_request come il mediatore che rende disponibili i comandi e parametri, individuando l’oggetto ricevente, chiamando la funzione di ricezione dello stesso e rendendo disponibile i dati in forma di comando analizzato. Ha come oggetti target quelli di tipo OPERATION_CALL  e CLB_messenger

class AFX_EXT_CLASS test_request
{
public:
	test_request(ARGPTR *p);

	bool is(__arg_value pars, ...);

	void *pop_param();

	static __arg_value exec_global_fn(__arg_value pars, ...);
	static __arg_value exec_obj_fn(__arg_value pars, ...);

	ARGPTR * p = 0;
	int _np = 0;

	__arg_value ret_va = 0l;
	void *sender = 0;
};

__arg_value  exec_global_fn(__arg_value pars, …)

Questa funzione è di tipo VARIADIC, cioè riceve un numero variabile di parametri, tutti di tipo __arg_value (cioè void *). non viene mai usata direttamente, ma internamente alla macro __exec_global_fn(..)   la quale permette la trasmissione di un comando ad un oggetto riconoscibile attraverso il suo nome (id dell’istanza).

In pratica non useremo mai questa funzione direttamente, ma attraverso le macro che preparano i parametri necessari.

Le classi in grado di ricevere un messaggio attraverso la macro __exec_global_fn sono derivate da OPERATION_CALL e le loro istanze individuabili sono quelle registrate globalmente attraverso la macro __register_global_object . Analizziamo ora come lanciare un comando ad una classe OPERATION_CALL registrata globalmente; per farlo utilizziamo come esempio  una partde di codice del framework di Colibrì:

		$_function(void, _inform_navigator)
		{
			if (GETBIT(NAVIGATOR))
				__exec_global_fn(_$dock_navigator, _$attach, _$view, $obj);
		}

la funzione  _inform_navigator,  scritta nella sintassi CLB_language, il linguaggio interno a Colibri, è parte del codice della classe WIDGET_DOCUMENT, che visualizza un oggetto di classe DOCUMENT; analizziamo la funzione: essa  verifica l’esistenza di una condizione (GETBIT(NAVIGATOR)) quindi esegue una macro, evidenziata in rosso nell’esempio:   __exec_global_fn(_$dock_navigator, _$attach, _$view, $obj);

__exec_global_fn   è la macro che manda il comando ad un oggetto OPERATION_CALL definito attraverso il suo nome globale,  _$dock_navigator ;

il comando mandato è _$attach, _$view   interpretabile come connettiti  questo controllo di visualizzazione:   $obj, (è il puntatore al controllo di visualizzazione da connettere)

Vedremo fra poco come questo comando sarà ricevuto dall’istanza della classe DOCK_document_navigator, destinataria dello stesso.

La macro __exec_global_fn definita nel file CLB_language.h ed è visibile di seguito:

#define __exec_global_fn(...)  test_request::exec_global_fn( __VA_ARGS__,_$end) 

Come si può notare, è una macro che contiene un numero di argomenti variabili (variadic)  e chiama la funzione statica test_request::exec_global_fn passando gli stessi argomenti più un id di fine lista (_$end)

La funzione  test_request::exec_global_fn  riceve così  la lista di argomenti, aspettandosi che il primo sia il nome dell’istanza globale di tipo OPERATION_CALL (nel nostro caso sarà l’istanza DOCK_document_navigator,  che è identificata dalla stringa  _$dock_navigator) ne analizza il significato e manda il comando alla  istanza riconosciuta. L’istanza era stata registrata all’atto della sua costruzione attraverso la macro  __register_global_object  inserita nel costruttore, come si vede nel codice sotto riportato:

DOCK_document_navigator::DOCK_document_navigator()
{
	$_init

	__register_global_object(_$dock_navigator)

	__on_signal_create(__on_create_window)
	__on_signal_end_create(__on_end__create_window)
	__on_signal_paint(__on_paint_wnd)
	__on_signal_resize(__on_resize_window)

}

Nel caso specifico, esiste un’unica istanza della classe DOCK_document_navigator creata nel software.

la macro __register_global_object 

  • Questa macro, che abbiamo dichiarato nel costruttore della classe, svolge più funzioni:
  • registra in una lista globale l’oggetto creato assegnandogli un nome (_$dock_navigator)
  • dichiara lo slot di ricezione dei messaggi lanciati attraverso la macro __exec_global_fn   

Lo slot di ricezione è stato definito poco prima del costruttore ed è visibile di seguito:

__slot_function_parser
{

	___exit_if_not_dock



	__is_request(_$attach,_$view)
	{
		__pop_param(w, WIDGET_DOCUMENT)
		$obj->send_to_obj(_$str->wdoc,_$attach, _$view,w,_$end);
		return true;
	}

	__is_request(_$hide)
	{
		$obj->Show(false);
		return true;
	}

}
__end_slot

__declare_slots
__add_function_parser
__end__declare_slots

DOCK_document_navigator::DOCK_document_navigator()
{
	$_init

	__register_global_object(_$dock_navigator)

	__on_signal_create(__on_create_window)
	__on_signal_end_create(__on_end__create_window)
	__on_signal_paint(__on_paint_wnd)
	__on_signal_resize(__on_resize_window)

}

__slot_function_parser .. __end_slotil blocco definisce il corpo della funzione slot di ricezione

__declare_slots  __add_function_parser  __end__declare_slots  :  questa sequenza dichiara la struttura per l’accesso allo slot, che __register_global_object userà per la registrazione

lo slot contiene un insieme di macro che individuano il tipo di comando:

_is_request(_$attach,_$view)   è il test che viene eseguito per verificare che il comando sia _$attach,_$view;

in questo caso con la macro __pop_param(w, WIDGET_DOCUMENT) viene estratto il puntatore all’oggetto WIDGET_DOCUMENT atteso con il comando (esattamente come descritto in precedenza);

il comando successivo eseguito è $obj->send_to_obj(_$str->wdoc,_$attach, _$view,w,_$end); 

La cosa si fa interessante:

$obj, che individua l’istanza  che sta ricevendo il comando è DOCK_document_navigator, a sua volta derivata da EXTENDED_WIDGET, come tutti i controlli di Colibrì, quindi possiede l’interfaccia CLB_messenger.

La funzione send_to_obj  chiamata manda un messaggio per informare il controllo figlio (_$str->wdoc) con il comando _$attach, _$view, w dove w è il parametro WIDGET_DOCUMENT ricevuto.. _$str->wdoc di cui non vogliamo esporre nulla se non che, essendo anch’esso derivato da EXTENDED_WIDGET, possedendo l’interfaccia CLB_messenger, sarà in grado di ricevere il messaggio nella sua funzione virtuale slot_messenger(..)  di cui scriveremo fra poco..

Per concludere questa non brevissima analisi della classe test_request: abbiamo scoperto due diverse funzioni di messaggistica disponibili:

exec_global_fn: che comunica con oggetti registrati globalmente, derivati da OPERATION_CALL

send_to_obj: che manda un messaggio ad un oggetto con interfaccia CLB_messenger, di cui è noto il puntatore.

le altre funzioni :

is(__arg_value pars, …)

è una funzione di supporto per l’individuazione della sequenza di comandi ricevuti; la funzione è usata all’interno della macro __is_request:

#define __is_request(...) if (_requ_.is( __VA_ARGS__,0))

nel nostro esempio __is_request(_$attach,_$view) rimanda true se la sequenza è quella sotto indicata. in questo caso, sempre analizzando l’esempio, viene eseguita la macro __pop_param:

#define __pop_param(ptr,type) type *ptr=(type *)_A(_requ_._np++);

la macro genera una variabile inizializzandola con il contenuto attuale nello stack dei comandi della classe test_request di supporto.

Nota: Osservare che la classe test_request  pur essendo il cuore di tutte le funzioni di comunicazione, non appare mai direttamente nel codice scritto dallo sviluppatore, ma è sempre presente ed utilizzata all’interno delle macro di gestione dei messaggi!

Non abbiamo ancora esaurito la descrizione delle caratteristiche di test_request, ma ne sappiamo a sufficienza per passare finalmente alla classe CLB_messenger

Pages: 1 2 3
Skip to toolbar