Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Zirkuläre Unit Reference (https://www.delphipraxis.net/81293-zirkulaere-unit-reference.html)

Koolance 24. Nov 2006 08:50


Zirkuläre Unit Reference
 
Hi,
ich schreibe gerade einen Quellcodegenerator der aus einer Datenbank eine Mittelschicht erzeugt (oder besser die Grundlage dafür).
Für jede Klasse wird eine eigene Unit erzeugt.

Beispiel für mein Problem.

Tabellen
Adresse------<Matchcode
Eine Adresse hat N Matchcodes. Ein Matchcode aber immer nur eine Adresse.

Lösung für die Zwischenschicht
Adresse.Liste_Matchcodes.Matchcode (in Adresse wird in der uses Liste_Matchcodes verwendet, in der uses von List_Matchodes Matchcode)
Matchcode.Adresse (in Matchcode wird in der uses Adresse Verwendet)

Probleme
Ich will die oben beschrieben Funktionalität, aber nicht alle Klassen in eine Unit schreiben.
Die Uses müssen im Interface-Bereich stehen da ich die Objekte als Klassenvariablen verwende.

Lösungsansätze

http://www.delphipraxis.net/internal...rkul%E4re+unit
Zitat:

Zudem kann man die Typdeklaration in eine Dritte Unit auslagern, die dann beide nutzen.
Habe mir eine Unit declarations erstellt alles in sie eingebunden und sie in meinen Klassen verwendet, das klappt aber überhaupt nicht.


http://www.delphipraxis.net/internal...rkul%E4re+unit
Zitat:

bei einem von beiden muss die Uses-Klausel dann in den implementation-Teil...allerdings kannst du dann die Klassendeklaration nicht mehr verwenden

ich glaube du kannst eine Art "Vordeklaration" machen, indem nur nur bekanntgibst, das es so eine Klasse gibt (type Elternklasse = class;) ohne eine richtige Deklaration, und dann die wirkliche Deklaration erst im implementation-Teil via uses includes....

bin mir aber nicht sicher...
Klingt gut, habe aber leider nichts dazu gefunden.

mkinzler 24. Nov 2006 08:53

Re: Zirkuläre Unit Reference
 
Zitat:

Klingt gut, habe aber leider nichts dazu gefunden.
Wurde doch beschrieben: Einfach

Delphi-Quellcode:
type TKlassenNamen = class;
und später richtig deklarieren.

Koolance 24. Nov 2006 09:06

Re: Zirkuläre Unit Reference
 
So einfach das klingt ich mache es wohl falsch.
Beispiele

Delphi-Quellcode:
unit ADDRESS;

interface
uses
  Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser,
  datastructs;

type list_ADD_MATCHCODE = class;
type


//***********************
//****Kopf Basisklasse***
//***********************
  TADDRESS = class(TBase)

....

implementation
uses list_ADD_MATCHCODE;
Fehlermeldung: Typ list_ADD_MATCHCODE ist nicht vollständig definiert.

Delphi-Quellcode:
unit ADDRESS;

interface
uses
  Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser,
  datastructs;


type
type list_ADD_MATCHCODE = class;

//***********************
//****Kopf Basisklasse***
//***********************
  TADDRESS = class(TBase)
....

implementation
uses list_ADD_MATCHCODE;
Fehlermeldungen:
Bezeichner erwartet aber Type gefunden.
Typ list_ADD_MATCHCODE ist nicht vollständig definiert.

[edit=SirThornberry]Zitat-Tags durch Delphi-Tags ersetzt - Mfg, SirThornberry[/edit]

SirThornberry 24. Nov 2006 09:09

Re: Zirkuläre Unit Reference
 
Heißt deine Unit genauso wie der Typ? :gruebel:

Verwendet TAddress die Klasse list_ADD_MATCHCODE oder warum muss list_ADD_MATCHCODE vorher definiert werden?
Ich glaub mit einer dritten Unit wäre das lösbar.

Koolance 24. Nov 2006 09:13

Re: Zirkuläre Unit Reference
 
Delphi-Quellcode:
unit ADDRESS;

interface
uses
  Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser,
  datastructs;


type
Tlist_ADD_MATCHCODE = class;

//***********************
//****Kopf Basisklasse***
//***********************
  TADDRESS = class(TBase)
....

implementation
uses list_ADD_MATCHCODE;
Klappt auch nicht sieht aber richtiger aus.

Unitname: list_ADD_MATCHCODE
Typname: Tlist_ADD_MATCHCODE

SirThornberry 24. Nov 2006 09:15

Re: Zirkuläre Unit Reference
 
Ja, klappen wird das so nicht. Die Declaration muss vollständig im Interfaceteil abgeschlossen werden.

Koolance 24. Nov 2006 09:20

Re: Zirkuläre Unit Reference
 
Dann wäre es ja so richtig:
Delphi-Quellcode:
unit ADDRESS;

interface
uses
  Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser,
  datastructs;

type Tlist_ADD_MATCHCODE = class;
type


//*********************** 
//****Kopf Basisklasse*** 
//*********************** 
  TADDRESS = class(TBase)
....

implementation
uses list_ADD_MATCHCODE;
Bringt aber die Fehlermeldung: Typ list_ADD_MATCHCODE ist nicht vollständig definiert.

xaromz 24. Nov 2006 09:36

Re: Zirkuläre Unit Reference
 
Hallo,

der Ansatz in Beitrag #3 war schon richtig. Du darfst aber nicht Folgendes machen:
Delphi-Quellcode:
type
  TClass1 = class;

type
  TClass2 = class(TObject)
    FClass1: TClass1;
  end;

  TClass1 = class(TObject)
    FClass2: TClass2;
  end;
sondern Folgendes:
Delphi-Quellcode:
type
  TClass1 = class;

  TClass2 = class(TObject)
    FClass1: TClass1;
  end;

  TClass1 = class(TObject)
    FClass2: TClass2;
  end;
Beachte: Es gibt nur eine Deklaration (ein "type").

Gruß
xaromz

Koolance 24. Nov 2006 09:47

Re: Zirkuläre Unit Reference
 
Das entspricht meinem Beitrag 5, funktioniert aber leider auch nicht.

Aber ich weiß jetzt warum, ich hatte bei der deklaration vergessen das alle Klassen von TBase abgeleitet sind. Habe es gerade geändert und es funktioniert.

Delphi-Quellcode:
unit ADDRESS;

interface
uses
  Classes, DBXpress, SqlExpr, db, SysUtils ,base,sqlparser,
  datastructs;


type
Tlist_ADD_MATCHCODE = class(TBASE);

//*********************** 
//****Kopf Basisklasse*** 
//*********************** 
  TADDRESS = class(TBase)
....

implementation
uses list_ADD_MATCHCODE;
Vielen Dank euch allen

Muetze1 24. Nov 2006 09:51

Re: Zirkuläre Unit Reference
 
Zitat:

Zitat von Koolance
Das entspricht meinem Beitrag 5, funktioniert aber leider auch nicht.

Hinweis: Unter Umständen kann es passieren, dass Delphi die Klassen-Forward Deklarationen nicht anerkennt, wenn die Deklaration der Klasse entgegen der Forward Deklaration mit einem neuen Type eingeleitet wurde.

Koolance 24. Nov 2006 15:08

Re: Zirkuläre Unit Reference
 
Nachdem ich zunächst dachte alle wird gut, musste ich feststellen das nur die geerbten Methoden und Eigenschaften übernommen werden :( (bei den Anderen Fehlermeldung: "Undefinierter Bezeichner")

Also habe ich zum test nicht mehr geerbt, nun kriege ich aber die Fehlermeldung: "Typ ... nicht vollständig definiert"

Die Fehlermeldung lässt sich so umgehen:
Delphi-Quellcode:
type
  TClass1 = class end;

  TClass2 = class
    FClass1: TClass1;
  end;

  TClass1 = class
    FClass2: TClass2;
  end;
Aber so sieht Delphi die Klasse leider als leere Klasse und ich kriege die Fehlermeldung: "Undefinierter Bezeichner"

Sidorion 24. Nov 2006 15:48

Re: Zirkuläre Unit Reference
 
ohne das end bei der Forward-Deklaration. Sonst denkt Delphi, die Klasse ist komplett, also so:
Delphi-Quellcode:
Type
  TFoo=Class;
 
  TBar=Class
   FFoo: TFoo;
  End;

  TFoo=Class
   FBar: TBar;
  End;

Koolance 24. Nov 2006 16:04

Re: Zirkuläre Unit Reference
 
Liste der Anhänge anzeigen (Anzahl: 1)
Das habe ich ja schon probiert. Passend zu dem Quelltext käme jetzt: Typ TFoo nicht vollständig definiert.

Ich habe ein kleines Beispiel gemacht.

xaromz 24. Nov 2006 21:50

Re: Zirkuläre Unit Reference
 
Hallo,

ich habe Dir schon in meinem letzten Beitrag geschrieben, wie es geht. In Deinem Beispielcode deklarierst Du die beiden Klassen in verschiedenen Units, bzw. schreibst in der einen Unit eine Forward-Deklaration auf eine Klasse in einer anderen Unit. Das funktioniert natürlich nicht. Wenn Du Forward-Deklaration nutzt ("type TFoo = class;"), dann musst Du diese Klasse auch komplett in dieser Unit deklarieren.

Gruß
xaromz

negaH 25. Nov 2006 08:19

Re: Zirkuläre Unit Reference
 
Präziser:

Forward Deklarationen eines Types müssen im gleichen "Type" Block erfolgen samt ihrer vollständigen Deklaration. Ein regülärer Bezeichner auf Unitebene beendet eines Type Block automatisch. Ein regulärer Unitbezeichner sind "Unit", "uses", "implementation" und "interface". Ergo: mit Delphi gehen keine Forward Deklarationen über Unitgrenzen hinweg, noch in unterschiedlichen Type-Blöcken.

Erweitere mal virtuell in Gedanken alle Sachen wie "const", "type", "implementation", "interface" nit einem sofortigem "begin end;" Block. Denn defakto existieren diese quasi im Compiler auch wenn wir sie nicht im Source schreiben müssen/dürfen. Dh. nach einem "type" stände sofort "begin end;" und dazwischen kannst du Forward Deklaration machen wie du möchtest Hauptsache du deklarierst diese Typen dann in diesem Block auch vollständig. Eine Deklaration innerhalb dieses "virtuellen" Bereiches ist nicht in anderen "Bereichen" sichtbar.

Einzigste Ausnahme speziell auf den Bezeichner "type" bezogen ist die Deklaration eines neuen eigenständigen Standarddatentyps abgeleitet von einem anderen Datentyp, also sowas:

Delphi-Quellcode:

type
//begin

   TTime = type TDateTime;
   TDate = type TDateTime;

   MyInteger = type Integer;

//end;

type
//begin

//end;
Gruß Hagen

Koolance 27. Nov 2006 14:28

Re: Zirkuläre Unit Reference
 
Liste der Anhänge anzeigen (Anzahl: 1)
Das Problem ließ sich mit noch ein bisschen externer hilfe lösen.
Es müssen zu den einzelnen Klassen Interfaces erstellt, und in einzelne Units gepackt werden.
Ich habe für ein Beispiel geschrieben und für alle Interessierten in den Anhang gepackt.

@xamroz
Mein Problem war das es mir sehr wichtig ist die Unitunterteilung aufrecht zu erhalten. Ich dachte es wäre so möglich...

xaromz 27. Nov 2006 16:01

Re: Zirkuläre Unit Reference
 
Hallo,
Zitat:

Zitat von Koolance
Das Problem ließ sich mit noch ein bisschen externer hilfe lösen.
Es müssen zu den einzelnen Klassen Interfaces erstellt, und in einzelne Units gepackt werden.
Ich habe für ein Beispiel geschrieben und für alle Interessierten in den Anhang gepackt.

Ich hoffe, Du weißt, was Du da tust. Interfaces sind eine sehr fehlerträchtige Sache. Insbesondere durch die gegenseitige Abhängigkeit der beiden Interfaces wirst Du vermutlich ein Speicherleck bekommen. Für diesen Zweck sind Interfaces

Zu Deinem Demoprogramm:
- Du castest ein Interface auf das zugrundeliegende Objekt. Das ist nicht zulässig.
- Du benutzt Objekte, ohne Instanzen zu erzeugen. Das fliegt Dir bald um die Ohren.

Gruß
xaromz

negaH 27. Nov 2006 16:22

Re: Zirkuläre Unit Reference
 
Neben den korrekten Bemerkungen von Xaromz noch als Bemerkung zusätzlich:

Das was du in deinem Testprojekt machts geht auch einfacher

Delphi-Quellcode:

unit class1;

interface

type
  TClass1 = class
  private
    FClass2: TObject;
  public
    procedure BlaBla;
  end;

implementation

uses class2;

procedure TClass1.BlaBla;
begin
  (FClass2 as TClass2).BlaBla;
end;

unit class2;

interface

type
  TClass2 = class
  private
    FClass1: TObject;
  public
    procedure BlaBla;
  end;

implementation

uses Class1;

procedure TClass2.BlaBla;
begin
  (FClass1 as TClass1).BlaBla;
end;
Gruß hagen

Koolance 27. Nov 2006 17:00

Re: Zirkuläre Unit Reference
 
Dieser Weg soll der sauberere sein, die andere Möglichkeit wäre alles von einem Basisobjektabzuleiten, dann statt dem Interface über das Basisobjekt die Typen zu deklarieren, und am ende wie bei dem Interface zu casten.

@xaromz
[qoute]Ich hoffe, Du weißt, was Du da tust. Interfaces sind eine sehr fehlerträchtige Sache. Insbesondere durch die gegenseitige Abhängigkeit der beiden Interfaces wirst Du vermutlich ein Speicherleck bekommen. Für diesen Zweck sind Interfaces [/quote]

Ich werde wie gesagt den Quellcode erzeugen, also muss ich es nur einmal richtig hin bekommen, und alle interfaces sind richtig.

Zitat:

- Du castest ein Interface auf das zugrundeliegende Objekt. Das ist nicht zulässig.
- Du benutzt Objekte, ohne Instanzen zu erzeugen. Das fliegt Dir bald um die Ohren.
Warum ist das casten unzulässig?
In meinem echten Projekt werden selbst verständlich alle Objekte instanziiert.

@NegaH
Du leitest statt von dem Interface von TObject ab, habe heute keine Zeit mehr das zu testen, werde ich aber auch ausprobieren. Hat es Nachteile?

negaH 28. Nov 2006 00:10

Re: Zirkuläre Unit Reference
 
Nein, hat keine Nachteile (mein Vorschlag oben), mal davon abgesehen das deine ganze Objektorientierte Konstruktion insich falsch sein muß.

Wenn du über solche Sachen wie oben mein Typcast oder den Interfaces herangehen musst dann stimmt irgendwas nicht an deinem Grundkonzept.

Xaromz hat aber insofern Recht, das

1.) der Weg über die Interfaces ein unnötiger Überbau darstellt da wie in meinem Beispiel das Ganze auch ohne Interfaces geht
2.) du die Verwendung von Interfaces und Klassen nicht vermischen solltest da ansonsten das automatische Referencecounting und somit die Speicherfreigabe durcheinander kommt
3.) der Typcast eines Interfaces direkt in das implementierende Objekt in deinem Source vollkommen falsch ist
4.) der Typcast eines Interfaces in sein implementierendes Objekt grundsätzlich gegen die Regeln, bzw. dem Sinn der Interfaces ist

Wenn du schon eine Referenz von einem Interface auf sein implementierendes Objekt erhalten möchtest dann so:

Delphi-Quellcode:

type
  IMyInterface = interface
    function Implementor: TInterfacedObject;
  end;

  TMyImplementor = class(TInterfacedObject, IMyInterface)
    function Implementor: TInterfacedObject;
  end;

function TMyImplementor.Implementor: TInterfacedObject;
begin
  Result := Self;
end;

// oder gleich so, da Result -> EAX und Self -> EAX
function TMyImplementor.Implementor: TInterfacedObject;
begin
end;
Aber!! zum Widersinnigen der Sache:

Interfaces sollen die Implementierung einer Funktionalität im Zusammenhang mit dessen Schnittstellen Deklaration vollständig voneinander trennen. Es soll also keinerlei Zusammenhang zwischen einer deklarierten Schnittstelle zu der eigentlichen Implementierung dieser Schnittstelle bestehen, vollkommene Abstraktion der verschiedenen Schichten eines Denkmodelles das zur "idealen" Black Box führt.

Führt man aber wie oben in die Schnittstelle = Interface wieder eine Schnittstelle auf den Implementor-> TMyInterfcae ein so durchbricht man diese strikte Logik und macht die Anwendung der Interfaces sinnnlos.

Denn die Deklaration von IMyInterface oben bedeutet das diese Schnittstelle nur durch ein Objekt kompatibel zur Klasse TInterfacedObject durchgeführt werden kann. Diese Restriktion soll aber gerade mit Interfaces strikt vermieden werden.

Ergo: ein normaler OOP Ansatz wie in meinem letzten Posting erreicht exakt das Gleiche wie der Vorschlag über die Interfaces. Nur mit dem Unterschied

1.) das es Typsicher ist und somit sauber, wir eben keinen harten und typ-unsicheren Cast verwenden wie in deinen Sourcen
2.) ohne Umwege funktioniert und somit zusätzliche Fehlerquellen beseitigt. Bedenke die schlimmsten Fehler liegen immer im Konzept eines Modelles !
3.) kein Hilfsmittel misbraucht das für ganz andere Aufgaben gedacht ist, hier eben die Interfaces, und somit die spätere Verwenung deines Modelles für andere "korrekt denkende" Programmierer unmöglich macht. Denn für diese Programmierer ist es ein Unding wie du Interfaces mißbrauchen möchtest.

Grundsätzlich wird man aber deine Problemstellung durch pure OOP und konzeptionelle Änderungen lösen können. Statt mit 2 Units zu arbeiten musst du OOP konform dein Klassenkonzept umstellen und mit 3 Unit arbeiten.

1. Unit -> Basisunit die eine abstrakte Vorfahrklasse enthält in der alle Gemeinsamkeiten der Nachfahrklassen deklariert wurden, meistens abstrakte Klassen. Sprich Klassen aus reiner Deklaration von abstrakten virtuellen Methoden ohne reale Implementierung.

2. Unit -> Nachfahre Class1 von der Basisklasse und Verwendung von UnitClass2 in der Implementation Sektion dieser Unit.

3. Unit -> Nachfahre Class2 von der Basisklasse und Verwendung von UnitClass1 in der Implementation Sektion dieser Unit.

Zitat:

Dieser Weg soll der sauberere sein,...

Ich werde wie gesagt den Quellcode erzeugen, also muss ich es nur einmal richtig hin bekommen, und alle interfaces sind richtig.
Wenn du davon überzeugt bist warum fragst du dann noch ? Ich meine, und damit bin ich nicht der Einzigste hier, das du mit dieser Meinung schon von vornherein deinen größten Fehler begehst. Begründungen siehe oben.

Gruß hagen

negaH 28. Nov 2006 00:43

Re: Zirkuläre Unit Reference
 
Zitat:

Warum ist das casten unzulässig?
Ein harter Cast, wie der in deinen Sourcen, ist generell unzulässig und absolut sinnfrei. Denn der Zeiger auf ein Interfaces ist immer unterschiedlich auf den Zeiger der implementerenden Klasse. Das mag für C++ Interfaces die direkt auch die implementieren de Klase darstellen anders sein (der einzgste Fall den ich kenne wo dies so ist). Aber bei Interfaces implementiert durch Borland Delphi PASCAL Klassen ist definitiiv der Zeiger auf ein Interface NICHT identisch mit dem Zeiger auf die Klasse.

Defakto ist der Zeiger auf das Interface ein Zeiger mit festen Offset relativ zum Zeiger auf die implementierende Instance der Klasse. Nachzulesen in Unit TypInfo.pas. Implementiert eine Klasse in Delphi sogar mehere Schnittstellen unterschiedlichen Types so unterscheiden sich sogar all diese Zeiger dieser Interfaces !!

Beispiel
Delphi-Quellcode:

type
  IInterface1 = interface;
  IInterface2 = interface;

  TMyImpl = class(TInterfacedObject, IInterface1, IInterface2)

var
  MyImpl: TMyImpl;
  MyInt1: IInterface1;
  MyInt2: IInterface2;
begin
  MyImpl := TMyImpl.Create;
  MyInt1 := MyImpl as IInterface1;
  MyInt2 := MyImpl as IInterface2;

  if (Pointer(MyImpl) = Pointer(MyInt1)) then // ist FALSE
  if (Pointer(MyImpl) = Pointer(MyInt2)) then // ist FALSE
  if (Pointer(MyInt1) = Pointer(MyInt2)) then // ist FALSE

// ergo:
 
  IInterface1(MyImpl).XYZ; // ist FALSCH
  IInterface2(MyImpl).XYZ; // ist FALSCH
 
  TMyImpl(MyInt1).XYZ;    // ist FALSCH
  TMyImpl(MyInt2).XYZ;    // ist FALSCH

  (MyImpl as IInterface1).XYZ; // ist RICHTIG, wenn IInterface1 eine GUID hätte, hat
  (MyImpl as IInterface2).XYZ; // ist RICHTIG, wenn IInterface2 eine GUID hätte, hat


  (MyInt1 as TMyImpl).XYZ; // ist FALSCH, da ein Interface keinen spzifischen Typ eines Implementor kennt, siehe Abstraktion oben
  (MyInt2 as TMyImpl).XYZ; // ist FALSCH, ....
end;
Ergo: die Frage ist nicht "warum unzulässig" sondern "überhaupt möhglich ?", und nein ist es nicht und erzeugt nur AVs und sonst nichts. Du versuchst nämlich damit nur aus einem Auto die Farbe Grün zu machen ohne irgendwelche Überprüfungen ;)

Gruß Hagen

Koolance 28. Nov 2006 13:18

Re: Zirkuläre Unit Reference
 
Erst mal vielen Dank für deine ausführlichen Antworten. Habe wieder echt was gelernt.

Zitat:

Wenn du davon überzeugt bist warum fragst du dann noch ? Ich meine, und damit bin ich nicht der Einzigste hier, das du mit dieser Meinung schon von vornherein deinen größten Fehler begehst. Begründungen siehe oben.
Ganz sicher war ich mir wohl doch noch nicht... :?
Die Interfacelösung habe ich verworfen.
Die Lösung über TObject habe ich ausprobiert und sie funktioniert sehr gut. :hello:

Die 3 Klassen-Lösung muss ich mal Komplett durchdenken, ich bin mir noch nicht sicher ob ich mit so mein Problem lösen kann.

negaH 28. Nov 2006 14:50

Re: Zirkuläre Unit Reference
 
Zitat:

Die 3 Klassen-Lösung muss ich mal Komplett durchdenken, ich bin mir noch nicht sicher ob ich mit so mein Problem lösen kann.
Sinnvoll ist es darüber nachzudenken, aber je nach Notwendigkeiten lassen sich bestimmte Konstrukte nicht vermeiden. Und in diesem Moment benötigst du wieder Typcasts. Wichtig ist dann nur das du per as Typcastest auch wenn das bischen langsammer geht, dafür absolut sicher ;)

Der Aufwand über abstrakte Vorfahrklassen ist nicht unerheblich, das muß man fairerweise sagen, und eben auch öfters nicht immer sauber umsetzbar. Auch da können dann Typcasts vorkommen.

Gruß Hagen


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:32 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