* * * *

Privacy Policy

Blog italiano

Clicca qui se vuoi andare al blog italiano su Lazarus e il pascal.

Forum ufficiale

Se non siete riusciti a reperire l'informazione che cercavate nei nostri articoli o sul nostro forum vi consiglio di visitare il
Forum ufficiale di Lazarus in lingua inglese.

Lazarus 1.0

Trascinare un file nel programma
DB concetti fondamentali e ZeosLib
Recuperare codice HTML da pagina web
Mandare mail con Lazarus
Stabilire il sistema operativo
Esempio lista in pascal
File INI
Codice di attivazione
Realizzare programmi multilingua
Lavorare con le directory
Utilizzare Unità esterne
TTreeView
TTreeview e Menu
Generare controlli RUN-TIME
LazReport, PDF ed immagini
Intercettare tasti premuti
Ampliare Lazarus
Lazarus e la crittografia
System Tray con Lazarus
UIB: Unified Interbase
Il file: questo sconosciuto
Conferma di chiusura di un applicazione
Liste e puntatori
Overload di funzioni
Funzioni a parametri variabili
Proprietà
Conversione numerica
TImage su Form e Panel
Indy gestiore server FTP lato Client
PopUpMenu sotto Pulsante (TSpeedButton)
Direttiva $macro
Toolbar
Evidenziare voci TreeView
Visualizzare un file Html esterno
StatusBar - aggirare l'errore variabile duplicata
Da DataSource a Excel
Le permutazioni
Brute force
Indy 10 - Invio email con allegati
La gestione degli errori in Lazarus
Pascal Script
Linux + Zeos + Firebird
Dataset virtuale
Overload di operatori
Lavorare con file in formato JSON con Lazarus
Zeos ... dietro le quinte (prima parte)
Disporre le finestre in un blocco unico (come Delphi)
Aspetto retrò (Cmd Line)
Lazarus 1.0
Come interfacciare periferica twain
Ubuntu - aggiornare free pascal e lazarus
fpcup: installazioni parallele di lazarus e fpc
Free Pascal e Lazarus sul Raspberry Pi
Cifratura: breve guida all'uso dell'algoritmo BlowFish con lazarus e free pascal.
Creare un server multithread
guida all'installazione di fpc trunk da subversion in linux gentoo
Indice
DB concetti fondamentali e connessioni standard
Advanced Record Syntax
DB concetti fondamentali e DBGrid
DB concetti fondamentali e TDBEdit, TDBMemo e TDBText
Advanced Record Syntax: un esempio pratico
Superclasse form base per programmi gestionali (e non)
Superclasse form base per programmi gestionali (e non) #2 - log, exception call stack, application toolbox
Superclasse form base per programmi gestionali (e non) #3 - traduzione delle form
Superclasse form base per programmi gestionali (e non) #4 - wait animation
Un dialog per la connessione al database:TfmSimpleDbConnectionDialog
Installare lazarus su mac osx sierra
immagine docker per lavorare con lazarus e free pascal
TDD o Test-Driven Development
Benvenuto! Effettua l'accesso oppure registrati.
Aprile 18, 2024, 11:21:56 pm

Inserisci il nome utente, la password e la durata della sessione.

177 Visitatori, 0 Utenti

Autore Topic: esperimenti con TNullable  (Letto 4587 volte)

DragoRosso

  • Scrittore
  • Hero Member
  • *****
  • Post: 1266
  • Karma: +43/-0
  • Prima ascoltare, poi decidere
Re:esperimenti con TNullable
« Risposta #15 il: Gennaio 11, 2022, 11:10:22 pm »
.... ho fallito con Lazarus ...  :-[ :-\ :'(

negli operators overload (class operator), c'è distinzione tra i parametri ma non tra i "risultati". In questo modo non è possibile definire l'overload dello stesso operatore, a meno di non definire più metodi distinti.

Ciò che volevo fare con il TNullable, in maniera semplice ed efficace non sembrerebbe possibile, almeno per lo mie skill con Lazarus.

D'altronde non è un caso se il NULL è gestito con un variant. I "Generic" hanno necessità di essere ancora sviluppati.

Ad esempio, il seguente codice non viene compilato, perchè per FPC le due implementazioni sono duplicate:
Codice: [Seleziona]
class operator TNullable.:= (aValue: TMyType): T;
begin
    ........
end;

class operator TNullable.:= (aValue: TMyType): Variant;
begin
   .........
end;


Parcheggiato per adesso il progetto, proverò con FPC 3.4 ....

Ciao
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

nomorelogic

  • Global Moderator
  • Hero Member
  • *****
  • Post: 2870
  • Karma: +20/-4
Re:esperimenti con TNullable
« Risposta #16 il: Gennaio 12, 2022, 08:58:37 am »
l'idea era buona, peccato non si possa fare

poi bisognava vedere come si comportava una variabile di questo tipo in una espressione di calcolo o concatenazione tra stringhe
però l'idea era buona sul serio e probabilmente il cast implicito al variant avrebbe funzionato a dovere

mi viene in mente  un workaround non proprio politically-correct :D
invece di usare i variant si potrebbero definire delle costanti con valori predefiniti che rappresentano il null
ad esempio:
Codice: [Seleziona]
const 
   NULLSTRING = '{NULL}';
   NULLINT = high(integer);
   NULLDATE = 0;

per cui si potrebbe scrivere:
Codice: [Seleziona]
var
   s: TNullableString;
begin

  s := NULLSTRING;

end;



l'assegnazione di NULLSTRING dovrebbe richiamare internamente il .Clear
poi, in una espressione o concatenazione, grazie agli operator overload, si potrebbe generare una eccezione

non è il massimo come cosa da implementare, ma si tratterebbe di poter definire delle costanti che scatenano l'assegnazione del NULL
 ::)

« Ultima modifica: Gennaio 12, 2022, 09:04:04 am da nomorelogic »
Imagination is more important than knowledge (A.Einstein)

nomorelogic

  • Global Moderator
  • Hero Member
  • *****
  • Post: 2870
  • Karma: +20/-4
Re:esperimenti con TNullable
« Risposta #17 il: Gennaio 12, 2022, 09:16:41 am »
comunque, ora che ci penso, si può sempre fare qualcosa del genere

Codice: [Seleziona]
var
  NullString,  // questa variabile è destinata a rimanere sempre a null (non va mai assegnata)
     s: TNullableString;
begin

  // per dare un valore
  s := 'ciao mondo';

  // per impostare a null
  s := NullString;
end;
Imagination is more important than knowledge (A.Einstein)

DragoRosso

  • Scrittore
  • Hero Member
  • *****
  • Post: 1266
  • Karma: +43/-0
  • Prima ascoltare, poi decidere
Re:esperimenti con TNullable
« Risposta #18 il: Gennaio 12, 2022, 10:45:17 am »
La funzione, teorica, del Nullable sarebbe quella di contenere due "stati" essenziali: uno è il tipo "specializzato" e l'altro lo stato di NULL o non assegnato.

Indipendentemente dalla rappresentazione interna, il problema concettuale è la sua rappresentazione esterna (assegnazione LEFT <-> RIGHT)

In questo momento ciò che può essere NULL, è rappresentato da una variant. Ed è su questo che ci si deve confrontare.

Sino a quando il compilatore non supporterà integralmente i GENERIC, e in particolare i record managed, non potranno esserci scorciatoie allo stato di fatto. Anche l'RTTI (definito sperimentale) andrebbe implementato.

Se io assegno a un NullableInteger.Value un Intero o un NULL (lasciando perdere per un attimo la conversione da altri formati come double) il "modo" di assegnazione dovrebbe essere sempre quello:
Codice: [Seleziona]
//Esempio, lasciando perdere il metodo di assegnazione (implicito o esplicito)
NullableInteger.Value := 1;
NullableInteger.Value := NULL;

ciò perchè non è noto in generale se il dato ricevuto sarà un intero o un NULL (ad esempio supponiamo che invece di una costante riceviamo l'input da un campo di un database).
Idem quando il valore viene "emesso".

L'alternativa c'è ovviamente, ma è assolutamente "grezza", inefficiente e priva di significato: basta avere due metodi di ingresso e due metodi di "uscita" e uso uno o l'altro in funzione di quello che è il valore effettivo come nell'esempio qui sotto
Codice: [Seleziona]
 //è solo esemplificativo !!!!
if Valore = NULL then
  NullableInteger.ValueNull := Valore
else
  NullableInteger.Value := Integer(Valore);

Con FPC 3.3.1 le cose non cambiano. Devo provare FPC 3.4.

Tanto per dare l'idea, questo è quello che volevo fare (stralcio), e che ho fatto da altre parti ....
Codice: [Seleziona]
    
    class operator Implicit(const Value: TNullable<T>): T;
    class operator Implicit(const Value: TNullable<T>): Variant;
    class operator Implicit(const Value: Pointer): TNullable<T>;
    class operator Implicit(const Value: T): TNullable<T>;
    class operator Implicit(const Value: Variant): TNullable<T>;

        // set values
        nullstr:=new(PNullString);
        nullstr^ := 'value 1';
        HashList.Add('one', nullstr);

        nullint:=new(PNullInteger);
        nullint^:=2;
        HashList.Add('two', nullint);

        nullint:=new(PNullInteger);
        nullint^:= NULL;
        HashList.Add('three', nullint);

RICORDO A TUTTI CHE LAZARUS si pone come linguaggio universale su tutte le piattaforme, e quindi per affrontare tali problematiche occorre una visione globale che molto spesso noi utenti non abbiamo. Gli sviluppatori invece hanno una sensibilità verso questi aspetti e quindi non propongono sicuramente una soluzione senza vagliare pro / contro a livello globale (codice esistente, piattaforme, impatto prestazionale)

Ciao ciao
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

nomorelogic

  • Global Moderator
  • Hero Member
  • *****
  • Post: 2870
  • Karma: +20/-4
Re:esperimenti con TNullable
« Risposta #19 il: Gennaio 12, 2022, 11:18:40 am »
Lazarus però vuole anche essere compatibile con Delphi.
Se in Delphi quello che hai postato viene compilato (e funziona), si può segnalare la cosa come richiesta di compatibilità
Imagination is more important than knowledge (A.Einstein)

nomorelogic

  • Global Moderator
  • Hero Member
  • *****
  • Post: 2870
  • Karma: +20/-4
Re:esperimenti con TNullable
« Risposta #20 il: Gennaio 12, 2022, 11:30:54 am »
L'alternativa c'è ovviamente, ma è assolutamente "grezza", inefficiente e priva di significato: basta avere due metodi di ingresso e due metodi di "uscita" e uso uno o l'altro in funzione di quello che è il valore effettivo come nell'esempio qui sotto
Codice: [Seleziona]
 //è solo esemplificativo !!!!
if Valore = NULL then
  NullableInteger.ValueNull := Valore
else
  NullableInteger.Value := Integer(Valore);


infatti questo non avrebbe senso, ma i campi dei database hanno .AsVariant quindi questo si potrebbe già fare

Codice: [Seleziona]
var 
  s: TNullableString;
begin
   ...
   s.AsVariant := Table1.FieldByName('campo1').AsVariant;
   ...
end;


Edit:
però torniamo sempre li, il fatto è che
Table1.FieldByName('campo1').AsInteger non varrà mai null
dovremmo invece avere
Table1.FieldByName('campo1').AsNullableInteger


Edit2:
penso che potrei provare un class Helper per TIntegerField...
appena posso provo

« Ultima modifica: Gennaio 12, 2022, 11:44:43 am da nomorelogic »
Imagination is more important than knowledge (A.Einstein)

nomorelogic

  • Global Moderator
  • Hero Member
  • *****
  • Post: 2870
  • Karma: +20/-4
Re:esperimenti con TNullable
« Risposta #21 il: Gennaio 12, 2022, 11:33:22 am »
Con FPC 3.3.1 le cose non cambiano. Devo provare FPC 3.4.

La fpc 3.3.x è la trunk che un giorno verrà rilasciata come 3.4 quindi se non funziona sulla 3.3.x non c'è speranza per la 3.4
Se implementano la cosa nella trunk invece sappiamo che arriverà nella 3.4
Imagination is more important than knowledge (A.Einstein)

DragoRosso

  • Scrittore
  • Hero Member
  • *****
  • Post: 1266
  • Karma: +43/-0
  • Prima ascoltare, poi decidere
Re:esperimenti con TNullable
« Risposta #22 il: Gennaio 12, 2022, 12:13:02 pm »
In Delphi quello che ho abbozzato funziona, ci sono ancora alcuni dettagli da definire (l'avevo abbozzato per portarlo su Lazarus).

Anche in Delphi il compilatore non è propriamente compatibile al 100% con i Generic, tanto è vero che i Nullable non sono supportati ufficialmente e secondo una nota di Embarcadero ci stanno lavorando.

Richiedere il porting sicuramente sarebbe da fare, fermo restando però che l'aspetto principale che è l'overloading completo (parametri dei metodi e valore di ritorno) Lazarus non c'è l'ha.

Pensa che ho già implementato l'ingresso di un dato variant e la sua conversione nel formato <T>, compreso il NULL, grazie alle RTTI.

Di fatto è un casting, ma comunque un casting a codice non a compilazione (ovviamente, essendo il dato di ingresso non specificato).

La compatibilità che chiederemmo secondo me è troppo precoce, e forse è già stata richiesta.

P.S.: ho aggiornato il tuo codice con qualcosa di più moderno, c'è pure l'enumerazione  ;D

Codice: [Seleziona]
var nullint: PNullInteger;
    nullstr: PNullString;
    i: integer;
    //HashList: TFPHashList;
    HashList: specialize TDictionary<string, pointer>;
    HashEl: specialize TPair<string, pointer>;
begin
  WriteLn('Start');
  //HashList:=TFPHashList.Create;
  HashList := specialize TDictionary<string, pointer>.Create;
  try
    // set values
    nullstr:=new(PNullString);
    nullstr^ := 'value 1';
    HashList.Add('one', nullstr);

    nullint:=new(PNullInteger);
    nullint^:=2;
    HashList.Add('two', nullint);

    nullint:=new(PNullInteger);
    nullint^:= 5;
    HashList.Add('three', nullint);

    // write
    //WriteLn('one = ', PNullString(HashList.Find('one'))^.Value);
    //WriteLn('two = ', PNullInteger(HashList.Find('two'))^ .Value);
    //WriteLn('three = ', PNullInteger(HashList.Find('three'))^ .Value);

    WriteLn('one = ', PNullString(HashList.Items['one'])^.Value);
    WriteLn('two = ', PNullInteger(HashList.Items['two'])^ .Value);
    WriteLn('three = ', PNullInteger(HashList.Items['three'])^.Value);

    // free values
    //for i := 0 to HashList.Count-1 do begin
    i := 0;
    for HashEl in HashList do begin
        try
          WriteLn('Dispose ' + i.ToString);
          // Dispose( PNullString(HashList[i]) );
          //FreeMem(HashList[i]);
          FreeMem(HashEl.Value);
        except
          on e: exception do begin
             WriteLn('Error during sispose (' + i.ToString + '):' + e.Message);
          end;
        end;
        inc(i);
    end;

  finally
    // free resources
    FreeAndNil(HashList);
    WriteLn('Stop');
  end;
end;             
« Ultima modifica: Gennaio 12, 2022, 12:25:54 pm da DragoRosso »
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

DragoRosso

  • Scrittore
  • Hero Member
  • *****
  • Post: 1266
  • Karma: +43/-0
  • Prima ascoltare, poi decidere
Re:esperimenti con TNullable
« Risposta #23 il: Gennaio 12, 2022, 12:18:30 pm »
però torniamo sempre li, il fatto è che
Table1.FieldByName('campo1').AsInteger non varrà mai null
dovremmo invece avere
Table1.FieldByName('campo1').AsNullableInteger

Secondo me, lo cose devono restare come sono: perchè in assegnazione devi "castare" il dato .... lascialo nativo....
Codice: [Seleziona]
Table1.FieldByName('campo1').Value := NullInt; <- Può essere valorizzato tranquillamente come NULL o Integer

EDIT: è da qui che ci deve essere il supporto dei Generic agli overload completi e del compilatore.

Ciao
« Ultima modifica: Gennaio 12, 2022, 12:22:03 pm da DragoRosso »
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

DragoRosso

  • Scrittore
  • Hero Member
  • *****
  • Post: 1266
  • Karma: +43/-0
  • Prima ascoltare, poi decidere
Re:esperimenti con TNullable
« Risposta #24 il: Gennaio 12, 2022, 02:56:01 pm »
Esempio:

Codice: [Seleziona]
        
        nullstr:=new(PNullString);
        nullstr^ := 'value 1';
        HashList.Add('one', nullstr);

        nullint:=new(PNullInteger);
        nullint^:=2;
        HashList.Add('two', nullint);

        nullint:=new(PNullInteger);
        nullint^:= NULL;
        HashList.Add('three', nullint);

        // write
        WriteLn('one = ', TNullString(HashList.Items['one']).Value);
        WriteLn('two = ', PNullInteger(HashList.Items['two'])^ .Value);
        WriteLn('three = ', TNullInteger(HashList.Items['three']).Value);  <- Qui il risultato dà 0 perchè WriteLn lo trasforma in 0

        testvariant := NullInt^;

        WriteLn('three is NULL ? -> ', testvariant = NULL);   <- qui viene testato invece che il valore NULL sia effettivamente "passato"

        nullint^:= 18;

        WriteLn('Ora invece è -> ', TNullInteger(NullInt).Value);
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

nomorelogic

  • Global Moderator
  • Hero Member
  • *****
  • Post: 2870
  • Karma: +20/-4
Re:esperimenti con TNullable
« Risposta #25 il: Gennaio 12, 2022, 03:11:45 pm »
quindi lo hai finito con i variant in Delphi?
il risultato è buono :)


cosa esce in output con?
Codice: [Seleziona]
WriteLn('three = ', TNullInteger(HashList.Items['three']).Value = NULL);
Imagination is more important than knowledge (A.Einstein)

DragoRosso

  • Scrittore
  • Hero Member
  • *****
  • Post: 1266
  • Karma: +43/-0
  • Prima ascoltare, poi decidere
Re:esperimenti con TNullable
« Risposta #26 il: Gennaio 12, 2022, 04:56:22 pm »
quindi lo hai finito con i variant in Delphi?
il risultato è buono :)

cosa esce in output con?
Codice: [Seleziona]
WriteLn('three = ', TNullInteger(HashList.Items['three']).Value = NULL);

No, non l'ho terminato. Infatti ..... ritorna FALSE

WriteLn con quel confronto richiede implicitamente un puntatore, puntatore che non è ovviamente NULL .... Stessa cosa che accade quando viene richiesto il valore dello stesso record più sopra (che è NULL ma WRITELN riporta 0). Devo implementare la class operator per i puntatori.

Il record come l'ho impostato adesso, funziona correttamente se usi la forma IMPLICITA (quindi senza il .Value) con le Variant o la forma esplicita / implicita però nello stesso formato <T>. L'implementazione di WRITELN probabilmente richiede un puntatore e il compilatore genera qualcosa che non controllo.

WriteLn non accetta la forma implicita del record (perchè manca la definizione di puntatore immagino).

Vedo di implementare anche i puntatori e fare qualche test per validare il tutto. Poi potremmo anche passare l'unità agli sviluppatori FPC e che se la sbroglino loro poi.

A più tardi.

Ciao
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

DragoRosso

  • Scrittore
  • Hero Member
  • *****
  • Post: 1266
  • Karma: +43/-0
  • Prima ascoltare, poi decidere
Re:esperimenti con TNullable
« Risposta #27 il: Gennaio 12, 2022, 06:29:01 pm »
Aggiornamento:

