Autor Beitrag
Zudomon
Hält's aus hier
Beiträge: 10



BeitragVerfasst: Mo 22.06.09 12:44 
Hallo euch allen! :D
Ich wollte euch fragen, ob ihr vielleicht eine bessere Lösung für folgenden Sachverhalt kennt:
Es werden mehrere Klassen von einer Haupt bzw. deren Unterklassen abgeleitet und ich wollte diese nun speichern und laden. Dafür habe ich einen ENUM erstellt, und über 2 Funktionen wird entweder der ENUM zu einer Klasse ermittelt oder die Klasse anhand des ENUMs erstellt.

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
  ENUM_GEOMETRIE = (
    G_GEOMETRY                 =  0,
    G_MODIFIERLAYER            =  1,
    G_MODIFIERMIRROR           =  2,
    G_MODIFIERCOMPOSITION      =  3,
    G_MODIFIERTRANSFORM        =  4,
    G_MODIFIERSUBDIVIDE        =  5,
    G_MODIFIERUVW              =  6,
    G_MODIFIERTEMPORARYLAYER   =  7,
    G_MODIFIERNOISE            =  8,
    G_MODIFIERSMOOTHNORMALS    = 10,
    G_MODIFIERTRANSFORMTOSPACE = 11,
    G_MESHHOLDER               = 13,
    G_MESHBEZIER               = 14,
    G_MODIFIERTRIANGULATE      = 15,
    G_MODIFIERWELD             = 16,
    G_MODIFIERSMOOTH           = 17,
    G_MODIFIERDELUNUSED        = 18,
    G_MODIFIERSCATTER          = 19,
    G_GEOMETRYBASE             = 20,
    G_GEOMETRYPLANE            = 21,
    G_GEOMETRYBOX              = 22
  );

function GeometryFactory(_GeoType: ENUM_GEOMETRIE): TGeometry;
begin
  Result:=nil;
  case _GeoType of
    G_GEOMETRY:                 Result:=TGeometry.Create;
    G_MODIFIERLAYER:            Result:=TModifierLayer.Create;
    G_MODIFIERMIRROR:           Result:=TModifierMirror.Create;
    G_MODIFIERCOMPOSITION:      Result:=TModifierComposition.Create;
    G_MODIFIERTRANSFORM:        Result:=TModifierTransform.Create;
    G_MODIFIERSUBDIVIDE:        Result:=TModifierSubdivide.Create;
    G_MODIFIERUVW:              Result:=TModifierUVW.Create;
    G_MODIFIERTEMPORARYLAYER:   Result:=TModifierTemporaryLayer.Create;
    G_MODIFIERNOISE:            Result:=TModifierNoise.Create;
    G_MODIFIERSMOOTHNORMALS:    Result:=TModifierSmoothnormals.Create;
    G_MODIFIERTRANSFORMTOSPACE: Result:=TModifierTransformToSpace.Create;
    G_MESHHOLDER:               Result:=TMeshholder.Create;
    G_MESHBEZIER:               Result:=TMeshBezier.Create;
    G_MODIFIERTRIANGULATE:      Result:=TModifierTriangulate.Create;
    G_MODIFIERWELD:             Result:=TModifierWeld.Create;
    G_MODIFIERSMOOTH:           Result:=TModifierSmooth.Create;
    G_MODIFIERDELUNUSED:        Result:=TModifierDelUnused.Create;
    G_MODIFIERSCATTER:          Result:=TModifierScatter.Create;
    G_GEOMETRYBASE:             Result:=TGeometryBase.Create;
    G_GEOMETRYPLANE:            Result:=TGeoPlane.Create;
    G_GEOMETRYBOX:              Result:=TGeoBox.Create;
  end;
end;

function GetTypeOf(_g: TGeometry): ENUM_GEOMETRIE;
begin
  Result:=G_GEOMETRY;
  if _g is TMeshholder then Result:=G_MESHHOLDER;  
  if _g is TModifierLayer then Result:=G_MODIFIERLAYER;
  if _g is TModifierMirror then Result:=G_MODIFIERMIRROR;
  if _g is TModifierComposition then Result:=G_MODIFIERCOMPOSITION;
  if _g is TModifierTransform then Result:=G_MODIFIERTRANSFORM;
  if _g is TModifierSubdivide then Result:=G_MODIFIERSUBDIVIDE;
  if _g is TModifierTemporaryLayer then Result:=G_MODIFIERTEMPORARYLAYER;
  if _g is TModifierNoise then Result:=G_MODIFIERNOISE;
  if _g is TModifierSmoothnormals then Result:=G_MODIFIERSMOOTHNORMALS;
  if _g is TModifierTransformToSpace then Result:=G_MODIFIERTRANSFORMTOSPACE;
  if _g is TMeshBezier then Result:=G_MESHBEZIER;
  if _g is TModifierTriangulate then Result:=G_MODIFIERTRIANGULATE;
  if _g is TModifierWeld then Result:=G_MODIFIERWELD;
  if _g is TModifierSmooth then Result:=G_MODIFIERSMOOTH;
  if _g is TModifierDelUnused then Result:=G_MODIFIERDELUNUSED;
  if _g is TModifierScatter then Result:=G_MODIFIERSCATTER;
  if _g is TGeometryBase then Result:=G_GEOMETRYBASE;
  if _g is TModifierUVW then Result:=G_MODIFIERUVW;
  if _g is TGeoPlane then Result:=G_GEOMETRYPLANE;
  if _g is TGeoBox then Result:=G_GEOMETRYBOX;
