lanciare messaggi
__send_cmd( cmd,…)
E’ usato per richiamare una funzione CLB_messenger::send(….) con una facilitazione: la macro send_cmd inserisce automaticamente _$end a fine lista, come atteso dalla CLB_messenger::send(..). Per usare la send_cmd:
il messaggio è trasmesso a tutti gli oggetti connessi (vedi CLB_regs(..)) ed esistono due diverse modalità d’uso, a seconda che si mandi il messaggio da un oggetto dato il suo putatore obj, o da una funzione della classe
- per un oggetto obj: obj->send_cmd(pars, ..) dove obj è un puntatore ad una istanza CLB_messenger (ad esempio un qualunque WIDGET)
- per una chiamata da dentro una funzione della classe: send_cmd(pars, ..)
__send_cmd_to_obj(w_plot, _$get, _$selected, _$document, doc); ... obj->__send_cmd_to_obj(w_plot, _$get, _$selected, _$document, doc);
__send_cmd_to_obj(receiver,…)
E’ usato per richiamare una funzione CLB_messenger::send_to_obj(….)
la macro send_cmd(..) inserisce automaticamente _$end a fine lista, come richiesto dalla send(..)
anche in questo caso come per per usare la send_cmd:
per un oggetto obj: obj->__send_cmd_to_obj(pars, ..)
per una chiamata da dentro una funzione della classe: __send_cmd_to_obj(pars, ..)
Il primo parametro del comando è l’oggetto CLB_messenger che deve ricevere il comando:
/** WIDGET *w_plot // assegnato in precedenza.. */ CURVE curva; __send_cmd_to_obj(w_plot, _$get, _$selected, _$curve, &curva); ...
Le due macro permettono la spedizione di un comando da parte di una classe con interfaccia CLM_messenger verso altri oggetti con la stessa interfaccia. I comandi usano, per trasferire il comando un dei due metodi di CLB_messenger:
bool CLB_messenger::send(__arg_value cmd, ...); bool CLB_messenger::send_to_obj(CLB_messenger *receiver, __arg_value pars, ...);
Comunque il messaggio, attraverso i due metodi, se il destinatario è correttamente definito, arrivera alla funzione slot_messenger della classe derivata: la funzione, essendo virtuale, sarà stata riscritta per ciascuna classe, e sarà in grado, utilizzando altre macro di CLB_lang, di estrarre il messaggio ed effettuare le scelte conseguenti:
bool Widget_plotter::slot_messenger(_received_data) { __getstr __is_request(_$get,_$selected, _$curve) { __pop_p(cv, CURVE); _$str->edit->get_selected_curve(cv); } return true; }
L’esempio di codice è già stato riportato in questo articolo. Lo riprendiamo per spiegare nei particolari il metodo di costruzione delle funzioni slot_messenger.
La funzione slot_messenger contiene nella dichiarazione dei parametri la macro _received_data; dovrà sempre essere in questa forma, poichè la macro con è altro che un’ istanza test_request (_requ_) usata all’interno delle macro di analisi del messaggio utilizzabili all’interno della funzione.
#define _received_data test_request &_requ_
descriviamo ora le macro di analisi del messaggio usate, sapendo che contengono inplicitamente la istanza _requ_ parametro della funzione slot_messenger.
_is_request(…)
#define __is_request(...) if (_requ_.is( __VA_ARGS__,0))
la macro , che ha come argomenti la frase del messaggio ( __is_request(_$get,_$selected, _$curve) ) utilizza la funzione test_request::is(..) per testare il significato del messaggio ricevuto; se il messaggio è quello testato, allora il blocco di codice che segue viene eseguito;
Nel blocco troviamo il comando CLB_lang __pop_p(cv, CURVE); che estrae dallo stack del messaggio il successivo parametro, che per la sintassi attesa deve essere un CURVE * (vedi poco sopra questo esempio..)
__pop_p(ptr,type)
#define __pop_p(ptr,type) type *ptr=(type *)_requ_.pop_param();
la macro costruisce un pointer ptr di tipo type (nel caso in esempio CURVE *cv ) richiamando al suo interno la test_request::pop_param(); (notare che l’istanza di questa classe usata è proprio la _requ_ passata alla funzione con la macro _received_data..)
Ovviamente la sequenza di estrazione del parametro obbedisce all’ordine di trasmissione definito nel comando lanciato dal sender.
la macro _is_request(…) verrà usata tante volte quante sono le frasi di ricezione accettate dalla classe; le frasi non riconosciute corrisponderanno a messaggi non intercettati, e non avranno alcun effetto sulla classe; questo permette ad esempio che più istanze CLB_messenger, connesse ad una classe mediante la funzione CLB_messenger::regs(..) intercetteranno solo i messaggi (le frasi) analizzati in slot_messenger, quindi classi diverse registrate possono intercettare messaggi diversi; notare che la stessa frase ovviamente potrà essere intercettata da classi diverse, purchè registrate nel sender.
per concludere questa esposizione, di seguito è riportato un altro esempio di slot_messenger, relativo ad un’altra classe, la WIDGET_DOCUMENT, dove si potrà osservare l’uso più volte delle due macro __pop_p e __is_request per intercettare diversi tipi di messaggio e parametri mandati dall’oggetto chiamante (definito qui sender)
bool WIDGET_DOCUMENT::slot_messenger(_received_data) { __getstr __is_request(_$attach, _$dock_navigator) { SETBIT(NAVIGATOR,true) } __is_request(_$display, _$area) { __pop_p(r, RectF); $obj->ZoomArea(r->X, r->Y, r->Width, r->Height); } __is_request(_$zoom, _$area) { __pop_p(sender, void) if (sender == this) return false; // è il chiamante .. __pop_p(org, PointF); __pop_p(scale, float); if (_MAPPER.Zoom(*scale) && _MAPPER.SetOrig(*org)) { PointF *orgig = _MAPPER.GetOrig(); INVALIDATE_SV_AREA(CANVAS, 1) PAINT(CANVAS) PAINT(_$str->scr_vert) PAINT(_$str->scr_horz) PAINT(_$str->h_ruler); PAINT(_$str->v_ruler); } } return false; }
Nota: il programmatore che progetterà una classe derivata da CLB_messenger, potrà comunque definire messaggi basati su keyword personali: ad esempio potrà lanciare comandi che contengono la keyword _$myDefin (=L”my definition word”) . In questo caso si consiglia di utilizzare almento una lettera maiuscola ed evitare le definizioni tutte minuscolo, per evitare conflitti con eventuali keyword di CLB_lang (che sono tutte, appunto minuscolo).