[DOC] Function Object's

[DOC] Function Object's

Postby CicTec » Tue Jul 16, 2013 4:04 pm

NOTA: Prima di leggere questa documentazione, si raccomanda di leggere dettagliatamente le seguenti documentazioni:
  • Scope's and Type Access
  • Process Method's


I Function Objects (FOBJECT) sono un nuovo tipo di dato speciale, di forma ibrida e simile ai puntatori a funzione di altri linguaggi (C, C++, etc...), che permettono di chiamare indirettamente blocchi FUNCTION, METHOD o funzioni di sistema.
Il principale utilizzo dei function objects è per la creazione di sistemi callbacks ed eventi basati su callbacks.

1 - DEFINIZIONE DI DATI FOBJECT
I function objects vengono definiti con la parola chiave FOBJECT e possono essere variabili, array o puntatori, della stessa forma di altri tipi di dati base.
La forma di dichiarazione è la seguente:
fobject{.proctype} {*/pointer} name{[N]}(signature);
  • .proctype - Rappresenta il nome di un tipo di PROCESS valido, per la definizione di fobjects per metodi di process del tipo indicato (es: fobject.myproc fobj).
  • */pointer - Rappresenta la definizione di puntatore. (es: *fobj)
  • name - Rappresenta il nome del nuovo dato (es: fobj).
  • [N] - Rappresenta la definizione di array (es: fobj[10]).
  • (signature) - Rappresenta la lista di parametri (es: fobj(int)).

Esempio:
Source Code (Gemix) [ Download ] [ Hide ]
  • global
  •   fobject fobj_var(int);
  •   fobject fobj_array[9](string);
  •   fobject *fobj_ptr(float, float);
  •   fobject.proc fobj_mvar(int);
  •   fobject.proc fobj_marray[9](string);
  •   fobject.proc *fobj_mptr(float, float);
  • ...
  •  
  • process proc(fobject p_fobj(string))
  • methods
  •   ...
  • begin
  •   ...
  • end
  •  

L'esempio precedente crea i seguenti dati:
  • Una variabile di nome "fobj_var" alla quale può essere assegnata blocchi FUNCTION, METHOD, funzioni di sistema o altri FOBJECT che abbiano una signature di tipo "(int)".
  • Un array di nome "fobj_array" al quale possono essere assegnati blocchi FUNCTION, METHOD, funzioni di sistema o altri FOBJECT che abbiano una signature di tipo "(string)".
  • Un puntatore di nome "fobj_ptr" al quale può essere assegnato variabili o array FOBJECT che abbiano una signature di tipo "(float, float)".
  • Una variabile di nome "fobj_mvar" alla quale può essere assegnata blocchi METHOD o altri FOBJECT che abbiano una signature di tipo "(int)" (e appartenenti allo stesso tipo PROCESS della definizione).
  • Un array di nome "fobj_marray" al quale possono essere assegnati blocchi METHOD o altri FOBJECT che abbiano una signature di tipo "(string)" (e appartenenti allo stesso tipo PROCESS della definizione).
  • Un puntatore di nome "fobj_mptr" al cuale può essere assegnato variabili variabili o array FOBJECT che abbiano una signature di tipo "(float, float)" (appartenenti allo stesso tipo PROCESS della definizione).
  • Un parametro variabile del PROCESS "proc" di nome "p_fobj" al quale può essere assegnato variabili o array FOBJECT che abbiano una signature di tipo "(string)".
NOTA:
  • Se si tenta di utilizzare come "proctype" un tipo di PROCESS non valido o inesistente, durante la definizione del FOBJECT, si avrà un errore di compilazione.
  • La signature di un FOBJECT deve essere una lista di tipo separati da virgole racchiusa da una coppia di ( ), se si pone un nome dopo il tipo di parametro si avrà un errore di compilazione.
  • I parametri di una signature possono essere di qualunque tipo base, process o user-def.


2 - INIZIALIZZAZIONE DI DATI FOBJECT
E' possibile inizializzare i function objects durante la definizione, nella stessa forma di altri tipi di dati.
La forma di inizializzazione di un FOBJECT è la seguente:
fobject{.proctype} {*/pointer} name{[N]}(signature) = values_list[/list]
  • values_list - Rappresenta una lista di uno o più valori (nel caso di arrays), con il quale il dato verrà inizializzato.

