Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: cappe - Ottobre 09, 2024, 07:46:25 am

Titolo: array of const
Inserito da: cappe - Ottobre 09, 2024, 07:46:25 am
Ciao a tutti,
   faccio una domanda che, forse, potrebbe essere interessante. Come si a passare un array of string in una funzione che accetta array of const in maniera semplice.

Ad esempio :

Codice: [Seleziona]
program Project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  Classes, sysutils
  { you can add units after this };

procedure caio(par : array of const);
begin
end;

var
  pp : TStringArray;
begin
  pp := TStringArray.Create('rrr', 'rrre', 'www', 'eeee');
  caio (pp);   // <-   non funziona
end.
     

Avevo trovato con TVarRec ma è lunga da fare. Esiste un modo più semplice?
Titolo: Re:array of const
Inserito da: nomorelogic - Ottobre 09, 2024, 10:23:58 am
ciao cappe

ho fatto qualche prova ma non ci sono riuscito

in questo link https://www.freepascal.org/docs-html/ref/refsu69.html

leggo
"The elements of the array of const are converted to a special variant record"

ho paura che TVarRec sia l'unico modo


Titolo: Re:array of const
Inserito da: DragoRosso - Ottobre 10, 2024, 11:07:47 am
A livello di compilazione "array of const" è equivalente ad "array of TVarRec" come ha accennato @nomorelogic.

La gestione è un pò più complicata di un tipo variant, ma di fatto non molto di più se si pensa che si stanno usando tipi "a caso" in un array ...

Codice: [Seleziona]
procedure TForm1.Prova(tip: array of const);
var i: TVarRec;
begin
  for i in tip  do
    begin
      if (i.VType = VTInteger)  then
        ShowMessage(inttostr(i.VInteger));
      if (i.VType = vtAnsiString)  then
        ShowMessage(String(i.VAnsiString));
      if (i.VType = vtExtended)  then
        ShowMessage(floattostr(Double(i.VExtended^)));
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var p: integer;
    a: string;
    b: double;
begin
  p := 10;
  a := 'Pollo arrosto';
  b := 234.87;
  Prova([p,a,b]);
end;

Occhio ai tipi, ad esempio il tipo VtAnsiString o VtExtended potrebbero essere diversi in realtà o "rappresentare" dati diversi: ad esempio se il progetto è impostato come UNICODE, i tipi string non sono più AnsiString ma diventano Unicode e quindi ciò che abbiamo scritto và implementato anche con VtUnicode ... il tipo VtExtended può rappresentare anche un valore di tipo single e quindi quando faccio il casting sul VExtended devo fare attenzione, ad esempio dovrei controllare la lunghezza del dato in modo da identificarlo tra single e double.

Occhio che usare una tale codifica potrebbe esporre il codice a certe e sicure anomalie in futuro, difficili poi da analizzare.
Con tutta sincerità, a meno che non sia indispensabile io non userei "array of const" per alcun motivo, come non userei TVarRec in quanto fortemente dipendente dalla versione del compilatore.

L'uso di array "aperti" con dati misti dovrebbe essere evitato il più possibile, a meno che i dati non siano a priori certi e noti (ad esempio: sicuramente double, interi e stringhe UNICODE).

Bye
Titolo: Re:array of const
Inserito da: DragoRosso - Ottobre 10, 2024, 12:15:23 pm
   faccio una domanda che, forse, potrebbe essere interessante. Come si a passare un array of string in una funzione che accetta array of const in maniera semplice.

Per venire alla tua domanda specifica dopo la dissertazione del post precedente , purtroppo in Lazarus non sono riuscito a fare funzionare ciò che dovrebbe essere logicamente funzionante e banale, ossia l'associazione tra tipi.
Il seguente codice funziona in Delphi ma non c'è modo di farlo funzionare in Lazarus, neanche usando i puntatori ... sicuramente sono io che sbaglio qualcosa ...

EDIT: usando il puntatore a tipo invece di un puntatore puro risolve la situazione in Lazarus.

Codice: [Seleziona]
type
TMioArray = array of string;
PTMioArray = ^TMioArray;

procedure TForm1.Prova(tip: array of const);
var i: TVarRec;
    punta: pointer;
    s: string;
begin
  for i in tip  do
    begin
      if (i.VType = VtInteger)  then
        ShowMessage(inttostr(i.VInteger));
      if (i.VType = VtAnsiString)  then                         //In Lazarus deve essere usato AnsiString, in Delphi invece UnicodeString
        ShowMessage(String(i.VAnsiString));
      if (i.VType = VtPointer)  then
       begin
        punta := PTMioArray(i.VPointer);
        for s in TMioArray(punta) do
          ShowMessage(s);
       end;
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var p: integer;
    a: string;
    b: double;
    ar: TMioArray;
begin
  p := 10;
  a := 'Pollo arrosto';
  b := 234.87;
  setlength(ar, 2);
  ar[0] := 'Pippo';
  ar[1] := 'Pluto';
  Prova([PTMioArray(ar), p, a, b]);
//  Prova([ar, p, a, b]); //<--- Questo funziona solo in Delphi, In Lazarus si deve usare un "puntatore a ar" al posto di ar ... non usare @ar !!!!
end;
Titolo: Re:array of const
Inserito da: DragoRosso - Ottobre 10, 2024, 06:15:25 pm
Si può usare anche con lo TStringArray ovviamente, ma bisogna usare il puntatore specializzato comunque:

Codice: [Seleziona]
type
TMioArray = array of string;
PTMioArray = ^TMioArray;

procedure TForm1.Prova(tip: array of const);
var i: TVarRec;
    punta: pointer;
    s: string;
begin
  for i in tip  do
    begin
      if (i.VType = VtInteger)  then
        ShowMessage(inttostr(i.VInteger));
      if (i.VType = VtAnsiString)  then                         //In Lazarus deve essere usato AnsiString, in Delphi invece UnicodeString
        ShowMessage(String(i.VAnsiString));
      if (i.VType = VtPointer)  then
       begin
        punta := PTMioArray(i.VPointer);
        for s in TMioArray(punta) do
          ShowMessage(s);
       end;
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var p: integer;
    a: string;
    b: double;
    ar: TMioArray;
    strar: TStringArray;
begin
  p := 10;
  a := 'Pollo arrosto';
  b := 234.87;
  setlength(ar, 2);
  ar[0] := 'Pippo';
  ar[1] := 'Pluto';
  strar := TSTringArray.Create('rrr', 'rrre', 'www', 'eeee');
  Prova([PTMioArray(strar), PTMioarray(ar), p, a, b]);
//  Prova([PTMioarr(ar), p, a, b]);
//  Prova([pp, p, a, b]); //<--- Questo funziona solo in Delphi, In Lazarus si deve usare un "puntatore a ar" al posto di ar ... non usare @ar !!!!
 
end;
Titolo: Re:array of const
Inserito da: cappe - Ottobre 11, 2024, 09:27:54 am
Grande,
  ti chiamerò Drago Rosso il magnifico.

ottima soluzione e farò così.
Titolo: Re:array of const
Inserito da: cappe - Ottobre 11, 2024, 09:32:34 am
Aspetta, però c'è un problema il tipo PStringArray deve essere conosciuto dall'iteratore VType. Quindi può andar bene se non utilizza qualcosa di già pronto.

L'idea è buona però bisogna vedere se funziona in qualcosa di già costruito, penso di no.

Ad ogni modo è un bellissimo esempio anche per chiarire

Provo
Titolo: Re:array of const
Inserito da: DragoRosso - Ottobre 11, 2024, 10:03:57 am
La lunghezza dell'array passato c'è comunque in quanto viene passata la struttura completa alla routine e il compilatore ovviamente sà come sbrogliare le cose.

Per l'iterazione, invece è necessario vedere se viene implementata l'interfaccia IEnumerator nel dato managed. Nel caso degli array, in FPC puoi sempre effettuare la enumerazione (se è questo che intendi).

Invece se intendi capire quale tipo di array viene passato e quindi ti ritrovi dalla parte della routine (per poter poi usare la corretta dereferenziazione) quello è da vedere: ad esempio ora si passano array di stringhe, ma potrei passare array di double.

Occorre verificare se le RTTI siano ancora valide e rilevabili ... in FPC sia le RTTI che le RTL sono sempre state un tallone di Achille.

Però, visto che si usa il passaggio con il puntatore direi che non dovrebbe essere un problema.

Ciao
Titolo: Re:array of const
Inserito da: DragoRosso - Ottobre 11, 2024, 10:39:52 am
La definizione di TVarRec definisce le seguenti tipologie di tipi:

Codice: [Seleziona]
   const
      vtInteger       = 0;
      vtBoolean       = 1;
      vtChar          = 2;
      vtExtended      = 3;
      vtString        = 4;
      vtPointer       = 5;
      vtPChar         = 6;
      vtObject        = 7;
      vtClass         = 8;
      vtWideChar      = 9;
      vtPWideChar     = 10;
      vtAnsiString    = 11;
      vtCurrency      = 12;
      vtVariant       = 13;
      vtInterface     = 14;
      vtWideString    = 15;
      vtInt64         = 16;
      vtQWord         = 17;
      vtUnicodeString = 18;

Per qualsiasi ulteriore tipo (come negli esempi sopra,  un'array) viene usato il VtPointer.

Nei variant (TVarType) sono invece identificati questi tipi, in più ci sono una serie di routine che aiutano a runtime a definire ulteriori caratteristiche del variant (ad esempio IsVarArray).

Codice: [Seleziona]
  varEmpty = 0;
   varNull = 1;
   varSmallInt = 2;
   varInteger = 3;
   varSingle = 4;
   varDouble = 5;
   varDate = 7;
   varCurrency = 6;
   varOleStr = 8;
   varDispatch = 9;
   varError = 10;
   varBoolean = 11;
   varVariant = 12;
   varUnknown = 13;
   varDecimal = 14;
   varShortInt = 16;
   varByte = 17;
   varWord = 18;
   varLongWord = 19;
   varInt64 = 20;
   varQWord = 21;

EDIT: a livello di curiosità, il costrutto "array of const" è quello usato ad esempio nella definizione della funzione Format che se fate presente viene usata allo stesso modo degli esempi riportati sopra:

Citazione
Format('%d - %s - %f', [p, a, b]);