2016-10-15 4 views
5

Ich bin neu in Delphi. Für ein Projekt, das von meiner Firma benötigt wird, muss ich Code aus unseren bestehenden C++ - Klassen nach Delphi übersetzen. Einige dieser Klassen sind Templates, wie zum Beispiel:Arithmetische Operationen mit generischen Typen in Delphi

Ich benutze es z.B. mit diesem Code

APoint<float> pt; 
pt.m_X = 2.0f; 
pt.m_Y = 4.0f; 
pt.Add(5.0f); 

und das funktioniert gut.

Jetzt muss ich gleichwertigen Code für Delphi schreiben. Ich habe versucht, eine Delphi generische Klasse zu schreiben, basierend auf dem C++ Code oben:

APoint<T> = record 
    m_X: T; 
    m_Y: T; 

    procedure Add(value: T); 
end; 

procedure APoint<T>.Add(value: T); 
begin 
    m_X := m_X + value; 
    m_Y := m_Y + value; 
end; 

jedoch dieser Code nicht kompiliert. Ich bekomme diesen Fehler:

AFAIK dieser Code sollte funktionieren, und ich verstehe nicht, was ist falsch mit ihm. So kann mir jemand erklären:

  1. Warum ein solcher Code in Delphi nicht kompiliert?

  2. Was ist der korrekte (und einfachste) Weg in Delphi, um eine Template-Klasse zu erstellen, die eine Add()-Funktion bietet, die dem C++ - Code und der obigen Verwendung am nächsten kommt?

auf 2016.10.17

EDITED

Danke für alle Antworten. Wenn ich also richtig verstanden habe, gibt es keine Möglichkeit, eine C++ - ähnliche Stilvorlage zu erstellen, da Delphi mehrere Einschränkungen auferlegt, die in C++ nicht existieren.

Darauf basierend suchte ich einen Workaround, um das gewünschte Ziel zu erreichen. Ich fand die folgende Lösung:

IPoint<T> = interface 
    procedure Add(value: T); 
end; 

APoint<T> = class(TInterfacedObject, IPoint<T>) 
    m_X: T; 
    m_Y: T; 

    procedure Add(value: T); virtual; abstract; 
end; 

APointF = class(APoint<Single>) 
    destructor Destroy; override; 
    procedure Add(value: Single); reintroduce; 
end; 

destructor APointF.Destroy; 
begin 
    inherited Destroy; 
end; 

procedure APointF.Add(value: Single); 
begin 
    m_X := m_X + value; 
    m_Y := m_Y + value; 
end; 

Ich benutze es zum Beispiel mit diesem Code

procedure AddPoint; 
var 
    pt: IPoint<Single>; 
begin 
    pt := APointF.Create; 

    APointF(pt).m_X := 2.0; 
    APointF(pt).m_Y := 4.0; 
    APointF(pt).Add(5.0); 
end; 

und das funktioniert gut. Jedoch finde ich den Stil etwas schwer, z.B. die Notwendigkeit, APointF (pt) zu verwenden. In Bezug auf obigen Code sind meine Fragen also:

  1. Ist diese Lösung eine gute Lösung? (d. h. besser, eine Version jedes Datensatzes für jeden Typ zu schreiben, den ich unterstützen möchte, wie z. B. APointF, APointI, APointD, ...)
  2. Gibt es eine Möglichkeit, diesen Code zu vereinfachen, z. eine Lösung um pt.m_X direkt ohne die APointF (pt) Konvertierung aufzurufen? (NOTE Ich habe hier die Implementierung von Eigenschaften weggelassen, auch wenn ich denke, sie eleganter als der Zugriff auf die Variable direkt)
  3. Was ist mit den Leistungen dieser Lösung? (Ie ist diese Lösung drastisch langsamer als eine direkte m_X: = m_X + Wert Addition?
  4. )

Schließlich sah ich eine andere Lösung in dem Delphi-Code, wo es möglich ist, einen Gleichheitsvergleich von 2 generischen Typen auf diese Weise zu implementieren:

function APoint<T>.IsEqual(const other: APoint<T>): Boolean; 
var 
    comparer: IEqualityComparer<T>; 
begin 
    Result := (comparer.Equals(m_X, other.m_X) and comparer.Equals(m_Y, other.m_Y)); 
end; 

Ich habe versucht, den Code hinter der Szene zu lesen aber ich fand es furchtbar kompliziert. Also, meine Fragen sind:

  1. Ist eine solche Lösung besser als die oben vorgeschlagene?
  2. Gibt es eine ähnliche gebrauchsfertige Lösung für mathematische Operationen?
  3. Ist die Leistung einer solchen Lösung akzeptabel?

Vielen Dank im Voraus für Ihre Antworten

Grüße

+0

[Generics Constraints] (http://docwiki.embarcadero.com/RADStudio/de/Constraints_in_Generics) können weder Ordinalwerte noch Gleitkommazahlen definieren. Dies ist, was Sie hier sehen, da jedes 'T' keine arithmetischen Operatoren hat. –

+0

Einige Tests hier mit Generika und arithmetischen Operatoren durchgeführt: https://delphihaven.wordpress.com/2011/03/18/generic-arithmetic/. –

+0

* "Generics in Delphi sind im Gegensatz zu Vorlagen in C++ oder generischen Typen in C#. Vor allem kann ein Typparameter nicht auf einen bestimmten einfachen Typ beschränkt werden, wie Integer, Double, String und so weiter." * [Link] (http://docwiki.embarcadero.com/RADStudio/en/Overloads_and_Type_Compatibility_in_Generics) –

Antwort

4

Delphi Generika unterstützen keine arithmetische Operatoren, die auf generische Typen handeln. Damit der Compiler den Code akzeptiert, muss er wissen, dass jede Operation eines generischen Typs bei der Instanziierung verfügbar sein wird.

Mit generischen Einschränkungen können Sie dem Compiler mitteilen, welche Funktionen der Typ hat. Mit generischen Einschränkungen können Sie dem Compiler jedoch nicht mitteilen, dass der Typ arithmetische Operatoren unterstützt.

Leider ist das, was Sie versuchen, einfach nicht möglich. Sicherlich können Sie selbst Frameworks erstellen, die Werkzeuge wie Schnittstellen verwenden können, um die Arithmetik durchzuführen, aber dabei die Leistung aufgeben. Wenn das akzeptabel ist, dann gut. Sonst beißt man am besten in die Kugel und vermeidet Generika hier.

Oh für C++ Vorlagen.

+0

Tatsächlich ist C++ Templates eines der beiden Dinge, die ich wirklich aus C++ vermisse. Kürzlich habe ich eine Bibliothek für lineare Algebra geschrieben und reelle und komplexe Matrizen implementiert. Ich musste den gesamten Basiscode für die beiden Fälle duplizieren ... –

5

Delphi-Generics unterscheiden sich von C++ - Templates und ähneln mehr ihrem C# -Pendant.

In C++ können Sie beliebige Operationen mit Vorlagentypen ausführen, und zum Zeitpunkt der Vorlageninstanziierung überprüft der Compiler, ob die Operation, die Sie in der Vorlage ausführen, für den von Ihnen verwendeten Typ verfügbar ist. Wenn nicht, erhalten Sie einen Compilerfehler.

In Delphi (und vielen anderen Sprachen) deklarieren Sie einen generischen Typ, der möglicherweise einige deklarative Einschränkungen bereitstellt, und diese Einschränkungen - Basisklassen oder Schnittstelle - bestimmen die Operationen, die Sie für den generischen Typ ausführen können. Zur Instanziierungszeit ist die einzige Prüfung, ob der deklarierte Typ die Beschränkung erfüllt.

Wahrscheinlich könnte die Delphi-Sprache Einschränkungen für Fließkomma- oder Ordinaltypen hinzufügen, aber dies würde eine sehr begrenzte Flexibilität bieten (Ändern des Floating- oder Integertyps, den Sie in der generischen Instanz verwenden können). Ich persönlich betrachte das nicht als ein kritisches Merkmal.

+3

Überladung des Operators bedeutet, dass die Arithmetik für andere Typen als die angegebenen ausgeführt werden kann. Ein gutes Beispiel wäre ein komplexer Zahlentyp, aber es gibt noch viele mehr. Daher ist der Wunsch nach mehr Funktionalität nicht auf eingebaute Typen beschränkt. Wirklich, was wir hoffen würden, ist in der Lage zu sein, einen Typ einzuschränken, um zu verlangen, dass er spezifische Operatoren definiert hat. Ob es ein kritisches Merkmal ist oder nicht, es hängt eindeutig davon ab, wen Sie fragen. Ich denke, jeder, der eine ernsthafte numerische Programmierung macht, würde davon profitieren, Arithmetik mit generischer Programmierung kombinieren zu können. –

+2

Delphi würde von einem Hintergrund mit umfangreicher numerischer Verarbeitung profitieren und würde sicherlich von den arithmetischen Operatorbeschränkungen in Generika profitieren. –

+0

Dies ist eines der beiden Dinge, die ich in Delphi im Vergleich zu C++ wirklich vermisse. –

Verwandte Themen