Esempio:
Source Code (Gemix) [ Download ] [ Hide ]
  • global
  •   fobject fobj_var(int) = func;
  •   fobject fobj_var2(int, int) = proc.set;
  •   fobject fobj_array[3](string) = func(string), func2, 0, NULL;
  •   fobject *fobj_ptr(float, float) = 0;
  •   fobject.proc fobj_mvar(int, int) = proc.set;
  •   fobject.proc fobj_marray[3](string) = info, proc.infoex, 0, NULL;
  • ...
  •  
  • process proc()
  • methods
  •   method public int set(int a, int b)
  •   begin
  •     ...
  •   end
  •  
  •   method public string info(string format)
  •   begin
  •     ...
  •   end
  •  
  •   method public string infoex(string format)
  •   begin
  •     ...
  •   end
  •  
  • begin
  •   ...
  • end
  •  
  • function func(int value)
  • begin
  •   ...
  • end
  •  
  • function func(string str)
  • begin
  •   ...
  • end
  •  
  • function func2(string str)
  • begin
  •   ...
  • end
  •  
  • function func2(float a, float b)
  • begin
  •   ...
  • end
  •  

La dichiarazione precedente inizializza i seguenti dati globali:
  • La variabile "fobj_var" con l'indirizzo della FUNCTION di nome "func" la cui signature corrisponde a "(int)".
  • La variabile "fobj_var2" con l'indirizzo del METHOD di nome "set" la cui signature corrisponde a "(int, int)".
  • L'array "fobj_array" con gli indirizzi delle FUNCTION di nome "func" e "func2" le cui signature corrispondono a "(string)" e con i valori 0 e NULL.
  • Il puntatore "fobj_ptr" con il valore 0.
  • La variabile "fobj_mvar" con l'indirizzo del METHOD di nome "set" (appartenente al PROCESS di tipo "proc"), la cui signature corrisponde a "(int, int)".
  • L'array "fobj_marray" con gli indirizzi dei METHOD di nome "info" e "infoex" (appartenenti al PROCESS di tipo "proc"), le cui signature corrisponde a "(string)" e con i valori 0 e NULL.
NOTA:
  • Se si tenta di inizializzare un dato con blocco PROCESS, un valore costante diverso da 0 (o NULL), o altro tipo di dato, si avrà un errore di compilazione.
  • Se si tenta di inizializzare un dato con blocco FUNCTION, METHOD o funzione di sistema con signature distinta, si avrà un errore di compilazione.
  • Per inizializzare un dato è possibile indicare solo il nome del blocco o funzione di sistema, viceversa è possibile includere anche la signature.
  • Se si vuole assegnare un METHOD a un dato, questo deve essere indicato utilizzando il nome del tipo di PROCESS, seguito dall'operatore di accesso (.) e dal nome del METHOD (o direttamente dal nome del METHOD nel caso la definizione del FOBJECT sia dello stesso tipo di PROCESS), inoltre il METHOD deve essere stato dichiarato precedentemente nella lista dei metodi del PROCESS (per una spiegazione più dettagliata sui metodi, vedere la DOC "Process Method's"), altrimenti si avrà un errore di compilazione.
  • Se si tenta di assegnare un METHOD di tipo di accesso PRIVATE a un FOBJECT di tipo GLOBAL o PUBLIC, si avrà un errore di compilazione.
  • Se si tenta di assegnare un METHOD appartenente ad un tipo PROCESS diverso da quello indicato nella definizione del FOBJECT, si avrà un errore di compilazione.
  • Se si tenta di assegnare un blocco FUNCTION o funzione di sistema ad un FOBJECT definito con un tipo di PROCESS specifico, si avrà un errore di compilazione.
  • E' possibile assegnare ad un array, solo blocchi METHOD appartenenti ad un tipo di PROCESS indicato nella definizione, o solo blocchi FUNCTION e funzioni di sistema senza tipo di PROCESS specificato, altrimenti si avrà un errore di compilazione.
  • Se la signature del dato corrisponde a "()", per inizializzarlo con un blocco o funzione di sistema è necessario usare la speciale signature "(void)" dopo il nome dello stesso, altrimenti si avrà un errore di compilazione.
  • Se non si inizializza esplicitamente un dato questo avrà per default il valore 0.
  • Se si tenta di porre più inizializzatori del numero totale di elementi si avrà un errore di compilazione.
  • Un puntatore può essere inizializzato unicamente con il valore 0 (o NULL), altrimenti si avrà un errore di compilazione.


