Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   einen Datensatzmitgliedsnamen dynamisch zuweisen (https://www.delphipraxis.net/210966-einen-datensatzmitgliedsnamen-dynamisch-zuweisen.html)

neumimnemecky 6. Jul 2022 09:58

einen Datensatzmitgliedsnamen dynamisch zuweisen
 
(to assign a record member name dynamically)

Guten Tag,
Ich suche Hilfe, um diesen Code zu vereinfachen und die aus der INI-Datei gelesenen Werte einer Variablen vom Typ "Datensatz" zuzuweisen, wobei sich der Elementname vom Schlüsselnamen in der INI-Datei unterscheidet. Ich habe nicht herausgefunden, wie ich den Mitgliedsnamen dynamisch zuweisen kann, da es zwei Gruppen (A und B) mit denselben Schlüsselnamen in der Datei, aber unterschiedlichen Namen im Datensatz gibt.

Code:
// ini file contains pairs key_name=value , the record contains abbreviated key name
if pars[0] = 'first_frame' then k := 'first_'+group else
  if pars[0] = 'end_frame' then k := 'last_'+group else
    if pars[0] = 'first_filename' then k := 'start_name_'+group else
       if pars[0] = 'path' then
         k := 'path_'+group else
           if pars[0] = 'check_if_files_exist' then
             k := 'check_files_exist_'+group;

if group = 'A' then
 begin
   if k = 'path_A' then
     ini.basic.path_A := pars[1] else
   if k = 'first_A' then
     ini.basic.first_A := pars[1] else
   if k = 'last_A' then
     ini.basic.last_A := pars[1] else
   if k = 'start_name_A' then
     ini.basic.start_name_A := pars[1] else
   if k = 'check_files_exist_A' then
     ini.basic.check_files_exist_A := true;
 end
else if group = 'B' then
 begin
   if k = 'path_B' then
     ini.basic.path_B := pars[1] else
   if k = 'first_B' then
     ini.basic.first_B := pars[1] else
   if k = 'last_B' then
     ini.basic.last_B := pars[1] else
   if k = 'start_name_B' then
     ini.basic.start_name_B := pars[1] else
   if k = 'check_files_exist_B' then
     ini.basic.check_files_exist_B := true;
 end;
end;
(English: Good morning,
I'm looking for help to simplify this code, assigning the values read from ini file to a variable of type Record, where the member name is different from the name of the key in the ini file. I haven't figured out how can I assign the member name dynamically because there are two groups (A and B) with the same key names in the file but different names in the record.)

Deklaration:
Code:
type TBasicSettings = record
  is_scroll_vertical: boolean; scroll_type=vertical
  is_image_direction_up: boolean;
  is_search_colors_HSL: boolean;
  path_A: string;
  path_B: string;
  check_files_exist_A: boolean;
  check_files_exist_B: boolean;
  RAM_disk: string;
  RAM_free: LongWord;
  start_name_A: string;first_filename
  first_A: string;first_frame
  last_A: string;end_frame=
  start_name_B: string;first_filename
  first_B: string;first_frame
  last_B: string;end_frame
  fps: byte;
  scroll_max_shift: integer;
end;
basic.ini
[GENERAL]
Code:
[GENERAL]
scroll_type=vertical
image_direction=up
search_colors_in=HSL
H_max_difference_deg=10%
S_max_difference_per=20%
L_max_difference_per=10%
;V_max_difference_per=10%
padding_left=22
padding_right=25
padding_top=0
padding_bottom=0
detect_max_character_count_on_screen=3
arrow_1_count=2
arrow_2_count=1
arrow_3_count=1
image_1_count=1
skip_detections_on_screen_when_frame_position_is_determined=1
group_to_copy_image_data_from=A
text_copy_color_direction=BA
RAM_disk=A:
RAM_disk_free_space=1GB

peterbelow 6. Jul 2022 11:47

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Nicht zu beantworten mit den gegebenen Infos. Zeig die Deklaration des Records und einen repäsentstiven Teil des Ini-Files.

neumimnemecky 6. Jul 2022 12:52

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Zitat:

Zitat von peterbelow (Beitrag 1508466)
Nicht zu beantworten mit den gegebenen Infos. Zeig die Deklaration des Records und einen repäsentstiven Teil des Ini-Files.

Ich habe die Frage zur Deklaration und ein Beispiel der INI-Datei hinzugefügt.

himitsu 6. Jul 2022 13:34

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Die Datenstruktur ändern
und gleiche Teile zusammenfassen?
(und dann nachfolgend den größten Teil ignorieren)

Delphi-Quellcode:
// nicht
ini.basic.path_A := pars[1]
ini.basic.path_B := pars[1]
// sondern
ini.basic.A.path := pars[1]
ini.basic.B.path := pars[1]

// für Objekte statt Records ... bei Records andersrum, also erst den Temp-Record füllen und dann zuweisen
if pars[0] = 'B' then X := ini.basic.B;
X.path := pars[1];





Delphi-Quellcode:
//if pars[0] = 'first_frame' then k := 'first_'+group
//else if pars[0] = 'end_frame' then k := 'last_'+group
//else if pars[0] = 'first_filename' then k := 'start_name_'+group
//else if pars[0] = 'path' then k := 'path_'+group
//else if pars[0] = 'check_if_files_exist' then k := 'check_files_exist_'+group
//else ; // was ist k, wenn nichts trifft?

// wozu den ersten Teil umbennen?
// sowas würde sich nur lohnen, wenn du anschließend mit diesen Namen via RTTI nach den Feldern suchen tätest.
TRTTIContext.Create.GetType(TIniBasicRecordIrgendwas).GetField(k).SetValue(ini.basic, pars[1]); // abgesehn vom check_if_files_exist
// es gibt auch Funktionen/Klassen/Frameworks, z.B. für INI/XML/JSON zu Record/Klasse, auch inkl. Umbenennen von SpeicherName zu FeldName

k := pars[0] + group;
if group = 'A' then begin
  if k = 'path_A' then ini.basic.path_A := pars[1]
  else if k = 'first_frame_A' then ini.basic.first_A := pars[1]
  else if k = 'end_frame_A' then ini.basic.last_A := pars[1]
  else if k = 'first_filename_A' then ini.basic.start_name_A := pars[1]
  else if k = 'check_if_files_exist_A' then ini.basic.check_files_exist_A := true;
end else if group = 'B' then begin
  if k = 'path_B' then ini.basic.path_B := pars[1]
  else if k = 'first_frame_B' then ini.basic.first_B := pars[1]
  else if k = 'end_frame_B' then ini.basic.last_B := pars[1]
  else if k = 'first_filename_B' then ini.basic.start_name_B := pars[1]
  else if k = 'check_if_files_exist_B' then ini.basic.check_files_exist_B := true;
end;
end; // ein end zuviel?
wozu nochmal die group extra vergleichen, wenn die schon in K enthalten ist?
Delphi-Quellcode:
k := pars[0] + group;
if k = 'path_A' then ini.basic.path_A := pars[1]
else if k = 'first_A' then ini.basic.first_A := pars[1]
else if k = 'last_A' then ini.basic.last_A := pars[1]
else if k = 'start_name_A' then ini.basic.start_name_A := pars[1]
else if k = 'check_files_exist_A' then ini.basic.check_files_exist_A := true
else if k = 'path_B' then ini.basic.path_B := pars[1]
else if k = 'first_B' then ini.basic.first_B := pars[1]
else if k = 'last_B' then ini.basic.last_B := pars[1]
else if k = 'start_name_B' then ini.basic.start_name_B := pars[1]
else if k = 'check_files_exist_B' then ini.basic.check_files_exist_B := true;
bzw.
Delphi-Quellcode:
case IndexText(pars[0] + group, ['path_A', 'first_A', 'last_A' , 'start_name_A' , 'check_files_exist_A' , 'path_B' , 'first_B' , 'last_B' , 'start_name_B', 'check_files_exist_B']) oF
  0: ini.basic.path_A := pars[1];
  1: ini.basic.first_A := pars[1];
  2: ini.basic.last_A := pars[1];
  3: ini.basic.start_name_A := pars[1];
  4: ini.basic.check_files_exist_A := true;
  5: ini.basic.path_B := pars[1];
  6: ini.basic.first_B := pars[1];
  7: ini.basic.last_B := pars[1];
  8: ini.basic.start_name_B := pars[1];
  9: ini.basic.check_files_exist_B := true;
end;


oder wozu unnötig umbenennen (Group anhängen), wenn die Groups eh getrennt behandelt werden?
Delphi-Quellcode:
//if pars[0] = 'first_frame' then k := 'first'
//else if pars[0] = 'end_frame' then k := 'last'
//else if pars[0] = 'first_filename' then k := 'start_name'
//else if pars[0] = 'path' then k := 'path'
//else if pars[0] = 'check_if_files_exist' then k := 'check_files_exist';

if group = 'A' then begin
  if pars[0] = 'path' then ini.basic.path_A := pars[1]
  else if pars[0] = 'first_frame' then ini.basic.first_A := pars[1]
  else if pars[0] = 'end_frame' then ini.basic.last_A := pars[1]
  else if pars[0] = 'first_filename' then ini.basic.start_name_A := pars[1]
  else if pars[0] = 'check_if_files_exist' then ini.basic.check_files_exist_A := true;
end else if group = 'B' then begin
  if k = 'path' then ini.basic.path_B := pars[1]
  else if pars[0] = 'first_frame' then ini.basic.first_B := pars[1]
  else if pars[0] = 'end_frame' then ini.basic.last_B := pars[1]
  else if pars[0] = 'first_filename' then ini.basic.start_name_B := pars[1]
  else if pars[0] = 'check_if_files_exist' then ini.basic.check_files_exist_B := true;
end;
bzw.
Delphi-Quellcode:
i := IndexText(pars[0] + group, ['path', 'first', 'last', 'start_name', 'check_files_exist']);
case group of
  'A':
    case i oF
      0: ini.basic.path_A := pars[1];
      1: ini.basic.first_A := pars[1];
      2: ini.basic.last_A := pars[1];
      3: ini.basic.start_name_A := pars[1];
      4: ini.basic.check_files_exist_A := true;
    end;
  'B':
    case i oF
      0: ini.basic.path_B := pars[1];
      1: ini.basic.first_B := pars[1];
      2: ini.basic.last_B := pars[1];
      3: ini.basic.start_name_B := pars[1];
      4: ini.basic.check_files_exist_B := true;
    end;
end;

neumimnemecky 6. Jul 2022 17:04

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Danke, aber
"IndexText" Problem in der D7 [Error]: Undeclared identifier: 'IndexText'.

himitsu 6. Jul 2022 17:17

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Zitat:

Zitat von neumimnemecky (Beitrag 1508491)
Problem in der D7

Wer macht denn sowas?

https://www.embarcadero.com/de/products/delphi/starter

Diese Funktionen gibt es bestimmt mindestens seit 2009 2006, also kurz nach D7, aber vielleicht auch schon im Delphi 7, wenn man die passende Unit einbindet? :angle:
Aktuell ist es in der System.StrUtils bzw. StrUtils ... aber mit der Zeit wurden auch Units umbenannt, bzw. Funktion in andere Units verschoben.

Delphi-Referenz durchsuchenStrUtils.IndexText (case-insensitive, also quasi mit einem AnsiUpperCase drumrum)
Delphi-Referenz durchsuchenStrUtils.IndexStr (case-sensitive)
Die gucken einfach nur in einem Array, ob/wo das da drin steht.

CASE kann ja leider im Delphi nur Ordinal und keine Strings, so wie nahezu jede andere Sprache :cry:

Delphi.Narium 6. Jul 2022 17:36

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Delphi 7 kennt aber AnsiIndexText (in der Unit StrUtils zu finden).

neumimnemecky 6. Jul 2022 18:09

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Viele Danke, daher habe ich den Code ein wenig geändert, um die Leistung zu steigern:

Code:
if group = 'A' then
 case AnsiIndexText(pars[0]+'_A', ['path_A', 'first_frame_A', 'end_frame_A' , 'first_filename_A' , 'check_if_files_exist_A']) oF
   0: ini.basic.A.path := pars[1];
   1: ini.basic.A.first := pars[1];
   2: ini.basic.A.last := pars[1];
   3: ini.basic.A.start_name := pars[1];
   4: ini.basic.A.check_files_exist := true;
 end
else
if group = 'B' then
 case AnsiIndexText(pars[0]+'_B', ['path_B' , 'first_frame_B' , 'end_frame_B' , 'first_filename_B', 'check_if_files_exist_B']) oF
   0: ini.basic.B.path := pars[1];
   1: ini.basic.B.first := pars[1];
   2: ini.basic.B.last := pars[1];
   3: ini.basic.B.start_name := pars[1];
   4: ini.basic.B.check_files_exist := true;
 end;
Es ist eine schöne Lösung.

himitsu 6. Jul 2022 18:29

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Delphi-Quellcode:
(pars[0]+'_B', ['path_B' , 'first_frame_B' , ...


Delphi-Quellcode:
(pars[0], ['path' , 'first_frame' , ...
:stupid:


Schneller ist es nur bedingt ... eher fast gleich schnell oder vielleicht eine Millisekunde langsamer (ein Funktionsaufruf, aber gleich viele String-Vergleiche),
aber es macht den Code kürzer und ohne die viele IF....THEN dazwischen auch etwas übersichlicher.

Hier die
Delphi-Quellcode:
123:
als Konstante oder mit
Delphi-Quellcode:
{Name}123:
, erspart sich, da der Kommentar/Name bereits als
Delphi-Quellcode:
.path :=
dran steht.

neumimnemecky 6. Jul 2022 18:42

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Zitat:

Zitat von himitsu (Beitrag 1508500)
Delphi-Quellcode:
(pars[0]+'_B', ['path_B' , 'first_frame_B' , ...


Delphi-Quellcode:
(pars[0], ['path' , 'first_frame' , ...
:stupid:


Schneller ist es nur bedingt ... eher fast gleich schnell oder vielleicht eine Millisekunde langsamer (ein Funktionsaufruf, aber gleich viele String-Vergleiche),
aber es macht den Code kürzer und ohne die viele IF....THEN dazwischen auch etwas übersichlicher.

Hier die
Delphi-Quellcode:
123:
als Konstante oder mit
Delphi-Quellcode:
{Name}123:
, erspart sich, da der Kommentar/Name bereits als
Delphi-Quellcode:
.path :=
dran steht.


Den letzten Satz verstehe ich nicht. Aber eine Millisekunde langsamer wäre viel. Hatten Sie nicht eine Mikrosekunde im Sinn?

Delphi.Narium 6. Jul 2022 18:47

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Ginge es eventuell auch so?
Delphi-Quellcode:
var
  ini : TBasicSettings;
begin
  case group of
    'A' : ini := ini.basic.A;
    'B' : ini := ini.basic.B;
  end;
  case AnsiIndexText(pars[0], ['path', 'first_frame', 'end_frame', 'first_filename', 'check_if_files_exist']) of
  // Wenn pars[0] immer in Kleinbuchstaben vorliegt, ginge auch
  // case AnsiIndexStr(pars[0], ['path', 'first_frame', 'end_frame', 'first_filename', 'check_if_files_exist']) of
  // das spart dann auch noch die Verpflichtung,
  // beim Vergleich nicht zwischen Groß- und Kleinschreibung zu unterscheiden.
    0: ini.path := pars[1];
    1: ini.first := pars[1];
    2: ini.last := pars[1];
    3: ini.start_name := pars[1];
    4: ini.check_files_exist := true;
  end;
end;

neumimnemecky 6. Jul 2022 19:03

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Nein, die ini ist ein Wrapper - sie enthält viele Einstellungen zu Suchfarben, Text, Bildern usw. Das würde sie verlieren.

himitsu 6. Jul 2022 19:33

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Der Funktionsaufruf ist nur einen "Hauch" langsamer ('ne Hand voll CPU-Takte),
dagegen beim case-insensitven braucht der eine Parameter nur einmal kleingemacht werden, anstatt mehrmal je Zeile/IF.

Delphi-Quellcode:
// turborennschnecke
if TArray.BinarySearch(['a', 'b', 'c'], AnsiLowerCase(S)) // kleingeschrieben und sortiert

// und natürlich ist ein
if AnsiIndexStr(AnsiLowerCase(S), ['a', 'b', 'c']) // Suchwerte direkt kleingeschrieben
// schneller/optimaler als ein
if AnsiIndexText(S, ['A', 'B', 'C'])
// und noch schneller als
if AnsiIndexStr(AnsiLowerCase(S), [AnsiLowerCase('A'), AnsiLowerCase('B'), AnsiLowerCase('C')])
// fast gleichschnell wie
K := AnsiLowerCase(S);
if K = AnsiLowerCase('A')
if K = AnsiLowerCase('B')
if K = AnsiLowerCase('C')
// aber schneller als
if AnsiLowerCase(S) = AnsiLowerCase('A')
if AnsiLowerCase(S) = AnsiLowerCase('B')
if AnsiLowerCase(S) = AnsiLowerCase('C')
// und die bummelschnecke
die RTTI (siehe vorher) ist am Kürzesten, aber dafür ist "langsam" etwas untertrieben ... aber eben einfach ... dagegen aber auch vom Compiler nicht validierbar, weil erst zur Laufzeit
Letztes wie die vielen IFs
und die ersten Beiden ... halt wie der Code schöner Lesbar ist.

Kommt halt drauf an, wie man es sieht ... lieber 'nen Hauch langsamer, aber dafür einfach/lesbar, oder eben nur schnell.
Delphi-Quellcode:
AnsiIndexStr(AnsiLowerCase(S), ['FirstFilename', 'CheckIfFilesExist', ...])


Delphi-Quellcode:
AnsiIndexText(S, ['firstfilename', 'checkiffilesexist', ...])


Wobei schnell, da nimmt man dann ein Directory, bzw. eine "sortierte" Liste/Array und macht dann eine binäre Suche (Delphi-Referenz durchsuchenTArray.BinarySearch, bzw. im Datenbank-Sprech: IndexScan), anstatt einer Volltextsuche (FullTableScan).



Delphi-Quellcode:
// AnsiIndexStr ist wie

if S = 'A' then ...
else if S = 'B' then ...
else if S = 'C' then ...

if AnsiSameStr(S, 'A' then ...
else if AnsiSameStr(S, 'B') then ...
else if AnsiSameStr(S, 'C') then ...

// AnsiIndexText ist wie

if AnsiLowerCase(S) = AnsiLowerCase('A') then ...
else if AnsiLowerCase(S) = AnsiLowerCase('B') then ...
else if AnsiLowerCase(S) = AnsiLowerCase('C') then ...

if AnsiSameText(S, 'A') then ...
else if AnsiSameText(S, 'B') then ...
else if AnsiSameText(S, 'C') then ...
Vorteil von IndexText vs. SameText/LowerCase ist, dass das
Delphi-Quellcode:
S
nur einmal, anstatt in jeder Zeile neu angepasst wird.

neumimnemecky 6. Jul 2022 19:40

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Ich bin mit der jetzigen Lösung zufrieden.
Code:
if group = 'A' then
 case AnsiIndexText(pars[0], ['path', 'first_frame', 'end_frame' , 'first_filename' , 'check_if_files_exist']) oF
   0: ini.basic.A.path := pars[1];
   1: ini.basic.A.first := pars[1];
   2: ini.basic.A.last := pars[1];
   3: ini.basic.A.start_name := pars[1];
   4: ini.basic.A.check_files_exist := true;
 end
else
if group = 'B' then
 case AnsiIndexText(pars[0], ['path' , 'first_frame' , 'end_frame' , 'first_filename', 'check_if_files_exist']) oF
   0: ini.basic.B.path := pars[1];
   1: ini.basic.B.first := pars[1];
   2: ini.basic.B.last := pars[1];
   3: ini.basic.B.start_name := pars[1];
   4: ini.basic.B.check_files_exist := true;
 end;
Ich denke, es ist nur eine Schande, dass ich etwas nicht mit Stil verwenden kann
Code:
ini.basic[group].check_files_exist

himitsu 6. Jul 2022 19:49

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Zitat:

Zitat von neumimnemecky (Beitrag 1508508)
Ich denke, es ist nur eine Schande, dass ich etwas nicht mit Stil verwenden kann

Könnte man schon, wenn man will
Delphi-Quellcode:
record // oder class
  A: TIrgendwas;
  B: TIrgendwas;
  C: TIrgendwas;
  property basic[S: string]: TIrgendwas read ... write ...; // die Getter/Setter nicht vergessen
end;
Zum Auslesen kein Problem ... beim Zuweisen mit Records muß man "bissl" aufpassen/rumpfuschen.

neumimnemecky 6. Jul 2022 20:11

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Zitat:

Zitat von himitsu (Beitrag 1508509)
Zitat:

Zitat von neumimnemecky (Beitrag 1508508)
Ich denke, es ist nur eine Schande, dass ich etwas nicht mit Stil verwenden kann

Könnte man schon, wenn man will
Delphi-Quellcode:
record // oder class
  A: TIrgendwas;
  B: TIrgendwas;
  C: TIrgendwas;
  property basic[S: string]: TIrgendwas read ... write ...; // die Getter/Setter nicht vergessen
end;
Zum Auslesen kein Problem ... beim Zuweisen mit Records muß man "bissl" aufpassen/rumpfuschen.

"property name declaration read ... write ..." Ich habe etwas Ähnliches in C/C++ gesehen, aber es ist eine sehr komplexe Sprache (programming language?) für mich. Hier muss ich Wissen hinzufügen. Wonach soll ich suchen? (englischer Begriff oder Name, lerne ich hauptsächlich von http://delphibasics.co.uk)

himitsu 6. Jul 2022 20:15

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
https://docwiki.embarcadero.com/RADS...aften_(Delphi)
https://docwiki.embarcadero.com/RADS...rties_(Delphi)

in älteren Delphis ging eine Überladung nicht (mehrere Array-Property), aber wenn, dann gingen sie nur als Default-Property,
und in noch älteren Delphi ging es nur in Klassen aber noch nicht in Records.



Und die string-like Array-Operatoren gab es im Delphi 7 auch noch lange nicht.

Delphi-Quellcode:
var X, Y: array of Integer;

X := [4, 5, 6];
Y := [7]; // SetLength(Y, 1); Y[0] := 7;
X := X + Y;
X := X + [1, 2, 3];
Insert(9, X, 2);

// [4, 5, 9, 6, 7, 1, 2, 3]
https://docwiki.embarcadero.com/RADS...Types_(Delphi)
https://blog.marcocantu.com/blog/201...delphixe7.html
http://embarcadero.qcomgroup.com.tw/...whats-new.html

neumimnemecky 6. Jul 2022 21:57

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Ich bin im Abschnitt "Indexspezifizierer" (Index Specifiers) verloren. Warum verwenden sie im Folgenden keine Klammern []? "property Left: Longint index 0 read GetCoordinate "
https://docwiki.embarcadero.com/RADS...Redeclarations

himitsu 6. Jul 2022 23:28

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Das ist was Anderes.

Über "diesen" Index kann man für mehrere Property den selben Getter/Setter verwenden.

Delphi-Quellcode:
property IrgendwasA: TSonstwas index 1 read GetItgendwas ...
property IrgendwasB: TSonstwas index 2 read GetItgendwas ...
property IrgendwasC: TSonstwas index 3 read GetItgendwas ...
Man kann den Index einfach einfach nur zur Verwendung der Erkennung welches Irgendwas# gemeint ist nutzen,
oder man kann diese 32 Bit auch verwende, u darin was zu Kodieren.

z.B. Property für den Zugriff auf einen/mehrere Bits (Bismaske) und darin codieren welche/wieviele Bits jeweils gemeint sind.
Oder man kann auch jeden anderen Ordinalen Typen dort angeben (z.B. Konstanten/Enums)

Delphi-Quellcode:
const cA = 1; // = Ord('A');
property IrgendwasA: TSonstwas index cA read GetItgendwas ...
property IrgendwasB: TSonstwas index cB read GetItgendwas ...

type TIrgendwasEnum = (iwA, iwB);
property IrgendwasA: TSonstwas index Ord(iwA) read GetItgendwas ...
property IrgendwasB: TSonstwas index Ord(iwB) read GetItgendwas ...
z.B. im Getter/Setter mit Index "rechnen",
das als Index für eine Array/Liste-Variable verwenden
oder für ein
Delphi-Quellcode:
CASE index OF
bzw.
Delphi-Quellcode:
CASE TIrgendwasEnum(index) OF
oder ...

freimatz 7. Jul 2022 06:31

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Zitat:

Zitat von neumimnemecky (Beitrag 1508508)
Ich bin mit der jetzigen Lösung zufrieden.
Code:
if group = 'A' then
 case AnsiIndexText(pars[0], ['path', 'first_frame', 'end_frame' , 'first_filename' , 'check_if_files_exist']) oF
   0: ini.basic.A.path := pars[1];
   1: ini.basic.A.first := pars[1];
   2: ini.basic.A.last := pars[1];
   3: ini.basic.A.start_name := pars[1];
   4: ini.basic.A.check_files_exist := true;
 end
else
if group = 'B' then
 case AnsiIndexText(pars[0], ['path' , 'first_frame' , 'end_frame' , 'first_filename', 'check_if_files_exist']) oF
   0: ini.basic.B.path := pars[1];
   1: ini.basic.B.first := pars[1];
   2: ini.basic.B.last := pars[1];
   3: ini.basic.B.start_name := pars[1];
   4: ini.basic.B.check_files_exist := true;
 end;

Da ist noch Luft nach oben.
a) Lokale Variablen mit schönen Namen verwenden und pars[0] bzw pars[1] zuweisen.
b) Prozedur machen für den doppelten Code und übergeben von ini.basic.A oder ini.basic.B. Dann spart man sich den doppelten Code. Dann der Aufruf:
Mach(group, 'A', pars[0], pars[1], ini.basic.A);
Mach(group, 'B', pars[0], pars[1], ini.basic.B);

neumimnemecky 7. Jul 2022 07:20

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Zitat:

Zitat von himitsu (Beitrag 1508520)
Das ist was Anderes.

Ich habe keinen Hochschulabschluss. Ohne konkrete Anwendungsbeispiele aus der Praxis sind mir diese Dinge zu kompliziert. Im Allgemeinen ist es für mich also nur eine Theorie, und Theorien sind für mich ohne ein konkretes Beispiel schwer zu verstehen. Deswegen verstehe ich das nicht. Insgesamt ist mir der Artikel zu schwer.

himitsu 7. Jul 2022 09:07

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Array-Property, das ist sowas, wie bei TList/TStrings/TStringList ... halt wie beim Array.
Delphi-Quellcode:
property Strings[Index: Integer]: string read GetString write SetStrings;

SL.Strings[index]
SL.Value[Name]
Default-Property, sind jene, welche "implizit" genutzt werden können.
Delphi-Quellcode:
property Strings[Index: Integer]: string read GetString write SetStrings; default;

SL.Strings[index] // direkt
SL[index] // default
dagegen die Default-Value bzw. Default-Property-Value

Delphi-Quellcode:
property Cool: Boolean read GetCool write SetCool default True;

Die Klasse Instanz neu erstelt, da ist der Wert True ... bzw. eigentlich heißt es, dass "True" nicht in der DFM gespeichert wird, also nur "False" steht drin.
(stimmen beide Aussagen aber nicht überein, dann hast du ein Problem, beim Laden einer DFM ... also aufpassen, wenn du Klassen baust)

Delphi-Quellcode:
property StringEx: string index 123 read GetString write SetStrings;

SL.StringEx // Index nur intern, z.B. zur Haldung von Kontroll-Daten und/oder um für mehrere Property nur "einen" Getter/Setter zu nutzen


// aber so fällt bestmmt auf, dass diese beiden "Index" nicht das Gleiche sind
property StringEx[Index2: Integer]: string index 123 read GetString write SetStrings;


// der Getter und Setter sieht hier erstmal gleich aus
property A: string index 1 read GetABC write SetABC;
property B: string index 2 read GetABC write SetABC;
property C: string index 3 read GetABC write SetABC;

property X[Index: Integer]: string read GetABC write SetABC;

procedure GetABC(Index: Integer): string;
begin
  //Assert((Index => 1) and (Index <= 3), 'peng');
  case Index of
    1: Result := 'Aaa';
    2: Result := 'Bbb';
    3: Result := 'Ccc';
    else raise Exception.Create('peng');
  end;
end;

neumimnemecky 7. Jul 2022 09:08

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Ich habe versucht, dem Beispiel in # 15 zu folgen. der Übersetzer nimmt keine Aufzeichnungen vor (Der Übersetzer nimmt den Code nicht; The compiler does not accept that). Ich probiere die Klasse aus. Fehler:

Code:
TBasicGroupSettings = record
  path: string;
  check_files_exist: boolean;
  start_name: string; // first_filename
  first: string; // first_filename
  last: string;  // end_frame
end;
// ini.basic has some general settings too
type TBasicSettings = class
  is_scroll_vertical: boolean;
  is_image_direction_up: boolean;
  is_search_colors_HSL: boolean;
  check_files_exist_A: boolean;
  check_files_exist_B: boolean;
  RAM_disk: string;
  RAM_free: LongWord; // v MB
  A: TBasicGroupSettings;
  B: TBasicGroupSettings;
  fps: byte;
  scroll_max_shift: integer;
  property basic[G:String] TBasicGroupSettings read Get_A write Set_A;
end;
[Error] constants_ini.pas(47): ':' expected but identifier 'TBasicGroupSettings' found

Code:
property basic[G:String] TBasicGroupSettings read Get_A write Set_A;

himitsu 7. Jul 2022 09:55

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Die Hilfe ist kaputt ... bei der automatischen Übersetzung sind ein paar Zeichen verloren gegangen.

Zitat:

[Error] constants_ini.pas(47): ':' expected but identifier 'TBasicGroupSettings' found
Rate mal, was zwischen Name und Typ kommt, bzw. vor den Typ. :zwinker:







Delphi-Quellcode:
BasicGroupSettings = class
und dann in TBasicSettings.Create die beiden Instanzen erstellen.

Und natürlich im TBasicSettings.Destroy wieder freigeben,
oder Beides als
Delphi-Quellcode:
= class(TComponent)
, beim Erstellen das den Owner auf Self und automatisch freigeben lassen.

Zitat:

Set_A
Als RECORD kann nur der gesamte Record zugewiesen werden.

Delphi-Quellcode:
// bei einem RECORD landet "first" nur im Result von Get_A, aber wird nicht automatisch in Set_A zurückgeschrieben.
basicsetting.basic['A'].first := 'abc';

// daher .... als CLASS gäbe es dieses Problem nicht
// eventuell vorher auslesen: temp := basicsetting.basic['A'];
temp.first := 'abc';
basicsetting.basic['A'] := temp;

neumimnemecky 7. Jul 2022 10:08

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Dann habe ich versucht, es mit einem Array zu tun, aber dort sagt es mir inkompatible Typen hinter dem Wort write.

Code:
type TBasicSettings = class
  private
  fGroups: array[0..1] of TBasicGroupSettings;
  function getA(Index: byte): TBasicGroupSettings;
  function getB(Index: byte): TBasicGroupSettings;
  procedure setA(Index: byte; O: TBasicGroupSettings);
  procedure setB(Index: byte; O: TBasicGroupSettings);
  public
  is_scroll_vertical: boolean; //  scroll_type=vertical
  is_image_direction_up: boolean; //  image_direction
  is_search_colors_HSL: boolean;
  check_files_exist_A: boolean;
  check_files_exist_B: boolean;
  RAM_disk: string;
  RAM_free: LongWord; // v MB
  start_name_A: string;
//  A: TBasicGroupSettings;
//  B: TBasicGroupSettings;
  fps: byte;
  scroll_max_shift: integer;
  // property basic[G:String] TBasicGroupSettings read Get_A write Set_A;
  property A: TBasicGroupSettings Index 0 read GetA write SetA;
  property B: TBasicGroupSettings Index 1 read GetB write SetB;
end;

Delphi.Narium 7. Jul 2022 10:11

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
In Delphi 7 gibt es diese Möglichkeiten noch nicht.

neumimnemecky 7. Jul 2022 10:34

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Zitat:

Zitat von himitsu (Beitrag 1508535)
Die Hilfe ist kaputt ... bei der automatischen Übersetzung sind ein paar Zeichen verloren gegangen.

Zitat:

[Error] constants_ini.pas(47): ':' expected but identifier 'TBasicGroupSettings' found
Rate mal, was zwischen Name und Typ kommt, bzw. vor den Typ. :zwinker:

Also wie hier (ich bevorzuge diese Quelle - das ist für mich leichter zu verstehen - weil sie die Beispiele enthält):
http://www.delphibasics.co.uk/RTL.asp?Name=Property

Code:
7.Property Name[Index : IndexType] : BaseType read Getter {default;}
Das habe ich in Beitrag 15# nicht gesehen.

Sie erklären auch das Standardwort, das mir unklar war.
"Default allows the Getter and Setter method calls to be replaced as in the following example:"
Code:
myValue := MyClass.Getter(23);
// can be replaced by :
myValue := MyCLass[23];
:

himitsu 7. Jul 2022 11:24

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Array-Property gingen in D7, aber Einiges davon funktioniert oftmals nur, wenn es auch als Default deklariert ist, sonst nicht.


Darum ist es in der StringList auch so
Delphi-Quellcode:
property Value[Name: String]: String ...
property ValueByIndex[Index: Integer]: String ...
weil es so nicht ging
Delphi-Quellcode:
property Value[Name: String]: String ...
property Value[Index: Integer]: String ...
seit den class-operators hatte ich mir dann so beholfen
Delphi-Quellcode:
type
  TIndexName = record
    Index: Integer;
    Name: string;
    class operator Implicit(Value: Integer): TIndexName;
    class operator Implicit(Value: String): TIndexName;
  end;

property Value[Name: TIndexName]: String ...; default;
ja, hier wäre auch Variant möglich , aber alles da oben stellt sicher, dass "nur" Integer oder String rein geht (Float, Boolean und Anderes wird direkt vom Compiler bemängelt)
Delphi-Quellcode:
property Value[Name: Variant]: String ...; default;


um sowas machen zu können

Delphi-Quellcode:
xyz.Value[123]
xyz.Value['abc']
xyz[123]
xyz['abc']

neumimnemecky 7. Jul 2022 11:44

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Also habe ich verschiedene Dinge ausprobiert, aber alles falsch ...

property A: TBasicGroupSettings Index 0 read GetA write SetA;default;
property B: TBasicGroupSettings Index 1 read GetB write SetB;default;
property group[Index: byte]: TBasicGroupSettings read GetB write SetB;default;

Denn dort muss String vorhanden sein, nicht Objekt. Das Objekt, das ich manuell in den Getter- und Setter-Implementierungscode implementieren muss.

himitsu 7. Jul 2022 12:05

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Wo ist da ein "String"?

Und was ist "Falsch"?


Delphi-Quellcode:
; default;
gibt es ausschließlich für Array-Property.
Delphi-Quellcode:
default EineOrdinaleKonstante;
, bzw.
Delphi-Quellcode:
stored TrueOderFalseOderFunktion;
, gibt es dagegen nur für einfache Property.


String: Meinst du sowas?
Delphi-Quellcode:
property Value[GroupABC: Char; Value: string]: string read GetValue write SetValue;

xxx.Value['A', 'start_name'] := 'abc';
xxx['A', 'start_name'] := 'abc'; // wenn mit "default"



PS:
Delphi-Quellcode:
property group[Index: Byte = 0]: TBasicGroupSettings read GetB write SetB;
läßt der Compiler erstmal durch, aber dann beim Setter/Getter knallt es, weil
Delphi-Quellcode:
procedure GetB(Index: Byte = 0): TBasicGroupSettings);
zwar noch geht,
es aber hier
Delphi-Quellcode:
procedure SetB(Index: Byte = 0; Value: TBasicGroupSettings {= nil});
leider nicht.

neumimnemecky 7. Jul 2022 12:47

AW: einen Datensatzmitgliedsnamen dynamisch zuweisen
 
Deklaration und Implementierung nur zu Testzwecken ist fertig. Aber das Programm hat jetzt viele Fehler im Anwendungsteil. Ich kann es also nicht als Ganzes kompilieren.

Code:
type TBasicSettings = class // the ini.basic
  private
  fGroups: array[0..1] of TBasicGroupSettings;
  function getA_(G:String): String;
  procedure setA_(G:String; V: String);
  function getA(Index: Integer): String;
  procedure setA(Index: Integer; V: String);
  function getAA(Index: Integer): String;
  procedure setAA(Index: Integer; V: String);
  public
  AA: TBasicGroupSettings;
  BB: TBasicGroupSettings;
  property basic[G:String]: String read GetA_ write SetA_;
  property A: String Index 0 read GetA write SetA;
  property B: String Index 1 read GetA write SetA;
  property group[Index: Integer]: String read GetA write SetA;default;
  constructor Create;
end;

type TSettings = record
  basic: TBasicSettings; // the ini
end;

implementation

constructor TBasicSettings.Create;
begin
end;

function TBasicSettings.getA_(G:String): String;
begin
  Result := 'test';
end;
procedure TBasicSettings.setA_(G:String; V: String);
begin
  G := V;
end;
function TBasicSettings.getA(Index: Integer): String;
begin
  Result := 'test';
end;
procedure TBasicSettings.setA(Index: Integer; V: String);
begin
  Index := 0;
end;
function TBasicSettings.getAA(Index: Integer): String;
begin
  Result := fGroups[0].path;
end;
procedure TBasicSettings.setAA(Index: Integer; V: String);
begin
  Index := 0;
end;
Jetzt ist die Frage, wie ich das Ganze modifizieren kann, damit ich den Code im Anwendungsteil ändern kann, zum Beispiel gibt es so eine Funktion:
Code:
// definition part - This must be edited to work correctly with newly created properties
procedure get_prefix_and_zeros_count(ini: TSettings; var prefix: string; var count: byte);
var i: byte;
begin
  with ini.bVal.A do
  prefix := copy(start,1,length(start)-length(ini.basic.A.first));
  i:=length(prefix);
  count := 0;
  repeat
    if prefix[i]='0' then
      inc(count);
    dec(i);
  until (prefix[i]<>'0') or (i=1);
  prefix := copy(prefix, 1, length(prefix)-count);
end;
Code:
// application part - This must be edited to work correctly with newly created properties
get_prefix_and_zeros_count(ini, ini.bVal.A.prefix, ini.bVal.A.maxZeros2Add);
get_prefix_and_zeros_count(ini, ini.bVal.B.prefix, ini.bVal.B.maxZeros2Add);
Ich persönlich denke, es wäre am besten, es so anzugehen:
Code:
get_prefix_and_zeros_count('A');
get_prefix_and_zeros_count('B');
Code:
// definition part changed
procedure get_prefix_and_zeros_count(g: string);
var count, i: byte;
begin
  // ini.bVal(g).maxZeros2Add is count of zeros
  with ini.bVal(g) do
  begin
  prefix := copy(start,1,length(start)-length(ini.basic(g).first));
  i:=length(prefix);
  count := 0;
  repeat
    if prefix[i]='0' then
      inc(count);
    dec(i);
  until (prefix[i]<>'0') or (i=1);
  ini.bVal(g, count);
  prefix := copy(prefix, 1, length(prefix)-count);
end;
end;
Aber ist eine Zuordnung auf diese Weise überhaupt möglich?
Code:
ini.bVal(g).prefix :=
Also sollte ich es nicht wiederholen, damit es so geht?

Code:
ini.bVal.A.prefix :=
Code:
ini.bVal.B.prefix :=
oder eher
Code:
ini.bVal[g].prefix :=
bVal ist Basic ähnlich, hat aber abgeleitete und berechnete Werte, während Basic die Einstellungen aus der INI-Datei hat.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:28 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz