Wie auf unbekanntes Element eines Recorarrays zugreifen?

Für Fragen rund um die Ide und zum Debugger
br_klaus
Beiträge: 244
Registriert: Do 21. Jan 2010, 22:33
OS, Lazarus, FPC: Windows Vista (L 0.9.31 FPC 2.5.1)
CPU-Target: 32Bit
Wohnort: z.z. Brasilien, sonst 82335 Berg-Leoni (südlich von München)

Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von br_klaus »

Hallo,

gibt es eine Möglichkeit, bei einem Recordarray auf ein einzelnes Element eines bestimmten Records zuzugreifen, um dann dessen Typ (Byte, Word, String etc.) und dementsprechend auch sizeof() herauszufinden?
Ich bräuchte das, um dann die Records nach diesem Element sortieren zu können.
Der Compiler macht das ja auch. Aber gibt es dafür auch geeignete Prozeduren/Funktionen?
Herzlichen Dank.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von Socke »

br_klaus hat geschrieben:gibt es eine Möglichkeit, bei einem Recordarray auf ein einzelnes Element eines bestimmten Records zuzugreifen, um dann dessen Typ (Byte, Word, String etc.) und dementsprechend auch sizeof() herauszufinden?
Bei Records musst du immer die Definition des Record-Typen haben. Ohne hast du keine Chance die in einem Record enthaltenen Daten richtig zu verarbeiten.
br_klaus hat geschrieben:Der Compiler macht das ja auch.
An welcher Stelle?
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
willi4willi
Lazarusforum e. V.
Beiträge: 172
Registriert: Sa 1. Nov 2008, 18:06
OS, Lazarus, FPC: Lazarus 3.8 FPC 3.2.2 x86_64-win64-win32/win64 x86_64-linux-gtk2
CPU-Target: i386, win64, arm

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von willi4willi »

Hallo br_klaus,

wenn Du den Namen des Elements kennst, kannst Du den Typ ermitteln, indem Du das Element einer Variant-Variable zuordnest und dann mit der Funktion VarTest den Typ herausfindest.

Du benötigst dazu die Unit variants.

Viele Grüße Willi4Willi
 

Viele Grüße

Willi4Willi

------------

br_klaus
Beiträge: 244
Registriert: Do 21. Jan 2010, 22:33
OS, Lazarus, FPC: Windows Vista (L 0.9.31 FPC 2.5.1)
CPU-Target: 32Bit
Wohnort: z.z. Brasilien, sonst 82335 Berg-Leoni (südlich von München)

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von br_klaus »

willi4willi hat geschrieben:Hallo br_klaus,

wenn Du den Namen des Elements kennst, kannst Du den Typ ermitteln, indem Du das Element einer Variant-Variable zuordnest und dann mit der Funktion VarTest den Typ herausfindest.

Du benötigst dazu die Unit variants.

Viele Grüße Willi4Willi
Hallo, Wili4Willi,
herzlichen Dank für Deinen Hinweis. Aber in der Unit Varians finde ich zwar eine Menge von Funktionen, aber keine namens VarTest (die gibt es in ganz Lazarus nicht).

Wie macht man das denn konkret: das Element einer Variant-Variablen zuordnen und dann deren Typ herausfinden?

herzlichen Dank!

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von Socke »

br_klaus hat geschrieben:Wie macht man das denn konkret: das Element einer Variant-Variablen zuordnen und dann deren Typ herausfinden?
Das ganze geht auch ohne Variants:
  • Records unterstützen kein RTTI.
  • Daher: Der Name eines Record-Elements muss beim Übersetzen des Programms bekannt sein.
  • Daher: kann man die Compiler-Funktion typeinfo() (oder typinfo(), da bin ich mir nicht mehr ganz sicher) verwenden um an Typinformationen zu gelangen. Der Compiler ersetzt die Funktion zur Übersetzungszeit mit einem entsprechenden Funktionsaufruf.
Über Variants geht das in etwa wie im folgenden Quelltext gezeigt. Nachteilig: Variants sind nicht gerade für ihre Geschwindigkeit bekannt.

Code: Alles auswählen

type TMyRecord = record
  x: Integer;
end;
 
var
  v: Variant;
  r: TMyRecord;
begin
  v := r.x;
  writeln(varType(v)); // was du auch immer damit tun möchtest