3 - OPERAZIONI CON DATI FOBJECT
E' possibile eseguire solo alcune operazioni con gli FOBJECTS, queste sono:
  • ASSEGNAZIONE: A un dato FOBJECT di un indirizzo di blocco FUNCTION, METHOD, funzione di sistema o altro dato FOBJECT dello stesso tipo (signature), per mezzo dell'operatore "=".
  • CONFRONTO: Di dati FOBJECTS per mezzo degli operatori "==" e "!=".
  • CHIAMATA: Di un blocco FUNCTION, METHOD o funzione de sistema mediante un FOBJECT.

3.1 - OPERAZIONI CON DATI FOBJECT - ASSEGNAZIONE
E' possibile assegnare a dati di tipo FOBJECT, indirizzi di blocchi FUNCTION, METHOD o funzioni di sistema. E' inoltre possibile assegnare altri dati di tipo FOBJECT.

Esempio:
Source Code (Gemix) [ Download ] [ Hide ]
  • global
  •   fobject fobj_var(int);
  •   fobject fobj_var2(int, int);
  •   fobject fobj_var3(string);
  •   fobject fobj_var4(float, float);
  •   fobject fobj_array[3](string);
  •   fobject *fobj_ptr(float, float);
  •   fobject.proc fobj_mvar(int, int);
  •   fobject.proc fobj_mvar2(int);
  •  
  • begin
  •   fobj_var = func; // OK se si incontra una funzione con signature "(int)", ERROR altrimenti
  •   fobj_var2 = func(int); // ERROR, le signatures non coincidono
  •   fobj_var3 = func(string); // OK, le signatures coincidono
  •   fobj_array[2] = fobj_var3; // OK, le signatures coincidono
  •   fobj_var = fobj_var2; // ERROR, le signatures non coincidono
  •   fobj_ptr = &func2; // ERROR, non è un dato di tipo FOBJECT
  •   fobj_ptr = &fobj_var4; // OK, è un dato di tipo FOBJECT e le signatures coincidono
  •   fobj_mvar = proc.set; // OK, le signatures coincidono ed il method appartiene allo stesso tipo PROCESS della definizione del FOBJECT
  •   fobj_mvar2 = func; // ERROR, le signatures coincidono tuttavia "func" non è un metodo appartenente al tipo PROCESS indicato nella definizione del FOBJECT
  • end
  •  
  • process proc()
  • methods
  •   method public int set(int a, int b)
  •   begin
  •     ...
  •   end
  •  
  • begin
  •   ...
  • end
  •  
  • function func(int value)
  • begin
  •   ...
  • end
  •  
  • function func(string str)
  • begin
  •   ...
  • end
  •  
  • function func2(string str)
  • begin
  •   ...
  • end
  •  
  • function func2(float a, float b)
  • begin
  •   ...
  • end
  •  

L'esempio precedente esegue le seguenti operazioni si assegnamento ai dati globali:
  • La variabile "fobj_var" è inizializzata con l'indirizzo del blocco FUNCTION, viene cercato se nella lista degli overloads esiste una funzione che corrisponde a "(int)", se coincide, l'assegnazione è corretta, questo provoca un errore di compilazione, poichè le signatures non coincidono.
  • La variabile "fobj_var2" è inizializzata con l'indirizzo del blocco FUNCTION la cui signature corrisponde a "(int)", questo provoca un errore di compilazione, poichè le signatures non coincidono.
  • La variabile "fobj_var3" è inizializzata con l'indirizzo del blocco FUNCTION la cui signature corrisponde a "(string)", questo è corretto, poichè le signatures coincidono.
  • L'elemento 2 dell'array "fobj_array" è inizializzato con il valore della variabile "fobj_var3", questo è corretto, poichè le signatures coincidono a "(string)".
  • La variabile "fobj_var" è inizializzata con il valore della variabile "fobj_var2", questo provoca un errore di compilazione, poichè le signatures non coincidono.
  • Il puntatore "fobj_ptr" è inizializzato con l'indirizzo del blocco FUNCTION "func2", questo provoca un errore di compilazione, poichè il puntatore non può ricevere l'indirizzo di un blocco.
  • Il puntatore "fobj_ptr" è inizializzato con l'indirizzo di un'altra variabile FOBJECT di nome "fobj_var4", questo è corretto, poichè le signatures coincidono ed il puntatore può ricevere l'indirizzo di un'altro FOBJECT.
  • La variabile "fobj_mvar" è inizializzata con l'indirizzo del blocco METHOD, viene cercato se nella lista degli overloads esiste un metodo che corrisponde a "(int, int)", se coincide, l'assegnazione è corretta, altrimenti provoca un errore di compilazione, poichè le signatures non coincidono.
  • La variabile "fobj_mvar2" è inizializzata con l'indirizzo del blocco FUNCTION, questo provoca un errore di compilazione, poichè, anche se le signatures coincidono, il blocco non corrisponde ad un METHOD appartenente al tipo di PROCESS indicato nella definizione della variabile.