eh eh, non avevo pensato al compilatore .... alle volte è contorto ....

Nella forma:
Codice: [Seleziona]
TNullableInt(.....).Value = .......

Il maledetto converte l'espressione di destra nello stesso tipo di quella di sinistra ....  :o :o :o

EDIT: e la conversione avviene come RECORD MANAGED dello stesso tipo <T> tramite il metodo CREATE e tutte le funnzioni annesse.
         Quindi bisogna fare attenzione a quello che si fà, perchè viene usato direttamente dal compilatore....

Ora siamo a posto. La situazione comunque non è perfettamente omogenea.

Questo perchè non ci sono limiti alla fantasia e alla perversione, come insegna @nomorelogic (  :P ).

Se devo usare i Nullable, DEVO ESSERE CERTO che chi riceve i dati (ad esempio una variabile, una funzione un ...... GESTISCA CORRETTAMENTE I NULL !!!! (ovviamente ammesso che usi i NULL).

In pratica se ciò che esce dal Nullable non è gestibile dal ricevente .... verrà generato un errore runtime.

Questo è il caso di Writeln che dei NULL proprio non ne vuole sapere.

Per fare ciò sono ricorso ad uno stratagemma ... usare una chiamata differenziata così:

Codice: [Seleziona]
WriteLn('three = ', PNullInteger(HashList.Items['three'])^.GetValueOrDefault);

Il Nullable riporterà o il valore corretto, o nel caso di NULL il valore di default del tipo.

Ora si puà gestire correttamente il metodo implicito. Con i risultati attesi.

Codice: [Seleziona]
WriteLn('three is NULL ? -> ', PNullInteger(HashList.Items['three'])^ = NULL);

In pratica,  la proprietà "Value" del NULLABLE non si dovrebbe mai usare (dovrei implementare le EXPLICIT, ma di fatto non ha molto senso non potendo comunque generare con il NULL un valore diverso da ..... NULL  ;D )

Le Explicit avrebbero senso per consentire una conversione gestita invece che lasciare fare al compilatore.

Ma va al di fuori delle funzionalità del NULLABLE.
 
Unico problema ... funziona in Delphi ... fà uso di RTTI ...

Ciao
« Ultima modifica: Gennaio 12, 2022, 07:06:18 pm da DragoRosso »
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

DragoRosso

  • Scrittore
  • Hero Member
  • *****
  • Post: 1266
  • Karma: +43/-0
  • Prima ascoltare, poi decidere
Re:esperimenti con TNullable
« Risposta #28 il: Gennaio 12, 2022, 07:25:51 pm »
quindi lo hai finito con i variant in Delphi?

Nel Nullable il variant viene usato solo ed esclusivamente per accettare (leggere) il valore NULL (che non viene memorizzato nel record ovviamente), con relative funzionalità del compilatore, e per generare il NULL di ritorno.

Nel codice del nullable ho implementato la capacità di acquisire un valore variant anche diverso dal NULL e convertirlo se possibile nel tipo <T> (altrimenti genera un errore runtime). Così pure in uscita se ce nè la necessità,  il tutto dovrebbe essere "automatico" e sotto giudizio del compilatore grazie agli operatori IMPLICITI.

Ciao
:) Ogni alba è un regalo, ogni tramonto è una conquista :)

nomorelogic

  • Global Moderator
  • Hero Member
  • *****
  • Post: 2870
  • Karma: +20/-4
Re:esperimenti con TNullable
« Risposta #29 il: Gennaio 12, 2022, 08:12:30 pm »
bel lavoro, complimenti

ci hai pensato se dare in pasto il sorgente delphi a quelli di fpc?
secondo me ne vale la pena :)

nomorelogic


PS:
poi un giorno mi spieghi la storia della "perversione"  :o

Imagination is more important than knowledge (A.Einstein)

 

Recenti

How To

Utenti
  • Utenti in totale: 785
  • Latest: gmax
Stats
  • Post in totale: 18771
  • Topic in totale: 2233
  • Online Today: 427
  • Online Ever: 900
  • (Gennaio 21, 2020, 08:17:49 pm)
Utenti Online
Users: 0
Guests: 177
Total: 177

Disclaimer:

Questo blog non rappresenta una testata giornalistica poiché viene aggiornato senza alcuna periodicità. Non può pertanto considerarsi un prodotto editoriale ai sensi della legge n. 62/2001.