end;
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
willi4willi
Lazarusforum e. V.
Beiträge: 172
Registriert: Sa 1. Nov 2008, 18:06
OS, Lazarus, FPC: Lazarus 3.8 FPC 3.2.2 x86_64-win64-win32/win64 x86_64-linux-gtk2
CPU-Target: i386, win64, arm

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von willi4willi »

Oh, verzeih. Die Funktion heißt natürlich VarType().

Mit folgender Funktion kann man mal testen. Du machst das dann sicherlich auf die Art und Weise, wie socke sie beschrieben hat.

Code: Alles auswählen

 
Procedure VarTest(v:variant);
begin
  case VarType(v) of
    varEmpty     : showmessage('$0000 Die Variante hat den Status Unassigned.');
    VarNull      : showmessage('$0001 Die Variante hat den Wert Null.');
    VarSmallint  : showmessage('$0002 16-Bit-Integer mit Vorzeichen (Typ Smallint).');
    VarInteger   : showmessage('$0003 32-Bit-Integer mit Vorzeichen (Typ Integer).');
    VarSingle    : showmessage('$0004 Gleitkommawert mit einfacher Genauigkeit (Typ Single).');
    VarDouble    : showmessage('$0005 Gleitkommawert mit doppelter Genauigkeit (Typ Double).');
    VarCurrency  : showmessage('$0006 Gleitkommawert für Währungsbeträge (Typ Currency).');
    VarDate      : showmessage('$0007 Datums-/Zeitwert (Typ TDateTime).');
    VarOleStr    : showmessage('$0008 Referenz auf einen OLE-String (dynamisch verwalteter Unicode-String).');
    VarDispatch  : showmessage('$0009 Referenz auf ein OLE-Automatisierungsobjekt (Zeiger auf IDispatch-Schnittstelle). ');
    VarError     : showmessage('$000A Betriebssystem-Fehlercode.');
    VarBoolean   : showmessage('$000B Boolescher 16-Bit-Wert (Typ WordBool).');
    VarVariant   : showmessage('$000C Variante (nur in Verbindung mit varianten Arrays).');
    VarUnknown   : showmessage('$000D Referenz auf ein unbekanntes OLE-Objekt (Zeiger auf IUnknown-Schnittstelle).');
    VarByte      : showmessage('$0011 8-Bit-Integer ohne Vorzeichen (Typ Byte).');
    VarString    : showmessage('$0100 Referenz auf einen dynamisch verwalteten langen String (Typ AnsiString).');
    VarTypeMask  : showmessage('$0FFF Bitmaske zur Ermittlung des Typencodes.');
    VarArray     : showmessage('$2000 Bit zur Kennzeichnung eines varianten Arrays.');
  else
    showmessage('was anderes');
  end;
end;               
 
Viele Grüße!

Willi4Willi
 

Viele Grüße

Willi4Willi

------------

br_klaus
Beiträge: 244
Registriert: Do 21. Jan 2010, 22:33
OS, Lazarus, FPC: Windows Vista (L 0.9.31 FPC 2.5.1)
CPU-Target: 32Bit
Wohnort: z.z. Brasilien, sonst 82335 Berg-Leoni (südlich von München)

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von br_klaus »

willi4willi hat geschrieben:Oh, verzeih. Die Funktion heißt natürlich VarType().

Mit folgender Funktion kann man mal testen. Du machst das dann sicherlich auf die Art und Weise, wie socke sie beschrieben hat.
....

Viele Grüße!

Willi4Willi
Herzlichen dank für diesen Hinweis. Es heißt jedoch, variants seien extrem langsam.
Wie macht das denn der Compiler selber, wenn er den Typ einer Variablen ermiteln will?
Ich nehme an, daß er intern eine (oder mehrere) varlist erstellt mit jeweils Name, Klassenname, Speicherplatz, Wert, ob Teil einer anderen Variablen (zB Record oder userdefinierte Klasse), Ort der Definition (Datei, Zeile, Spalte), sonst könnte er ja nicht bei Fehlermeldungen (zB Doppeldeklaration) angeben, wo eine Variable desselben Namens schon erstellt wurde. Und bei Arrays muß er ja auch unterscheiden können zwischen fixen arrays[x..y] of ... und dynamischen arrays of ...
Gibt es eine Möglichkeit, auf diese Liste zugreifen zu können, bzw kann mir jemand sagen, wo (in welcher Unit) diese Liste erstellt bzw. ausgelesen wird (mit welchen Prozeduren)?
Herzlichen Dank!

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von mschnell »

Kannst Du nicht 'mal Deine Record- / Array- (oder was auch immer) Definition posten ?

-Michael
Zuletzt geändert von mschnell am Mi 28. Nov 2012, 10:45, insgesamt 1-mal geändert.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von Socke »

br_klaus hat geschrieben:Herzlichen dank für diesen Hinweis. Es heißt jedoch, variants seien extrem langsam.
Wie macht das denn der Compiler selber, wenn er den Typ einer Variablen ermiteln will?
Variants sind auch (im Vergleich zu nativen Datentypen wie Integer oder Char) sehr langsam, da einiges an Verwaltung zu den Daten hinzu kommt.

Der Compiler erstellt selbstverständlich ebenfalls Typinformationen. Diese werden aber nur Teilweise für Klassen (mit dem Compilerschalter {$M+}) in das fertige Programm übernommen (RTTI). Für alle primitiven Datentypen gibt es den oben von mir beschriebenen Weg um an Daten zu kommen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

br_klaus
Beiträge: 244
Registriert: Do 21. Jan 2010, 22:33
OS, Lazarus, FPC: Windows Vista (L 0.9.31 FPC 2.5.1)
CPU-Target: 32Bit
Wohnort: z.z. Brasilien, sonst 82335 Berg-Leoni (südlich von München)

Re: Wie auf unbekanntes Element eines Recordarrays zugreifen

Beitrag von br_klaus »

mschnell hat geschrieben:Kannst Du nicht 'mal Deine Record- / Array- (oder was auch immer) Definition posten ?

-Michael
Wenn ich zB folgendes habe:

Code: Alles auswählen

  Type TRec = record a:integer; b:boolean; c:string; d:pointer; end;
  var recs: array of TRec;
 
dann würde ich die gerne sortieren nach einzelnen Elementen, und zwar, indem ich nur einen String Sortstr anzugeben brauche, der die Sortierreihenfolge enthält, zB '2 1 0 3'.

Code: Alles auswählen

    procedure SortRecs(sortstr);
 
Mit vartype kann ich zwar den Typus der einzelnen Komponenten des Records bestimmen und somit auch die entsprechende Sort- (TListSortCompare-) Funktion, aber dazu muß ich konkret die Namen der Variablen eingeben.
Der Compiler macht das sicher eleganter mit internen arrays. Nur hat der als Ausgangspunkt i.a. die Cursorposition bzw die zu kompilierende Datei. Ich habe schon einige Units gefunden, die damit zu tun haben: bei den CodeTools (finddeclaration u.ä.) und auch bei den packages\fcl-passrc die Unit Pastree.

Es wird wohl keine Möglichkeit geben, die Variablen automatisch in einem array zu speichern mit dem jeweiligen Variablentyp (und dann natürlich auch den Offset im Record und die Speichergröße), oder?

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Wie auf unbekanntes Element eines Recordarrays zugreifen

Beitrag von mschnell »

br_klaus hat geschrieben:dann würde ich die gerne sortieren nach einzelnen Elementen, und zwar, indem ich nur einen String Sortstr anzugeben brauche, der die Sortierreihenfolge enthält, zB '2 1 0 3'.
Mir ist völlig schleierhaft, was Du damit meinst und was das mit irgendwelchen Typen zu tun hat.

br_klaus hat geschrieben:Es wird wohl keine Möglichkeit geben, die Variablen automatisch in einem array zu speichern mit dem jeweiligen Variablentyp (und dann natürlich auch den Offset im Record und die Speichergröße), oder?
Auch hier ist mir völlig schleierhaft, was Du da vor hast.

Aber jedenfalls ist der Record Typ ja programmatisch vorgegeben, deshalb sind die Typen der Elemente zur Compile-Zeit bekannt und brauchen nicht irgendwie zur Laufzeit dynamisch bestimmt werden.

-Michael

br_klaus
Beiträge: 244
Registriert: Do 21. Jan 2010, 22:33
OS, Lazarus, FPC: Windows Vista (L 0.9.31 FPC 2.5.1)
CPU-Target: 32Bit
Wohnort: z.z. Brasilien, sonst 82335 Berg-Leoni (südlich von München)

Re: Wie auf unbekanntes Element eines Recordarrays zugreifen

Beitrag von br_klaus »

mschnell hat geschrieben:
br_klaus hat geschrieben:dann würde ich die gerne sortieren nach einzelnen Elementen, und zwar, indem ich nur einen String Sortstr anzugeben brauche, der die Sortierreihenfolge enthält, zB '2 1 0 3'.
Mir ist völlig schleierhaft, was Du damit meinst und was das mit irgendwelchen Typen zu tun hat.

br_klaus hat geschrieben:Es wird wohl keine Möglichkeit geben, die Variablen automatisch in einem array zu speichern mit dem jeweiligen Variablentyp (und dann natürlich auch den Offset im Record und die Speichergröße), oder?


Auch hier ist mir völlig schleierhaft, was Du da vor hast.

Aber jedenfalls ist der Record Typ ja programmatisch vorgegeben, deshalb sind die Typen der Elemente zur Compile-Zeit bekannt und brauchen nicht irgendwie zur Laufzeit dynamisch bestimmt werden.

-Michael
Die procedure SortRecs('0 2 1 3') soll folgendes machen: Sortieren der Recs mit folgender Vergleichsfunktion:

Code: Alles auswählen

function CompareRecs(p,q:pointer):integer; 
 var off,j:integer; 
 begin j:=0; result:=0;
    repeat 
       off := offs[seq[j]]; func:=sorts[typ[seq[j]]];
           result:=func((P+off)^, (q+off)^);
           if result<>0 then exit;
           inc(j);
      until j>= length(seq);
 end;
 
Dabei ist seq ein IntegerArray mit der Sortiersequenz, in diesem Fall (0,2,1,3),
offs ist ein IntegerArray mit den Offsets (zum Recordbeginn) der einzelnen Record-Komponenten
off daher der Offset der jeweiligen zu sortierenden Variablen
sorts ist ein array of TListSortCompare, je nach Typ der zu vergleichenden Variable (und hier benötige ich den Typ), zB wenn Integer, dann IntegerVergleich, etc.
zB.

Code: Alles auswählen

Function SortCompareInt(p,q:pointer):integer; 
  begin result:=CompareInt(Pinteger(p)^, PInteger(q)^) end;
//mit
function CompareInt(x,y:integer):integer; 
  begin result:=0; if x=y then exit; if x>y then inc(result) else dec(result); end;
 
Wie soll denn der Compiler die Prozedur
Sortrecs('0 2 1 3')
ausführen, wenn er nicht weiß, von welchem Typ das erste,zweite,dritte,.. Element des Records ist?

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von mschnell »

"Normal" wäre, eine an die Liste zu übergebende Vergleichsfunktion etwa wie:

Code: Alles auswählen

 
Type TpRec = ^Trec;
 
function CompareRecs(p,q:pointer):integer; 
var 
 rp, rq TpRec;
begin
  rp := TpRec(p);
  rq := TpRec(q);  
  Result := rq.a - rp.a;
end;
-Michael
Zuletzt geändert von mschnell am Sa 1. Dez 2012, 02:01, insgesamt 1-mal geändert.

br_klaus
Beiträge: 244
Registriert: Do 21. Jan 2010, 22:33
OS, Lazarus, FPC: Windows Vista (L 0.9.31 FPC 2.5.1)
CPU-Target: 32Bit
Wohnort: z.z. Brasilien, sonst 82335 Berg-Leoni (südlich von München)

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von br_klaus »

[quote="mschnell"]"Normal" wäre eine an die Liste zu übergebende Vergleichsfunktion etwa wie:

Code: Alles auswählen

 
Type TpRec = ^Trec;
 
function CompareRecs(p,q:pointer):integer; 
var 
 rp, rq TpRec;
begin
  rp := TpRec(p);
  rq := TpRec(q);  
  Result := rq.a - rp.a;
end;
An so etwas hatte ich gar icht gedacht. Herzlichen Dank.
Aber das geht wohl nur bei Zahlen. Bei Strings muß ich sicher byteweise vergleichen. Und bei booleans?

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Wie auf unbekanntes Element eines Recorarrays zugreifen?

Beitrag von Socke »

br_klaus hat geschrieben:Und bei booleans?
Der Datentyp Boolean lässt sich nicht ordinal vergleichen. Du kannst zwischen den Werten True und False nur eine Gleichheit bzw. Ungleichheit feststellen. Welches davon in einer Liste zuerst stehen soll, ist anwendungsabhängig und muss daher von dir festgelegt werden.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Antworten