![]() |
Delphi-Version: 11 Alexandria
into Record?
Moin, wie nennt man eigentlich das hier?
Delphi-Quellcode:
type
TInner = record Value: Variant; end; TOuter = record Inner: TInner; _____ property Value: Variant read Inner.Value write Inner.Value; end; Ich wollte mal im QS schauen/fragen, ob die das mal erweitern könnten. (so bis spätestens nächstes Jahrtausend)
Delphi-Quellcode:
Und schön wäre es auch, wenn es ebenfalls mit Record-Pointern und Klassen funktionieren würde.
type
TInner = record Value: Variant; procedure SetValue(const Value: Variant); end; TOuter = record Inner: TInner; ________ property Value: Variant read Inner.Value write Inner.SetValue; end;
Delphi-Quellcode:
type
PInner = ^TInner; TInner = record Value: Variant; procedure SetValue(const Value: Variant); end; TInnerClass = class Value: Variant; procedure SetValue(const Value: Variant); end; TOuter = record InnerR: TInner; InnerP: PInner; InnerC: TInnerClass; property Value: Variant read InnerR.Value write InnerR.SetValue; property Value: Variant read InnerP.Value write InnerP.SetValue; property Value: Variant read InnerC.Value write InnerC.SetValue; end; |
AW: into Record?
Das mit dem
Delphi-Quellcode:
ist etwas schwierig: Welchen Self-Parameter soll denn dann der Setter bekommen?
write Inner.SetValue
|
AW: into Record?
Nja, als Self kann eigentlich nur Inner rein gehn.
Also dort, wo der Setter aufgerufen wird, wäre es kein Problem jeweils den Offset auf die Variable draufzurechnen. Bei Records funktioniert ja leider das IS nicht, so dass man da nicht auf TOuter prüfen kann. :angle: Aber ja, schön wäre es, wenn man hier auch Outer im Setter hätte, aber dieses Problem lässt sich notfalls anders lösen. z.B. Index als Erkennung, von wem es kommt. Oder im TInner ein Markierungs-Feld, was über die Custom-Managed-Record-Methoden gesetzt wird. Weil mit Pointern vor oder hinter TInner zuzugreifen und zu hoffen da könnte TOuter (oder sonstwas) sein, würde bestimmt gehen, aber wäre nicht wirklich sicher. |
AW: into Record?
Zitat:
|
AW: into Record?
Ja?
Mir fallen hierfür noch ein paar mehr "Sonderfälle" ein und irgendwann wird es dann normal :stupid: Rein technisch sollten es im Compiler nicht so große Änderungen sein. * hier ein +FieldOffset bei den Aufrufen * und für Pointer und Classen zusätzlich noch eine Referenz auflösen Und beim Parser/LSP muß nur der eine Punkt beachtet werden, wobei das ja schon gemacht wird, beim Zugriff auf das Feld. Ich hatte schon mehrmals den Wunsch nach sowas. Aktuell hätte ich zwei Records, welche nach außen genau gleich aufgebaut sind. Am Liebsten wäre es mir, es wäre nur ein Record, aber sie unterscheiden sich im Verhalten, wenn man komplette Records einander zuweist. * einmal wird nur der Wert übernommen und zurückgeschrieben * und das andere Mal wird Wert+Name übernommen jenachdem von wo die Records kommen * als Result aus einem Getter * oder es ist eine Variable Also einmal TResult oder TVariable, die aber von den Schnittstellen her komplett identisch sind. Aktuell tendiere ich mehr in Richtung Generic, mit zwei Ableitungen, nur um es namentlich zu unterscheiden. [edit] ohhh, jetzt wo ich nochmal drüber nachdenke ... eventuell geht auch
Delphi-Quellcode:
.
type
Delphi-Quellcode:
Aber ich glaube noch nicht wirklich, dass ich da im Setter etwas vom B mitbekomm. :(
type
A = record procedure Test; end; B = type A; Eigentlich wäre eine gute Lösung auch, wenn Emba die Getter/Setter reparieren würde. Denn das ist der Grund, warum ich hier jetzt das "eigentliche" Problem habe.
Delphi-Quellcode:
type
TFuu = record {or class} FBar: TPoint; procedure SetBar(Value: TPoint) propery Bar: TPoint read FBar write SetBar; end; Fuu.Bar.X := 123; // wird ja intern als Temp := Fuu.FBar; // Temp := Fuu.GetBar; Temp.X := 123; // dann wird Temp entsorgt und die Zuweisung ist weg // besser wäre es, würde der Compiler daraus das machen Temp := Fuu.Bar; // Temp := Fuu.GetBar; Temp.X := 123; Fuu.SetBar(Temp); |
AW: into Record?
Ich hab da mal bissl rumgespielt.
Einmal hätte ich ja das Problem, dass der Record sich unterschiedlich verhalten soll, jenachdem von wo er kommt -> Property oder Variable. Und wenn ich zwei Records habe, dass sie aber dennoch zuweisungskompatibel sind. Kein Problem mit den ClassOperatoren, aber mit den Generics hatte ich sowas noch nie mit sich selbst probiert. Versuch 1 (Idee siehe vorheriger Post) lief schief, da ich von SELF keinen Typ bekomme (falls der überhaupt sich unterscheiden hätte, was ich kaum glaube). Dabei wäre Dieses die einfachste/übersichtlichste Lösung geworden. :cry:
Delphi-Quellcode:
Versuch 2 klappte dann erstmal.
uses
TypInfo; type TFuu = record procedure Test; end; TBar = type TFuu; procedure TForm11.FormCreate(Sender: TObject); var A: TBar; begin A.Test; end; procedure TFuu.Test; begin ShowMessage(PTypeInfo(TypeInfo(Self)).Name); // geht nicht end; Zwar wieder kein Typ von Self, aber dafür von <T>. Aber dennoch nicht funktional, da diese Typen sich nicht einander zuweisen lassen.
Delphi-Quellcode:
Versuch 3 ... naja
uses
TypInfo; type TFuu<T> = record procedure Test; end; TFuu = TFuu<Byte>; TBar = TFuu<Word>; procedure TForm11.FormCreate(Sender: TObject); var A: TBar; begin A.Test; end; procedure TFuu<T>.Test; begin //ShowMessage(PTypeInfo(TypeInfo(Self)).Name); // geht auch nicht ShowMessage(PTypeInfo(TypeInfo(T)).Name); end;
Delphi-Quellcode:
[dcc32 Fehler] E2521 Operator 'Implicit' muss einen 'TFuu<X>'-Typ im Parameter oder Ergebnistyp übernehmen
type
TFuu<X> = record procedure Test; class operator Implicit(const A: TFuu<Word>): TFuu<Byte>; class operator Implicit(const A: TFuu<Byte>): TFuu<Word>; end;
Delphi-Quellcode:
Also genau was der Compiler nun wollte, aber dennoch mag er es nun nicht, wegen der Nicht-Eindeutigkeit.
type
TFuu<X> = record procedure Test; class operator Implicit(const A: TFuu<X>): TFuu<Byte>; class operator Implicit(const A: TFuu<X>): TFuu<Word>; class operator Implicit(const A: TFuu<Word>): TFuu<X>; class operator Implicit(const A: TFuu<Byte>): TFuu<X>; end; Erstes und Letztes sind identisch und verweisen auch noch jeweils auf sich selber.
Delphi-Quellcode:
// z.B. X = Byte
class operator Implicit(const A: TFuu<Byte>): TFuu<Byte>; class operator Implicit(const A: TFuu<Byte>): TFuu<Word>; class operator Implicit(const A: TFuu<Word>): TFuu<Byte>; class operator Implicit(const A: TFuu<Byte>): TFuu<Byte>; Dann, bei der 4, wurde es kompliziert und fast unübersichtlich (hatte mehrmals 'nen Knoten im Hirn und dachte es sei andersrum), ABER es scheint zu gehn. :freak:
Delphi-Quellcode:
Früher war mir so, als wenn Temp-Variablen für Zwischenergebnisse als lokale Funktionsvariablen (von begin bis end) erstellt würden.
uses
TypInfo; type TFuu<X,Y> = record Value: string; procedure Test; class operator Implicit(const A: TFuu<X,Y>): TFuu<Y,X>; class operator Implicit(const A: TFuu<Y,X>): TFuu<X,Y>; class operator Initialize(out Dest: TFuu<X,Y>); class operator Finalize (var Dest: TFuu<X,Y>); class operator Assign (var Dest: TFuu<X,Y>; const [ref] Src: TFuu<X,Y>); end; TFuu = TFuu<Byte,Word>; TBar = TFuu<Word,Byte>; procedure TForm11.FormCreate(Sender: TObject); var F: TFuu; Q: TFuu; B: TBar; begin // create TFuu(F), create TFuu(Q), create TBar(B) F.Test; // test TFuu(F) B.Test; // test TBar(B) F.Value := 'Test'; // -setValue TFuu(F)- Q := F; // assign TFuu(F)->TFuu(Q) B := F; // create TBar(Temp), toMe TFuu(F)->TBar(Temp), assign TBar(Temp)->TBar(B), free TBar(Temp) if B.Value = 'Test' then ; // -getValue TFuu(B)- end; // free TBar(B), free TFuu(Q), free TFuu(F) procedure TFuu<X,Y>.Test; begin //ShowMessage(PTypeInfo(TypeInfo(X)).Name + ' ' + PTypeInfo(TypeInfo(Y)).Name); if TypeInfo(X) = TypeInfo(Byte) then ShowMessage('test TFuu') else ShowMessage('test TBar'); end; class operator TFuu<X,Y>.Assign(var Dest: TFuu<X,Y>; const [ref] Src: TFuu<X,Y>); begin if TypeInfo(X) = TypeInfo(Byte) then ShowMessage('assign TFuu->TFuu') else ShowMessage('assign TBar->TBar'); Dest.Value := Src.Value; end; class operator TFuu<X,Y>.Finalize(var Dest: TFuu<X,Y>); begin if TypeInfo(X) = TypeInfo(Byte) then ShowMessage('free TFuu') else ShowMessage('free TBar'); Dest.Value := ''; end; class operator TFuu<X,Y>.Initialize(out Dest: TFuu<X,Y>); begin if TypeInfo(X) = TypeInfo(Byte) then ShowMessage('create TFuu') else ShowMessage('create TBar'); Dest.Value := 'empty'; end; class operator TFuu<X,Y>.Implicit(const A: TFuu<Y,X>): TFuu<X,Y>; begin //ShowMessage(PTypeInfo(TypeInfo(Y)).Name + ' ' + PTypeInfo(TypeInfo(X)).Name // + ' -> ' + PTypeInfo(TypeInfo(X)).Name + ' ' + PTypeInfo(TypeInfo(Y)).Name); if TypeInfo(X) = TypeInfo(Byte) then ShowMessage('toMe TBar->TFuu') else ShowMessage('toMe TFuu->TBar'); //Result := TFuu<X,Y>(A); Result.Value := A.Value; end; class operator TFuu<X,Y>.Implicit(const A: TFuu<X,Y>): TFuu<Y,X>; begin //ShowMessage(PTypeInfo(TypeInfo(X)).Name + ' ' + PTypeInfo(TypeInfo(Y)).Name // + ' -> ' + PTypeInfo(TypeInfo(Y)).Name + ' ' + PTypeInfo(TypeInfo(X)).Name); if TypeInfo(X) = TypeInfo(Byte) then ShowMessage('fromMe TFuu->TBar') else ShowMessage('fromMe TBar->TFuu'); //TFuu<X,Y>(Result) := A; // [dcc32 Fehler] E2064 Der linken Seite kann nichts zugewiesen werden //Result := TFuu<Y,X>(A); Result.Value := A.Value; end; Jetzt scheint das wirklich nur dort zu sein, wo es ist ... vielleicht haben sie nun die neuen inline-Variablen hier selbst benutzt. :stupid: |
AW: into Record?
Zitat:
![]() Bis bald... Thomas |
AW: into Record?
Jo, also doch richtig gesehn.
Ist ja blöd, denn das alte Verhalten hatte in einem Fall einen Vorteil. Für einfache Logging-Funktionen, also zu Beginn ein Interface erstellen und bei Funktionsende wird es freigegeben, also auch nochmal das Ende automatisch loggen können. |
AW: into Record?
Zitat:
In dem verlinkten Beispiel wird das Interface aber im Scope eines inneren begin-end erstellt. Das funktioniert jetzt anders als vorher. |
AW: into Record?
ahhh OK.
jetzt noch probieren, ob es auch in einem IF-THEN funktioniert, aber theoretisch wäre das doch auch in einem anderen Scope. :duck: Nja, ansonsten wäre noch die Überlegung für eine Attribut an Variable oder an einem Function-Result, so wie bei [Weak] und Co. Aktuell haben wir für Logging und Exception-Beahndlung am Ende eine Kennung ... die reicht, um in der bis nach oben durchgewanderten Exception noch paar Infos mit anzuzeigen, aber will man mittendrin mit Try-Except das abfangen und die Exception anzeigen, dann fehlt das natürlich. Drum wäre es besser das schon zu Beginn anf 'nen Stack zu schieben und am Ende automatisch entfernen zu lassen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:22 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz