Interface-Methoden mit return-Werten vom Typ des Interface?
-
Es gibt für dein Problem keine Lösung ohne Zeiger/Referenzen. Ich habe allerdings das Gefühl, dass hier ein grösserer Designfehler dahinter steckt. Warum willst du mathematische Funktionen, welche ja eigentlich absolut gültig sein sollten, abstrahieren?
-
EDIT
Konstruktionen wie diese möchte ich dabei natürlich vermeiden:
void cos(IComplex& ziel);Danke
/EDIT
Hallo!
Wenn Du einen Microsoft-Compiler zur Verfügung hast:
- Erzeug mal ein ATL Projekt und importier z.B. den XML-Parser:
#import <msxml6.dll> //oder welche Version auch immerDas erzeugt zwei Dateien msxml6.tli und msxml6.tlh.
Da drin findest Du zuhauf zurückgeleiferte Interfaces.
Sinngemäss wird dort
ErrorCode X(Irgendwas& foo) // wrapped durch Irgendwas& _X() //und dabei ein Layer für Fehlerbehandlung eingezogenVielleicht kein schlechter Ansatz.
Grüsse
Gast++
-
KasF schrieb:
Hi,
para schrieb:
An dieser Stelle kommt es nun jedoch vor, dass einzelne Funktionen Objekte vom eigenen Typ zurückliefern
[/cpp]dann gib doch auch den eigenen Typ zurück und nicht IComplex. Oder du gibst nen Zeiger bzw ne Referenz zurück ...
Mach es so wie KasF gesagt hat. Das funktioniert (nennt sich covariant return type)
-
Antwort schrieb:
Es gibt für dein Problem keine Lösung ohne Zeiger/Referenzen.
Das vermute ich leider auch...
Antwort schrieb:
Ich habe allerdings das Gefühl, dass hier ein grösserer Designfehler dahinter steckt. Warum willst du mathematische Funktionen, welche ja eigentlich absolut gültig sein sollten, abstrahieren?
Es geht hier wie gesagt um Adapterklassen. Hier werden nicht die Funktionsinhalte abstrahiert, sondern der Zugriff auf konkrete Implementierungen in 3rd Party Libraries wie z.B. std::complex oder gsl_complex.
para
-
lolz schrieb:
Mach es so wie KasF gesagt hat. Das funktioniert (nennt sich covariant return type)
Das ist mir an sich bekannt, und dementsprechend hatte ich das bereits versucht. Allerdings klappt es so nicht wie in meiner Antwort auf seinen Vorschlag zu lesen ist. Zusätzlich zum dort angegebenen Fehler bekomme ich folgenden (Folge-)Fehler:
ComplexAdapter1.h: error: invalid covariant return type for 'virtual ComplexAdapter1 ComplexAdapter1::cos()' IComplex.h: error: overriding 'virtual IComplex IComplex::cos()'Was mich hier jedoch wundert, ist die erste Zeile - wieso wird ComplexAdapter1::cos() als virtual angegeben/interpretiert?
para
-
para schrieb:
An dieser Stelle kommt es nun jedoch vor, dass einzelne Funktionen Objekte vom eigenen Typ zurückliefern
glaub ich dir nicht. dann müsste die funktionsdeklaration nämlich
//statt IComplex cos(); //heißen: ComplexAdapter1 cos(); //bzw. ComplexAdapter2 cos();denn wenn du schreibst, die einzelnen funktionen liefern "objekte vom eigenen typ zurück", musst du in ihnen auch dieses objekt instanziieren
IComplex cos () { //berechnungen... return ComplexAdapter1(...); }das problem ist jetzt folgendes: ComplexAdapter1 wird, weil IComplex eine basisklasse ist, in der funktion ComplexAdapter1::cos automatisch in ein IComplex umgewandelt, und zwar über den copy-constructor IComplex::IComplex(IComplex const&), d.h. alle zusätzliche information, die du ComplexAdapter1 gibst, wird durch die return-anweisung ausgelöscht. nochmal zur veranschaulichung:
ComplexAdapter1 ca; ca.cos()zunächst wird ca instanziiert: d.h. IComplex::IComplex() und danach ComplexAdapter1::ComplexAdapter1() werden aufgerufen. in cos wird ebenfalls ein ComplexAdapter1-objekt konstruiert, d.h. auch dort werden diese beiden funktionen verlassen. mit der "}"-klammer von cos wird jedoch dieses temporäre objekt vernichtet, d.h. IComplex::~IComplex und ComplexAdapter1::~ComplexAdapter1 werden aufgerufen, gleichzeitig wird (versucht) ein neues IComplex *auf basis des danach gleich zerstörten temporären ComplexAdapter1-objekts* herzustellen: IComplex::IComplex(IComplex const&). dieses ist das objekt, das die funktion zurückgibt.
die konsequenz bedeutet für dich: es ist in der tat ein IComplex objekt, das von cos zurück gegeben wird, _es gibt_ kein ComplexAdapter1 objekt mehr, vor allem bedeutet es: du kannst es nicht in ein ComplexAdapter1 objekt casten:
ComplexAdapter1 x = ca.cos();wird nicht hinhauen und
IComplex ic = ca.cos(); dynamic_cast<ComplexAdapter1&>(ic);wird eine exception werfen.
(ein grund mehr, dass anfängern C-style-casts übrigens nicht beigebracht werden sollte.)und du hast jetzt eben das "problem", das IComplex eine rein virtuelle basisklasse ist, bzw. das glück, dass dich der compiler deshalb auf deinen designfehler aufmerksam macht.
/edit: was ist bloß mit meiner rechtschreibung los heute? pfui!
-
para schrieb:
Was mich hier jedoch wundert, ist die erste Zeile - wieso wird ComplexAdapter1::cos() als virtual angegeben/interpretiert?
weil's in ein ersatz für IComplex::cos() ist, und das ist virtuell. die funktionen müssen die gleiche signatur haben. (bis auf den rückgabetyp, weil hier die regeln für die schon genannten kovarianten rückgabetypen einspringen)
-
para schrieb:
lolz schrieb:
Mach es so wie KasF gesagt hat. Das funktioniert (nennt sich covariant return type)
Das ist mir an sich bekannt, und dementsprechend hatte ich das bereits versucht. Allerdings klappt es so nicht wie in meiner Antwort auf seinen Vorschlag zu lesen ist. Zusätzlich zum dort angegebenen Fehler bekomme ich folgenden (Folge-)Fehler:
ComplexAdapter1.h: error: invalid covariant return type for 'virtual ComplexAdapter1 ComplexAdapter1::cos()' IComplex.h: error: overriding 'virtual IComplex IComplex::cos()'Was mich hier jedoch wundert, ist die erste Zeile - wieso wird ComplexAdapter1::cos() als virtual angegeben/interpretiert?
para
Habe vergessen zu erwähnen, dass du Zeiger/Referenzen auf die Objekte zurückgeben musst.
Wenn du die in einen SmartPtr packst, hast du es fast wie zuvor über die Kopie.
-
ich stelle wieder mal ein paar doofe fragen, wenn ihr nichts dagegen habt

warum versuchst du eigentlich die cos sin funktionen in die klasse der komplexen zahlen zu packen? warum denn nicht so:struct myComplexNumber{ float x,y; //evtl n paar konstruktoren //operatorenüberladung, whatever... } /*das interface. Warum machst du das zB nicht so ähnlich wie mit Math.cos() in Java? */ class ComplexMath{ virtual myComplexNumber cos(myComplexNumber c)=0; virtual myComplexNumber sin(myComplexNumber c)=0; virtual myComplexNumber pow(myComplexNumber c)=0; virtual myComplexNumber sinh(myComplexNumber c)=0; virtual myComplexNumber cosh(myComplexNumber c)=0; //usw... die ganzen funktionen halt }; class ComplexMathAdapter1 : public ComplexMath{ //lahme implementierung }; class ComplexMathAdapter2 : public ComplexMath{ //schnelle implementierung };denn... ähm, irgendwie ist mir nicht so ganz klar, warum plötzlich die implementierung der cosinus funktion zu den komplexen zahlen gehören soll.. wenn schon, dann würde ich solche funktionen komplett von allen klassen trennen und als template realisieren, für alles, was eine interne addition/multiplikation sowie eine externe multiplikation mit einem skalar unterstützt... dann bräuchtest du die ganzen funktionen für all deine anderen Körper nicht neuzuschreiben...

naja... wahrscheinlich wieder ein eher sinnloser beitrag, weil ich evtl. das problem gar nicht richtig verstanden habe, aber was solls... :p
sry, wenn's nix nützt
-
warum nicht sinnvoll? es geht doch die ganze zeit schon um richtiges/falsches design.
-
Antwort schrieb:
Es gibt für dein Problem keine Lösung ohne Zeiger/Referenzen....
Und zwar aus dem einfachen Grund, dass ansonsten der Kompiler prinzipiell ein Rückgabeobjekt (mittels CopyCtor) erzeugt ... und das geht nunmal nicht mit abstrakten Klassen.
(Ja - es gibt RVO, aber IIRC prüft der Compiler trotzdem die "Copy-Erzeugbarkeit" des Returntyps).
Nur mit Referenz/Pointer tut der Compiler das nicht (bzw. nur mit dem Pointer selbst) ... und auch nur so kann man überhaupt die Polymorphie (für die mn ja eigentlich das Interface gebaut hat) einsetzen.Typischer Fall von "zuviel Java programmiert"

Gruß,
Simon2.
-
queer_boy schrieb:
para schrieb:
An dieser Stelle kommt es nun jedoch vor, dass einzelne Funktionen Objekte vom eigenen Typ zurückliefern
glaub ich dir nicht. dann müsste die funktionsdeklaration nämlich
//statt IComplex cos(); //heißen: ComplexAdapter1 cos(); //bzw. ComplexAdapter2 cos();Deswegen schrieb ich auch extra "Pseudocode", der nur der Veranschaulichung der Situation dienen sollte. Trotzdem hast du mit diesem Detail natürlich Recht...
queer_boy schrieb:
die konsequenz bedeutet für dich: es ist in der tat ein IComplex objekt, das von cos zurück gegeben wird, _es gibt_ kein ComplexAdapter1 objekt mehr
Das ist völlig ok und beabsichtigt, da es sich hier um einen Adapter handelt, der per Definition (s.o., "Schnittmenge") nicht über mehr Funktionalitäten als die Schnittstelle verfügt. Diese Konformität nach außen ist ja auch der Sinn eines Interfaces.
queer_boy schrieb:
und du hast jetzt eben das "problem", das IComplex eine rein virtuelle basisklasse ist, bzw. das glück, dass dich der compiler deshalb auf deinen designfehler aufmerksam macht.
Als Designfehler sehe ich das nach wie vor nicht an, und die Frage nach dem "richtig/falsch" wird so einfach sicherlich auch nicht beantwortet werden können. Ein "richtig" ist in Sachen Design schließlich auch eine Frage des Kontextes. Es kann durchaus gute Gründe geben Funktionen wie cos() als eine Art indirekte Eigenschaft einer komplexen Zahl (auf Grund des direkten Bezuges zu ihr) zu betrachten und diese somit zu integrieren, um z.B. im Sinne einer Fassade die wahre Komplexität zu kapseln. Ich benötige hier auch keine Templates, da nicht etwa eine generische Funktion cos() im Vordergrund steht, sondern lediglich die komplexe Zahl...
Wenn ich Software-Architekturen entwerfe, gehe ich i.d.R. zunächst rein theoretisch an die Sache heran. Dummerweise ist in diesem speziellen Fall C++ das Problem, da bspw. Java diese Konstruktion ohne Murren zulässt - leider merkt man das eben u.U. erst bei der Realisierung in einer konkreten Sprache

para
-
Simon2 schrieb:
Typischer Fall von "zuviel Java programmiert"

Danke, genau das scheint der Fall zu sein (wahlweise auch c#)

-
Andrey schrieb:
wahrscheinlich wieder ein eher sinnloser beitrag, weil ich evtl. das problem gar nicht richtig verstanden habe, aber was solls...
Absolut nicht sinnlos (sowas gibt's nicht)! In einem anderen Kontext hättest du auch Recht, nur ist der Hintergrund hier eben ein anderer (s. vorletzte Antwort von mir) - und so wie's bislang aussieht komme ich in C++ nicht an diesem Weg vorbei und werde somit ein zweites Interface zum adaptieren der Funktionen auf komplexen Zahlen anbieten...
Dank dir,
para
-
para schrieb:
Wenn ich Software-Architekturen entwerfe, gehe ich i.d.R. zunächst rein theoretisch an die Sache heran. Dummerweise ist in diesem speziellen Fall C++ das Problem, da bspw. Java diese Konstruktion ohne Murren zulässt - leider merkt man das eben u.U. erst bei der Realisierung in einer konkreten Sprache
Nein, Java lässt das auch nicht zu. Weil du verwechselst leider die Syntax und die Bedeutung.
Sowas hat Java garnicht:IComplex cos();Du hast einfach die Syntax aus Java 1:1 auf C++ übertragen, hast aber deren andere Semantik missachtet! Die Syntax in C++ ist eher die hier, wenn man Javas Semantik übertragen will:
IComplex& cos(); // oder IComplex* cos();
-
para schrieb:
Dummerweise ist in diesem speziellen Fall C++ das Problem, da bspw. Java diese Konstruktion ohne Murren zulässt - leider merkt man das eben u.U. erst bei der Realisierung in einer konkreten Sprache
ich hab da mal gestern versucht, ne kleine mathematische spielerei in java zusammenzubasteln, bin beinahe dran verzweifelt, weil es in java nun mal keine zeiger gibt^^ Wie verdammt unterschiedlich die beiden sprachen sind merkt man irgendwie erst dann, wenn man plötzlich von einer zu der anderen wechselt^^

-
@Artchi:
Duh! Ich gebe zu, dass da was dran ist. Mea culpa, vielleicht war's heute einfach zu warm und ich sollte mir die Sache nochmal genauer ansehen...@Andrey:
Da hast ja soo Recht! Als wenn diese beiden dann auch die noch die einzigen wären - ich glaub ich werd langsam alt
-
para schrieb:
...Dummerweise ist in diesem speziellen Fall C++ das Problem, da bspw. Java diese Konstruktion ohne Murren zulässt - leider merkt man das eben u.U. erst bei der Realisierung in einer konkreten Sprache

para
Sagen wir mal so: Wenn Du mit der Sprache richtig vertraut wärst, würdest Du merken, dass das in C++ genauso einfach und korrekt umzusetzen ist und Deine Schwierigkeiten mitnichten an der Sprache liegen

Das C++-Pendant zu Java-Referenzen sind nämlich Pointer ! Wenn Du es nun wie in Java abbilden, aber dabei keine Pointer verwenden möchtest, hast Du Dir einfach selbst ein Bein gestellt.
Ist aber nicht besonders tragisch, weil es einer der typischen Umsteigerfehler ist, wie auch hier zu sehen:
Andrey schrieb:
...weil es in java nun mal keine zeiger gibt...
Einfach falsch !!
Es gibt in Java nichts anderes als Zeiger (und primitive Typen) ! Allerdings besteht da eben deswegen keine Notwendigkeit, diese besonders zu kennzeichnen (im Gegensatz zu C++, wo es noch "Variablen" und C++-Referenzen gibt, weswegen man * und & in der Typenbezeichnung braucht).Gruß,
Simon2.
-
Simon2 schrieb:
Sagen wir mal so: Wenn Du mit der Sprache richtig vertraut wärst, würdest Du merken, dass das in C++ genauso einfach und korrekt umzusetzen ist und Deine Schwierigkeiten mitnichten an der Sprache liegen

Das C++-Pendant zu Java-Referenzen sind nämlich Pointer ! Wenn Du es nun wie in Java abbilden, aber dabei keine Pointer verwenden möchtest, hast Du Dir einfach selbst ein Bein gestellt.
Ist aber nicht besonders tragisch, weil es einer der typischen Umsteigerfehler istOch, damit habe ich auch kein Problem. Wie ich in meinen letzten Posts bereits schrieb, hatte ich an der Stelle einfach ein "Brett vorm Kopp". Mir sind diese Punkte durchaus bekannt, nur sind sie eben länger nicht mehr abgerufen worden. Zum einen wechseln heute recht häufig meine eingesetzten Sprachen (und C++ hatte die längste Pause) und zum anderen code ich heutzutage auch immer weniger, da sich nur noch alles um Architekturen, Prozesse und Steuern von Consultants dreht. Soll heißen: fast alles entsteht als (UML-) Diagramm, und die konkrete Sprache ist "bloß" eine Nebensache...

Dank euch,
para
-
para schrieb:
......
Och, damit habe ich auch kein Problem....... und es ist großes Kino von Dir, das hier so zu sagen.

Danke !Ich hoffe, ich konnte Dir helfen.
Gruß,
Simon2.