end;


Das blöde an der ganzen Sache ist, jede Klasse muss da extra eingetragen werden. Gibt es einen Weg, das zu automatisieren?

Ich benutze Turbo Delphi. :wink:

Gruß
Zudomon
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mo 22.06.09 13:17 
Erstmal: Wofür brauchst du das? Würde mich mal interessieren ^^

Dann: Zur zweiten Funktion ginge es, dass jede Klasse eine Funktion anbietet, die den passenden Enum-Wert zurückgibt.

Besser wäre aber wahrscheinlich eine seperate Klasse, die die anderen verwaltet. Da könnte sich dann jede Klasse registrieren und würde eine interne Nummer bekommen - diese kannst du dann statt dem Enum verwenden. Die Zuprdnung Nummer<>Klasse sollte natürlich eineindeutig sein.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mo 22.06.09 14:15 
Erstell einen Typ

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
type TEnumClassLookupFoo = array[TDeinEnumBarTyp] of class of TDeineBasisClassBaz = (
    ENUM_FOO: TFooEnumClass;
    ENUM_BAR: TBarEnumClass;
    //...
);


Lookup der zugehörigen Klasse geht dann mit einem einfachen Lookup im Array, für die andere Richtung kannst Du einfach dein Datenarray durchsuchen.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
Zudomon Threadstarter
Hält's aus hier
Beiträge: 10



BeitragVerfasst: Mo 22.06.09 14:24 
Danke für die schnelle Antwort :)

@jfheins
Öh, ich brauche das für einen 3D-Editor, der Geometrie erstellen kann. Auf diese sollen dann verschiedene Modifikatoren aufgetragen werden.

Das hört sich an sich ja nicht schlecht an, allerdings hat man dann ja letztendlich noch mehr Aufwand.

@BenBE
Wäre das auch irgendwie ohne ENUMs möglich?


Ideal wäre, wenn man statt des ENUMs vielleicht direkt den Namen der Klasse speichern könnte und wenn es möglich wäre, diesen String zu laden. Wenn die Variable, in die dann der Name wieder eingelesen wird z.B. str heißen würde und man dann sagen könnte str.create, dann könnte man das alles ehrheblich automatisieren.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mo 22.06.09 14:27 
Jede Klasse hat ein Attribut ClassName sowie ein weiteres ClassType. Im Endeffekt reicht es, wenn Du dir eine Funktion zum Registrieren der gewünschten Klassen in einer Liste baust und dann basierend auf dem ClassType alles weitere ausliest. Siehe ggf. auch zum Thema RTTI.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mo 22.06.09 15:20 
Es gibt übrigend eine TClassList, in der du deine Klassen speichern kannst ;)
Zudomon Threadstarter
Hält's aus hier
Beiträge: 10



BeitragVerfasst: Mo 22.06.09 15:50 
Ich danke euch nochmal für die Hilfe!

Also hab jetzt erstmal versucht eine Klasse zu registrieren, um die dann hinterher über FindClass zu finden, daran bin ich aber gescheitert, weil meine Klasse nicht von TPersistent abgeleitet war. Wollte das dann ableiten, aber das ging auch nicht, weil Delphi dann wegen meinen Membervariablen meckert, die müssten vom Typ class oder interface sein.

Mit TClassList scheint das ganze aber zu klappen. Kannte ich vorher nicht. Hab das auch noch nicht vollkommen implementiert nur mal geschaut, ob ich da die Klasse rein bekomme und die hinterher wieder finden kann um dann eine Instanz davon zu erzeugen. Also für mich ist das nun erstmal die beste Lösung. :D

Edit: Zu früh gefreut. Wenn ich eine Instanz über
ausblenden Delphi-Quelltext
1:
Result:=GeometryClass.Items[i].create as TGeometry;					

erzeugen möchte, dann klappt das zwar, aber es wird nicht der richtige Konstruktor aufgerufen, sondern nur der von TObject.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Mo 22.06.09 17:22 
ausblenden Delphi-Quelltext
1:
2:
3:
type TGeometryClass = class of TGeometry;
{...}
Result:=TGeometryClass(GeometryClass.Items[i]).create;


Das hängt mit der Vererbung zusammen: TObject.Create ist statisch deklariert und nicht, wie es bei abgeleiteten Konstruktoreen üblich ist virtual mit reintroduce. Daher sucht Delphi bei einem TObject (und nichts weitere ist TClass vereinfacht ausgedrückt) nicht nach dem eigentlichen Konstruktor. Wenn man jedoch explizit auf eine abgeleitete Class castet und dann von dieser den Konstruktor aufruft sieht der Compiler, dass er hier eine virtuelle Methode aufrufen muss und findet somit den richtigen Konstruktor.

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.