Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Zeigertypen ändern


Gewuerzgurke - Mi 01.07.09 18:17
Titel: Zeigertypen ändern
Hallo, ich suche nach einer Möglichkeit, den Typen eines Pointers zu ändern. Mein Problem: Ich habe zwei verschiedene Records. Jedes Record enhält eine (keine Ahnung, wie es in Records heist) "Variable" Transparency. Zu jedem Record habe ich auch den passenden Pointer. Also:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
type
 PRec1 = ^TRec1;
 TRec1 = record
  Transparency : Cardinal;
  AndereVariablen : sonstwas;
 end;
 PRec2 = ^TRec2;
 TRec2 = record
  Transparency : Cardinal;
  GanzAndereVariablen : irgendwas;
 end;

var
 Rec1 : TRec1;
 Rec2 : TRec2;


In meiner Funktion möchte ich die "Variable" Transparency über einen Poiner erreichen. Wenn ich den Pointer einrichte als PRec1 (also var DerPointer : PRec1;) dann kann ich damit die Variable in Rec2 nicht erreichen (außer Tranzparency steht - wie im Beispiel - in beiden Records an der selben Position, was ich aber in meinem Programm aus verschiedenen Gründen nicht verwirklichen kann...). Ich möchte wissen, ob man den Pointer zur Laufzeit von PRec1 in PRec2 ändern kann - oder ob es eine andere Lösung gibt.
Danke schonmal.


Boldar - Mi 01.07.09 18:27

Schreib dir doch die Records wie folgt:


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:
type
 PRec1 = ^TRec1;
 TRec1 = record
  function gettransparency: Pointer;
  Transparency : Cardinal;
  AndereVariablen : sonstwas;
 end;
 PRec2 = ^TRec2;
 TRec2 = record
  function gettransparency: pointer;
  Transparency : Cardinal;
  GanzAndereVariablen : irgendwas;
 end;

var
 Rec1 : TRec1;
 Rec2 : TRec2;


...
function TRec1.gettransparency: pointer;
begin
  result := addr(Transparency);
end;


function TRec2.gettransparencypointer: pointer;
begin
  result := addr(Transparency);
end;



Die getter-Funktionen schreibst du dann an die selbe Stelle und ruftst den Wert Dann darüber ab.
Oder es geht villeicht auch mit einem explizitem Typecast, denn du weisst doch, welcher Record das ist?
mfg Boldar


Narses - Mi 01.07.09 18:30

Moin!

Wozu gibt´s OOP?! :nixweiss: Ich würde da eine(n Satz) Klasse(n) draus machen. ;) Das Pointer-Gehake ist doch eher unschön... :?

cu
Narses


Gewuerzgurke - Mi 01.07.09 18:57

Ja, ich habe auch immer versucht, Pointer zu vermeiden, aber dieses Mal geht es nicht anders... außer ich mache riesige "IF-Tabellen". OK, ich könnte auf die Variante mit "function gettransparency: Pointer;" zurückgreifen, aber das währe auch ziemlich aufwändig, wegen den Klassen. Ich hatte gehofft, dass es eine Möglichkeit gibt, nach dem Motto "Wenn Ziel_Von_Pointer = Rec2, dann Position_Von_Transparency = 1", was genau dem Problem mit dem ändern des Typen gleichkäme. Aber es muss doch eine Möglichkeit geben, die Variablen-Adressen abhängig von dem Typ des Records zu bekommen...


Boldar - Mi 01.07.09 19:02

Meine oben genannte Methode arbeitet nicht mit Klassen, man muss es nicht uf Klassen umstellen, es muss nur eine Methode hinzugefügt werden.


Narses - Mi 01.07.09 19:03

Moin!

user profile iconGewuerzgurke hat folgendes geschrieben Zum zitierten Posting springen:
Ja, ich habe auch immer versucht, Pointer zu vermeiden, aber dieses Mal geht es nicht anders... außer ich mache riesige "IF-Tabellen". OK, ich könnte auf die Variante mit "function gettransparency: Pointer;" zurückgreifen, aber das währe auch ziemlich aufwändig, wegen den Klassen.
:gruebel:

Du kannst die records auch kaskadieren:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
type
  TDataRec1 = record
    //...
  end;

  TDataRec2 = record
    //...
  end;

  TCompositeRec1 = record
    Transparency: Cardinal;
    DataRec1: TDataRec1;
  end;

  TCompositeRec2 = record
    Transparency: Cardinal;
    DataRec2: TDataRec2;
  end;
Dann kannst du aber auch gleich eine Klasse nehmen, das ist definitiv besser. :nixweiss:

Vielleicht erklärst du mal kurz, was das werden soll, gibt ja vielleicht eine optimalere Alternative. :idea: ;)

cu
Narses


jaenicke - Mi 01.07.09 19:09

Mir kämen da noch variante Records in den Sinn.
http://www.delphi-treff.de/sprachen/object-pascal/komplexe-datentypen/ hat folgendes geschrieben:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
type
  person = record
    name: string;
    case erwachsen: boolean of
      true: (personalausweisnr: integer);
      false: (kinderausweisnr: integer;
        erziehungsberechtigte: string);
  end;
Ich nehme aber auch an, dass es eine bessere Lösung gibt...


Gewuerzgurke - Mi 01.07.09 19:41

Also was ich machen möchte ist eine Animation (DirectX) mit verschiedenen Objekten. Es giebt verschiedene Sorten von Objekten. Für jede Sorte Objekt ist ein Array da, indem die Objekte "stehen" (Also var Sorte1 : Array of TSorte1;). Um ein Objekt zu verändern, z.B. ausblenden, giebt es eine procedure, die den Wert Transparency verändert. So ziemlich alle Objekte haben das Attribut Transparency, weswegen der Effekt Ausblenden auch auf alle Objekte anwendbar wäre. Aber dafür muss unterschieden werden, zu welcher Sorte das Objekt gehört. Mit einem Pointer könnte ich es mir ersparen, da große "IF-Tabellen" anzulegen. Aber dann habe ich das Problem mit dem Typen des Pointers. Ich glaube irgendwo gelesenzuhaben, dass man diesen Typen ändern kann, aber ganz so einfach ist es wohl nicht...

In diesem Fall würde es wirklich Sinn machen, darauf zu achten, dass Transparency immer an der selben Position im Record steht, aber das währe nicht so "sauber" - oder?


jaenicke - Mi 01.07.09 20:11

Du kannst die Pointer auch casten.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var
  a: TRec1;
  b: PRec1;
begin
  b := @a;
  PRec2(b).Transparency ...
Oder du kannst auch direkt die Speicherstelle benutzen. Allerdings solltest du dann einen packed record nehmen, damit die Variablen direkt hintereinander im Speicher liegen.


Boldar - Mi 01.07.09 22:20

Für sowas kann man doch sehr gut Klassen nehmen, die sich dann selbst verwalten und evtl. selber zeichnen.


jaenicke - Mi 01.07.09 22:23

Ja, sicher, sinnvoller wäre das mit Klassen wohl, wurde ja auch schon gesagt.

Insbesondere, weil man dann allgemeine Sachen in Elternklassen legen könnte und ggf. auch noch mit Interfaces arbeiten könnte. So könnte man das dann alles gleichzeitig erschlagen.


Reinhard Kern - Mi 01.07.09 23:54

user profile iconGewuerzgurke hat folgendes geschrieben Zum zitierten Posting springen:
Hallo, ich suche nach einer Möglichkeit, den Typen eines Pointers zu ändern.
....


Warum eigentlich, nimm doch einen Pointer direkt auf Transparency (eine Zuweisung an den Pointer brauchst du ja so oder so):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
type
 PTransparency = PCardinal;
 PRec1 = ^TRec1;
 TRec1 = record
  Transparency : Cardinal;
  AndereVariablen : sonstwas;
 end;
 PRec2 = ^TRec2;
 TRec2 = record
  Transparency : Cardinal;
  GanzAndereVariablen : irgendwas;
 end;

var
 PTrans : PTransparency;
 Rec1 : TRec1;
 Rec2 : TRec2;

begin
 PTrans := @Rec1.Transparency;
 PTrans := @Rec2.Transparency;


getestet hab ich das NICHT!

Gruss Reinhard


jaenicke - Mi 01.07.09 23:59

Ja, das geht schon so denke ich. Man kann auch noch viel mehr Pointerspielerei hineinbringen. Z.B. um von einem allgemeinen Pointer auf einen der Records auszugehen kann man den auch auf PCardinal casten um auf den Cardinalwert am Anfang zuzugreifen.
SizeOf(Cardinal) = 4 Bytes später kommt dann die nächste Variable, ...

Aber das macht das alles natürlich nicht übersichtlicher. ;-)


BenBE - Do 02.07.09 08:11

Je nachdem, wieviele verschiedene Möglichkeiten es gibt, kann entweder die Variante mit dem Varianten Record ODER die Lösung über entsprechende Klassen übersichtlicher sein.

Keinesfalls sollte man aber unnötig Pointer-Casting betreiben, da das die Sache nicht übersichtlicher macht und eher Fehler provoziert.

Insgesamt wäre es aber durchaus mal sinnvoll, noch etwas mehr Quelltext zu bekommen, wie mit der Transparenz gearbeitet werden soll, da das so eher Spekulation ist ...


Gewuerzgurke - Do 02.07.09 15:41

@Reinhard Kern ( <-- Ist jetzt kein Pointer :mrgreen: ) - Hab' ich versucht - geht nicht. Es kommt zwar kein Fehler, aber es passiert auch nichts. Schade, denn das währe echt einfach gewesen. :(
Wenn ich den Pointer (Wie von jaenicke vorgeschlagen) caste (als PCardinal), dann funktioniert es zumindest dann, wenn Transparency als erstes im Record steht. Gibt es eine Möglichkeit, das Ganze nach hinten zu verschieben, soll heißen die Variablen, die vor Transparency kommen wieder "herauscasten"? Ich denk' mal, dafür muss ich dann packed record verwenden.

Wehe, wenn ich dann nochmal das Reckord verändere, aber ich gehe nicht davon aus, dass dies nötig sein wird.


jaenicke - Do 02.07.09 15:58

user profile iconGewuerzgurke hat folgendes geschrieben Zum zitierten Posting springen:
@Reinhard Kern ( <-- Ist jetzt kein Pointer :mrgreen: ) - Hab' ich versucht - geht nicht.
Ich habe es getestet, das klappt bei mir problemlos. :gruebel:

user profile iconGewuerzgurke hat folgendes geschrieben Zum zitierten Posting springen:
Wenn ich den Pointer (Wie von jaenicke vorgeschlagen) caste (als PCardinal), dann funktioniert es zumindest dann, wenn Transparency als erstes im Record steht. Gibt es eine Möglichkeit, das Ganze nach hinten zu verschieben, soll heißen die Variablen, die vor Transparency kommen wieder "herauscasten"?
Wie ich schon sagte: Einfach den Pointer verschieben. Wenn davor ein Integerwert (4 Byte) steht so ca.:

Delphi-Quelltext
1:
PTrans := PTransparency(Integer(@Rec1) + 4);                    


Gewuerzgurke - Do 02.07.09 16:32

Oh, nett, dass du es noch ausprobiert hast. Da muss ich wohl irgendwo einen Fehler gemacht haben. Ich probiere nochmal 'rum. :lupe:


Gewuerzgurke - So 05.07.09 19:24

So, geht! :D

Mein Fehler war, dass ich versehendlich den Pointer, bevor dieser verwendet wird, nocheinmal zugewiesen habe (PTrans := @Rec1;). Da kann man vorher soviel richtig machen, wie man will :autsch: ...sowas blödes aber auch.