NOTA:
  • L'utilizzo di qualunque altro operatore di assegnamento diverso sa "=", provocherà un errore di compilazione.
  • Se si tenta di inizializzare un dato con blocco PROCESS, un valore costante diverso da 0 (o NULL), o altro tipo di dato, si avrà un errore di compilazione.
  • Se si tenta di inizializzare un dato con blocco FUNCTION, METHOD o funzione di sistema con signature distinta, si avrà un errore di compilazione.
  • Per inizializzare un dato è possibile indicare solo il nome del blocco o funzione di sistema, viceversa è possibile includere anche la signature.
  • Se si vuole assegnare un METHOD a un dato, questo deve essere indicato utilizzando il nome del tipo di PROCESS, seguito dall'operatore di accesso (.) e dal nome del METHOD (o direttamente dal nome del METHOD nel caso la definizione del FOBJECT sia dello stesso tipo di PROCESS), inoltre il METHOD deve essere stato dichiarato precedentemente nella lista dei metodi del PROCESS (per una spiegazione più dettagliata sui metodi, vedere la DOC "Process Method's"), altrimenti si avrà un errore di compilazione.
  • Se si tenta di assegnare un METHOD di tipo di accesso PRIVATE a un FOBJECT di tipo GLOBAL o PUBLIC, si avrà un errore di compilazione.
  • Se si tenta di assegnare un METHOD appartenente ad un tipo PROCESS diverso da quello indicato nella definizione del FOBJECT, si avrà un errore di compilazione.
  • Se si tenta di assegnare un blocco FUNCTION o funzione di sistema ad un FOBJECT definito con un tipo di PROCESS specifico, si avrà un errore di compilazione.
  • E' possibile assegnare ad un array, solo blocchi METHOD appartenenti ad un tipo di PROCESS indicato nella definizione, o solo blocchi FUNCTION e funzioni di sistema senza tipo di PROCESS specificato, altrimenti si avrà un errore di compilazione.
  • Se la signature del dato corrisponde a "()", per inizializzarlo con un blocco o funzione di sistema è necessario usare la speciale signature "(void)" dopo il nome dello stesso, altrimenti si avrà un errore di compilazione.
  • Un puntatore può essere inizializzato unicamente con il valore 0 (o NULL), o l'indirizzo di un'altro dato di tipo VOID * o FOBJECT le cui signatures coincidano, altrimenti si avrà un errore di compilazione.


3.2 - OPERAZIONI CON DATI FOBJECT - CONFRONTO
E' possibile confrontare dati di tipo FOBJECT con indirizzi di blocchi FUNCTION, METHOD, funzioni di sistema o con altri dati di tipo FOBJECT.

Esempio:
Source Code (Gemix) [ Download ] [ Hide ]
  • global
  •   fobject fobj_var(int);
  •   fobject fobj_var2(int, int);
  •   fobject fobj_var3(string);
  •   fobject fobj_var4(float, float);
  •   fobject fobj_array[3](string);
  •   fobject *fobj_ptr(float, float);
  •  
  • begin
  •   ...
  •  
  •   if(fobj_var3 == NULL)
  •     ...
  •   else if(fobj_var3 == func(string))
  •     ...
  •   else if(fobj_var3 == func2(string))
  •     ...
  •   end
  •  
  •   if(fobj_array[3] != NULL)
  •     fobj_array[3]("Gemix");
  •   end
  • end
  •  
  • process proc()
  • methods
  •   method public int set(int a, int b)
  •   begin
  •     ...
  •   end
  •  
  • begin
  •   ...
  • end
  •  
  • function func(int value)
  • begin
  •   ...
  • end
  •  
  • function func(string str)
  • begin
  •   ...
  • end
  •  
  • function func2(string str)
  • begin
  •   ...
  • end
  •  
  • function func2(float a, float b)
  • begin
  •   ...
  • end
  •  

L'esempio precedente esegue le seguenti operazioni di confronto dei dati globali:
  • La variabile "fobj_var3" è confrontata varie volte in una sentenza IF..ELSEIF per verificare se il suo valore è NULL o uguale all'indirizzo dei blocchi FUNCTION "func" e "func2".
  • L'elemento 3 dell'array "fobj_array" è confrontato con il valore NULL, se è vero, si produce la chiamata del blocco FUNCTION, METHOD o funzione di sistema assegnato all'FOBJECT.
NOTA:
  • L'utilizzo di qualunque altro operatore di assegnamento diverso da "==" o "!=", provocherà un errore di compilazione.
  • Se si tenta di confrontare un dato con blocco PROCESS, un valore costante diverso da 0 (o NULL), o altro tipo di dato, si avrà un errore di compilazione.
  • Se si vuole confrontare un METHOD a un dato, questo deve essere indicato utilizzando il nome del tipo di PROCESS, seguito dall'operatore di accesso (.) e dal nome del METHOD (o direttamente se il confronto si trova nella zone del programma che corrisponde al tipo di PROCESS al quale il METHOD appartiene), inoltre deve essere stato dichiarato precedentemente nella lista dei metodi del PRFOCESS (per una spiegazione più dettagliata sui metodo, vedere la DOC "Process Method's"), altrimenti si avrà un errore di compilazione.
  • Se la signature del dato corrisponde a "()", per confrontarlo con un blocco o funzione di sistema è necessario usare la speciale signature "(void)" dopo il nome dello stesso, altrimenti si avrà un errore di compilazione.
  • Un puntatore può essere confrontato con un blocco FUNCTION, METHOD, funzione di sistema o un'altro dato di tipo FOBJECT, solo se questo è derefenziato, altrimenti si avrà un errore di compilazione.


3.3 - OPERAZIONI CON DATI FOBJECT - CHIAMATE
I dati di tipo FOBJECT, permettono di chiamare indirettamente i blocchi FUNCTION, METHOD o le funzioni di sistema alle quali sono assegnati.
La forma di chiamata varia leggermente secondo il tipo di dati:
  • Per le variabili la forma è: name(arguments_list);
  • Per gli array la forma è: name[N](arguments_list);
  • Per i puntatori la forma è: (*name)(arguments_list); o anche name(arguments_list);
    • arguments_list - Rappresenta la lista degli argomenti passati ai parametri dell'FOBJECT da chiamare.

Esempio:
Source Code (Gemix) [ Download ] [ Hide ]
  • global
  •   fobject fobj_var(int);
  •   fobject fobj_var2(int, int);
  •   fobject fobj_var3(string);
  •   fobject fobj_var4(float, float);
  •   fobject fobj_array[3](string);
  •   fobject *fobj_ptr(float, float);
  •   fobject.proc fobj_mvar(int, int);
  •  
  •   proc proc_instance;
  •  
  • begin
  •   ...
  •  
  •   ...
  •  
  •   fobj_var3 = func;
  •  
  •   fobj_var3(); // ERROR, no vi sono corrispondenze di funzioni.
  •   fobj_var3("Gemix"); // OK, chiamata corretta.
  •  
  •   fobj_array[2]("Gemix"); // OK, chiamata corretta, tuttavia genera un errore di runtime poichè l'FOBJECT è 0.
  •  
  •   (*fobj_ptr)(); // ERROR, no vi sono corrispondenze di funzioni.
  •   (*fobj_ptr)(10.3f, 33.7f); // OK, chiamata corretta.
  •  
  •   proc_instance = proc();
  •   fobj_mvar = proc.set;
  •   fobj_mvar(); // ERROR, aspettando una sentenza.
  •   fobj_mvar(proc_instance); // ERROR, no vi sono corrispondenze di funzioni.
  •   fobj_mvar(proc_instance, 10, 10); // OK, chiamata corretta.
  • end
  •  
  • process proc()
  • methods
  •   method public int set(int a, int b)
  •   begin
  •     ...
  •   end
  •  
  • begin
  •   ...
  • end
  •  
  • function func(int value)
  • begin
  •   ...
  • end
  •  
  • function func(string str)
  • begin
  •   write(0, 160, 100, 4, str, 0);
  • end
  •  
  • function func2(string str)
  • begin
  •   ...
  • end
  •  
  • function func2(float a, float b)
  • begin
  •   ...
  • end
  •  

L'esempio precedente esegue le seguenti operazioni di chiamata dei dati globali:
  • La variabile "fobj_var3" è utilizzata per la chiamata senza argomenti, questo provoca un errore di compilazione poichè gli argomenti della chiamata non coincidono con i parametri della signature del FOBJECT.
  • La variabile "fobj_var3" è utilizzata per la chiamata con un argomento STRING, questo è corretto poichè il tipo ed il numero di argomenti della chiamata coincidono (o sono compatibili) con i parametri della signature del FOBJECT.
  • L'elemento 2 dell'array "fobj_array" è utilizzato per la chiamata con un argomento STRING, questo è corretto poichè il tipo ed il numero di argomenti della chiamata coincidono (o sono compatibili) con i parametri della signature del FOBJECT.
  • Il puntatore "fobj_ptr" è utilizzato per la chiamata senza argomenti, questo provoca un errore di compilazione poichè gli argomenti della chiamata non coincidono con i parametri della signature del FOBJECT, da notare inoltre è che il nome è dereferenziato e racchiuso da una coppia di (), questo è necessario per la corretta chiamata, visto che si tratta di un puntatore e l'operatore di dereferenzia "*" ha una priorità minore dell'operatore "()" di chiamata.
  • Il puntatore "fobj_ptr" è utilizzato per la chiamata con argomenti FLOAT, questo è corretto poichè il tipo ed il numero di argomenti della chiamata coincidono (o sono compatibili) con i parametri della signature del FOBJECT, da notare inoltre è che il nome è dereferenziato e racchiuso da una coppia di (), questo è necessario per la corretta chiamata, visto che si tratta di un puntatore e l'operatore di dereferenzia "*" ha una priorità minore dell'operatore "()" di chiamata.
  • La variabile "fobj_mvar" è utilizzata per la chiamata senza argomenti, questo provoca un errore di compilazione poichè l'FOBJECT è di un tipo PROCESS specifico, ed è necessario passare un ID di istanza valida dello stesso tipo PROCESS come primo argomento, inoltre gli argomenti della chiamata non coincidono con i parametri della signature del FOBJECT.
  • La variabile "fobj_mvar" è utilizzata per la chiamata senza argomenti, questo provoca un errore di compilazione poichè si abbia passato un ID di istanza valida dello stesso tipo PROCESS come primo argomento, gli altri argomenti della chiamata non coincidono con i parametri della signature del FOBJECT.
  • La variabile "fobj_mvar" è utilizzata per la chiamata con un argomento di tipo PROCESS uguale a quello indicato nella definizione del FOBJECT e due argomenti INT questo è corretto poichè il tipo ed il numero di argomenti della chiamata coincidono (o sono compatibili) con i parametri della signature del FOBJECT.
NOTA:
  • Se si tenta di chiamare un dato FOBJECT definito con un tipo di PROCESS specifico, senza passare come primo argomento in ID di istanza valida dello stesso tipo PROCESS, si avrà un errore di compilazione.
  • Se l'indirizzo assegnato al dato FOBJECT è un blocco FUNCTION o METHOD contenente argomenti di defaults, è possibile effettuare correttamente la chiamata specificando solo gli argomenti che corrispondano ai parametri di tipo non default.
  • Se gli argomenti della chiamata coincidono con i parametri della signature del FOBJECT, non si avrà nessun errore di compilazione, tuttavia se l'FOBJECT contiene il valore 0 (o NULL), si avrà un errore di esecuzione durante la chiamata.
User avatar
CicTec
 
Posts: 15202
Joined: Thu Jul 31, 2008 10:18 pm

Re: [DOC] Function Object's

Postby CicTec » Mon Nov 18, 2013 1:38 am

Aggiornata e migliorata DOC per il nuovo update della beta 7.0.
User avatar
CicTec
 
Posts: 15202
Joined: Thu Jul 31, 2008 10:18 pm


Return to Documentazione

Who is online

Users browsing this forum: No registered users and 1 guest