[C++] Template Typen einschränken

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • [C++] Template Typen einschränken

    Moin liebe C++ Liebhaber.
    (Eventuell auch: Hallo Zerm!)

    Ich erweitere gerade meinen Horizont um C++ mit einer Literatur aus 1994 und hätte da mal ein spezifisches Problem.

    Die Anforderung sei eine Mitgliederverwaltung eines Vereins, welcher sowohl natürliche als auch juristische Personen aufnimmt.
    Gegeben sei folgende Klassenhierachie der zu verwaltenden Daten.

    C-Quellcode

    1. class Person {
    2. protected:
    3. String name;
    4. PhoneNumber telefon;
    5. MailAddress email;
    6. public:
    7. /* Getter, Setter, Konstruktoren, Destruktoren... */
    8. }
    9. class NaturalPerson : public Person {
    10. private:
    11. Salutation anrede;
    12. String vorname;
    13. Address wohnadresse;
    14. Address jobadresse;
    15. Date geburtstag;
    16. public:
    17. /* Getter, Setter, Konstruktoren, Destruktoren... */
    18. }
    19. class JuristicPerson : public Person {
    20. private:
    21. Address firmensitz;
    22. String handelsregisterNummer;
    23. String umsatzsteuerID
    24. Date gruendungsdatum
    25. public:
    26. /* Getter, Setter, Konstruktoren, Destruktoren... */
    27. }
    Alles anzeigen


    Nun möchte ich die Dinger so verwalten, dass meine Liste über die Mitgliedsnummer nach Eintritt indiziert (der Einfachheit halber ein Stack) und idealerweise für jeden Kontakt eine Adresse für die Aufkleber der Mitgliederzeitung generiert.

    Dazu meine zwei brennenden Fragen zur Gestaltung dieses Stacks, der vermutlich wegen der unterschiedlichen Datentypen als Template definiert werden muss:
    1) (Wie) Kann ich in dem Template sicherstellen, dass zwar NaturalPerson und JuristicPerson in den Stack gepackt werden können, aber keine 'abstrakte' Person?
    ('Abstrakt' in Anführungszeichen. Diese Person soll schon relativ konkret sein und auch im internen Telefonverzeichnis aufgeführt werden, wobei die Adressen irrelevant werden. Das Schlüsselwort abstract scheidet also aus.)

    2a) (Wie) Kann ich besagte Adressaufkleber generieren, wo doch die notwendigen Adressen über unterschiedliche Nachrichten angesteuert werden?
    (Wenn ich zerm richtig verstanden habe, ist die Klassenprüfung in objektorientiertem C++ ja verpönt.)
    2b) Natürlich könnte ich eine Methode bevorzugteKontaktadresse() definieren, die dann je nach hinterlegter Präferenz eine valide (natürliche Person) oder die einzige (natürliche und juristische Person) Adresse ausspuckt.
    Nur kann ich die ja nicht in Person hinterlegen, weil die den Typen Address nicht einmal kennt. Und sofern 1) nicht gelöst werden kann ist somit der Compiler folgerichtig der Meinung, dass eine Person nicht auf bevorzugteKontaktaddresse() reagieren wird, obwohl die konkrete Subklasse im Stack das kann. Ein Dilemma, das ich lösen möchte.
    Ganz genau genommen ist diese bevorzugte Kontaktadresse auch keine Eigenschaft einer Person als solche sondern kann von Verein zu Verein variieren und gehört somit auch gar nicht in die konkreten Subklassen.

    3a) Liege ich eventuell mit meiner Vermutung falsch und ein Template ist der falsche Weg?
    3b) Wie sähe in diesem Fall der richtige Weg aus?

    Disclaimer:
    Nein, das ist kein konkretes Projekt. Ich habe mir zur Veranschaulichung nur etwas Realistisches ausgesucht. Ich will ja nicht, dass Andere Programme für mich entwickeln. Ich will mich nur weiterbilden.
    In Objective-C stellt sich mir diese Frage nicht.
    3) entfällt, da Templates nicht unterstützt werden.
    1) würde ich als Personenprotokoll realisieren und den Stack auf dieses Protokoll typisieren
    2) finge ich stumpf mit -respondsToSelector: ab

    Falls Freunde von Swift bis hierher gelesen haben:
    1) und 3) wäre ja identisch zu Objective-C
    Wie könnte ich aber 2) in Plain Swift (also ohne Objective-C Overhead) realisieren?
    Eventuell lässt sich daraus eine C++ Lösung ableiten.

    Selbstnotiz: Fragestellung + Korrekturen ~1h
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • warum nicht einfach eine map <unsigned, Person> oder vector<Person> oder was auch immer.
    Eventuell in eine eigene klasse verpacken mit nur push(NaturalPerson &p) und push(JuristicPerson &p)?
    In Person kannst du ein class forward für "Address" verwenden und somit eine methode deklarieren die dann erst in NaturalPerson und JuristicPerson eine entsprechende adresse liefert.

    respondsToSelector ist ja gleich verpönt wie auf eine bestimmte klasse zu prüfen.

    hab mit swift nicht viel am hut aber da könntest du ja zb AnyObject verwenden...
  • Also für mich hört sich das nach ganz konventionellem OO-Zeug an. Da Du ja Adressaufkleber von Personen drucken möchtest, sollte Person auf jeden Fall eine Methode besitzen, die eine Adresse zurückgibt. Welche das ist, entscheiden die konkreten Subklassen. Ergo:

    virtuelle Methode in Person:

    Marco Feltmann schrieb:

    class Person {
    protected:
    String name;
    PhoneNumber telefon;
    MailAddress email;

    public:
    /* Getter, Setter, Konstruktoren, Destruktoren... */
    virtual ~Person(); // siehe Scott Meyers Effective C++ et.al.
    virtual const Adress& adress() const=0;
    }
    Und noch was: bitte keine C++ Bücher von 1994 benutzen (wenn, dann nur unter MS-DOS), wir leben im 21.Jhd...
  • Ich habe mir jetzt den ganzen Roman nicht genau durchgelesen. Mutmaßlich wären Concepts das Richtige für dich. Die gab es allerdings 94 noch nicht. (Und immer noch nicht, AFAIK, aber sie sind kurz davor Standard zu werden.)
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • gritsch/Markus
    Ja, nee.
    In meinem Verein arbeiten ehrenamtlich Flüchtlinge und Obdachlose.
    Da wir im 21. Jahrhundert leben, haben die zwar Smartphones und dementsprechend Telefonnummer (Mobilfunk) und Mailadresse (Apple ID, Blackberry ID, Microsoft Live ID, Google Konto... Hab ich wen vergessen?) aber keine Adresse. 'Unter der Kleinen Graskellerbrücke' oder 'In der Flüchtlingsstation am südlichen Stadtrand' sind gemäß hier geltendem Standard keine gültigen Adressen.

    Ich statte also ein konkretes Objekt mit einer Methode aus, die es überhaupt nicht braucht. Damit ich C++ krudes Typsystem überlisten kann um auf konkrete abgeleitete Objekte zuzugreifen.

    Gibts denn keine Interfaces wie in Java bzw. Protokolle wie in Objective-C/Swift, mit denen ich die Subklassen als Adressierbar implementieren kann um dieses Interface/Protokoll dann ins Template zu stopfen? Damit würde ich ohne direkte Objektprüfung die adressenlose Elternklasse aussperren.

    Vermutlich darf ich mir eine abstrakte Klasse Adressierbar erstellen und hätte damit die einzig sinnvolle Nutzungsweise für Mehrfachvererbung gefunden.


    Markus Müller schrieb:

    Und noch was: bitte keine C++ Bücher von 1994 benutzen (wenn, dann nur unter MS-DOS), wir leben im 21.Jhd...

    Amin Negm-Awad schrieb:

    Die gab es allerdings 94 noch nicht. (Und immer noch nicht, AFAIK, aber sie sind kurz davor Standard zu werden.)


    Fun Fact:

    Kapitel 3.14.1, Überladen von Templates, Objektorientiertes Programmieren in C++, 1. Auflage 1994/ 2. unveränderter Nachdruck 1995, Nicolai Josuttis, Addison-Wesley Verlag schrieb:


    Da dieses Problem immer wieder auftriit, gibt es die Überlegung, C++ um die Möglichkeit zu erweitern, die Menge der für Templates zugelassenen Datentypen durch ein spezielles Sprachkonstrukt einzuschränken.

    Im 21. Jahrhundert hat C++ dieses Sprachkonzept nach 20 Jahren Überlegung immer noch nicht drin. Scheint ein komplexes Thema zu sein.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Aber auch ein Flüchtling, Obdachloser oder sonstwas ist eine "Person" oder willst du dafür eine eigene "Klasse" erstellen?
    Ob die dann eine korrekte Adresse haben oder nicht ist doch dann eine eigenschaft der Instanz.

    Auch in Obj-C oder Java könntest du das Problem nicht mit Protokollen oder sonstwas erledigen denn du willst Personen in einer Personenklasse speichern, dann aber doch wieder nicht. Und dass eine Klasse manchmal Eigenschaften hat welche nur von bestimmten Instanzen verwendet werden ist doch immer so. Man erstellt ja auch nicht extra eine "Albino"-Klasse weil man für solche Personen die Haut- bzw Haarfarbe eh nicht benötigt...
  • gritsch schrieb:

    Aber auch ein Flüchtling, Obdachloser oder sonstwas ist eine "Person" oder willst du dafür eine eigene "Klasse" erstellen?
    Person: Hauptklasse ohne Adresse.
    Natürliche Person: Subklasse von Person mit mehreren Adressen
    Juristische Person: Subklasse von Person mit einer Adresse

    Natürlich geht das in Objective-C mit Protokollen, wenn Person dieses nicht implementiert ist sichergestellt, dass nur Personen mit Adresse im vector/der map landen.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Marco Feltmann schrieb:

    gritsch schrieb:

    Aber auch ein Flüchtling, Obdachloser oder sonstwas ist eine "Person" oder willst du dafür eine eigene "Klasse" erstellen?
    Person: Hauptklasse ohne Adresse.Natürliche Person: Subklasse von Person mit mehreren Adressen
    Juristische Person: Subklasse von Person mit einer Adresse

    Natürlich geht das in Objective-C mit Protokollen, wenn Person dieses nicht implementiert ist sichergestellt, dass nur Personen mit Adresse im vector/der map landen.
    Dann bräuchtest du aber noch eine dritte klasse die das gar nicht implementiert denn einfach nur keine adresse zurückgeben oder eine flag die anzeigt dass es keine gültige adresse gibt kannst du so ja auch schon (und anhand dessen dann filtern).
    Es kommt aber auch drauf an ob du eine adresse willst oder mehrere. einfach eine methode implementieren die dir zb alle adressen für newsletterversand liefert. Das hängt dann ja auch nicht von der klasse ab sondern vor allem ob die natürliche person a) eine adresse hat, b) den AGBs zubgestimmt hat und c) nicht per opt-out vom newsletterempfang ausgeklinkt hat. Genauso kann bei einer juristischen person eine gültige newsletter adresse sein oder auch mehrere oder eben wiederum garkeine...
    Also warum sowas von der Klasse abhängen lassen und nicht einfach vom resultat einer methode welche jede klasse implementieren kann wie sie will (vererbung eben).
  • Der Vorgang 'Versandadresse für Mitgliederzeitschrift herausgeben' gehört halt nicht zu einer Person sondern zur Verwaltung.

    Selbstverständlich kann ich eine Klasse 'Mitglied' erstellen, die dann eine 'Person' als Member hat, eine Methode 'bevorzugteVersandadresse' kennt und dann die Annahmen bezüglich des Etikettendrucks trifft.

    Diese könnte dann natürlich bei Personen ohne Adresse nix zurückgeben, genauso wie bei Personen, die die Mitgliederzeitschrift nicht empfangen wollen.

    Oder ich verhindere in der 'Mitglied' das Einhängen von per Definition adresslosen Personen.
    Selbes Problem wie zuvor.

    Die Mehrfachvererbung mit einer abstrakten Klasse 'Adressierbar' scheint mir am Sinnvollsten.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Ich verstehe hier nicht warum du eine adressliste abhängig von klassen machen willst. es liegt doch an den einstellungen (AGB, opt-in, opt-out, empfangsland, adresstypen, sprache, etc) in den entsprechenden klassen an welche adressen etwas gesendet wird oder eben nicht und nicht an der klasse in der die daten grad liegen (denn es macht nicht sinn 20 verschiedene klassen allein für postalieschen newsletterversand zu erstellen, dann noch 15 für newsletterversand per e-mail, dann noch 35 für newsletterversand per SMS etc).
  • Ja, eine abstrakte Klasse und Mehrfachvererbung sind in der Tat die C++-Äquivalente zu Protokollen.

    Ich verstehe aber immer noch nicht, warum es mit meiner oben vorgeschlagenen Variante nicht gehen soll? Du möchtest ja schließlich Adressaufkleber von Personen drucken - es sollte also eine wie auch immer geartete Verbindung Person<->Adresse geben.

    Zu dem C++ Buch aus der Steinzeit: mit C++11 und C++14 hat sich vieles an der Sprache zum besseren geändert, wenn man bei C++ von so etwas wie Verbesserung sprechen möchte...SCNR ;)
  • Marco Feltmann schrieb:

    In Objective-C stellt sich mir diese Frage nicht.
    Wenn Du weißt wie man das in Obj-C macht, dann kannst Du das auch in Cpp so machen. Statt Protocol heißt das dann Abstract Class in C++ (in anderen Sprachen Interface). Templates (Generics) schreibe ich in C++ kaum. Ist ja mehr oder weniger ein Code-Generator. Das kann mann ruhig beim Code-Refactoring machen. Ist unter Umständen auch beim Debugging einfacher, wenn es kein Template ist. ResponseToSelector gibt es nicht, aber man kann z.B. dynamisch Kasten. Oder Du machst es dir noch einfacher und packst jede Person in eine Map<string, string> und schreibst passende getter/setter/factory Methoden. Möglichkeiten gibt es viele...

    Ich guck in meinem Stroustrup von '99 auch nicht mehr rein. Wenn man OO kennt, dann ist C++ nicht so das Problem. Man muss halt die Konzepte hinter der Sprache verstehen. Wie jede Sprache hat auch C++ seine Eigenheiten. Als Dokumentation finde cplusplus.com nicht schlecht. Da gibt es sicher auch Artikel. Ansonsten halt Stroustrup oder Scott Meyers.
  • Marco Feltmann schrieb:

    gritsch schrieb:

    Aber auch ein Flüchtling, Obdachloser oder sonstwas ist eine "Person" oder willst du dafür eine eigene "Klasse" erstellen?
    Person: Hauptklasse ohne Adresse.Natürliche Person: Subklasse von Person mit mehreren Adressen
    Juristische Person: Subklasse von Person mit einer Adresse

    Natürlich geht das in Objective-C mit Protokollen, wenn Person dieses nicht implementiert ist sichergestellt, dass nur Personen mit Adresse im vector/der map landen.
    Das geht doch eben genau so mit Concepts C++. Deshalb verstehe ich nicht, warum das keine Lösung sein soll.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • manoh schrieb:

    Marco Feltmann schrieb:

    In Objective-C stellt sich mir diese Frage nicht.
    Wenn Du weißt wie man das in Obj-C macht, dann kannst Du das auch in Cpp so machen. Statt Protocol heißt das dann Abstract Class in C++ (in anderen Sprachen Interface). Templates (Generics) schreibe ich in C++ kaum. Ist ja mehr oder weniger ein Code-Generator. Das kann mann ruhig beim Code-Refactoring machen. Ist unter Umständen auch beim Debugging einfacher, wenn es kein Template ist. ResponseToSelector gibt es nicht, aber man kann z.B. dynamisch Kasten. Oder Du machst es dir noch einfacher und packst jede Person in eine Map<string, string> und schreibst passende getter/setter/factory Methoden. Möglichkeiten gibt es viele...
    Ich guck in meinem Stroustrup von '99 auch nicht mehr rein. Wenn man OO kennt, dann ist C++ nicht so das Problem. Man muss halt die Konzepte hinter der Sprache verstehen. Wie jede Sprache hat auch C++ seine Eigenheiten. Als Dokumentation finde cplusplus.com nicht schlecht. Da gibt es sicher auch Artikel. Ansonsten halt Stroustrup oder Scott Meyers.
    Dann braucht er schon mindestens einmal Mehrfachvererbung, weil er von Person erben möchte und dann noch die abstrakte Klasse auf diejenigen Subklassen vererben muss, die darin landen dürfen.

    Ganz generell geht natürlich im Übrigen sonst nicht alles, weil bei C++ alles zur Übersetzungszeit festliegen muss.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Marco Feltmann schrieb:

    Vermutlich darf ich mir eine abstrakte Klasse Adressierbar erstellen und hätte damit die einzig sinnvolle Nutzungsweise für Mehrfachvererbung gefunden.
    Du hast eine "sinnvolle" Nutzung für Mehrfachvererbung *in C++* gefunden. Das Problem ist, dass Klassen ein Interface und eine Implementierung beinhalten. Protokolle enthalten nur ein Interface. C++ sagt jetzt, ist ja alles einerlei, implementiere ich einfach NULL und dann habe ich ja ein Protokoll, äh, eine abstrakte Klasse, äh, ein Protokoll, äh, ist ja dasselbe. Technisch stimmt das fast, semantisch nicht einmal ansatzweise.

    Richtiger wäre es, wenn man als API nur Protokolle kennt und das Interface einer Klasse automatisch ein Protokoll wäre. Das würde dann gleich auch Kategorien und Mix-Ins helfen.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Amin Negm-Awad schrieb:

    Ganz generell geht natürlich im Übrigen sonst nicht alles, weil bei C++ alles zur Übersetzungszeit festliegen muss.
    ???

    Wenn das eine Anspielung auf die Typisierung sein soll, dann hat das doch nur mit "wie man programmiert" zu tun. Wenn alles zur Übersetzung festliegen muss, dann würde man keine PlugIns und andere in C (C/C++) geschriebene Laufzeitumgebungen für Sprachen (Obj-C, Ruby, Python ...) usw. schreiben können.

    Die strenge Typisierung hat Vor- und Nachteile. Mir gefällt es eigentlich gut, wenn der Compiler schon Fehler erkennt und man nicht erst durch Laufzeitfehler darauf aufmerksam gemacht wird bzw. dann auch 100% Code-Coverage machen muss.
  • Eigentlich mittlerweile ganz schön off topic ...

    Zur Übersetzungszeit muss doch immer irgendein Typ bekannt sein, auch wenn überall void * steht. Und void * kann doch alles sein.

    respondsToSelector ist doch eine Methode von NSObject, dass Introspection/Reflexion von Klassen bietet, aber keine Module. Also irgendwelche Metadaten werden hier berücksichtigt. Das bietet C++ nicht direkt. Wenn man sowas braucht, dann muss man das selber implementieren, es könnte sein, dass da was mit Boost geht und Qt erzeugt mit moc (zusätzlicher Preprozessor) irgendwelche Metadaten. Ich glaube, das kommt auch noch direkt in C++ demnächst oder ich verwechsle da was mit dem C++ Interpreter von Root/Cern.

    Eine Laufzeitumgebung wie NSObject hat C++ auch nicht. Muss man selber schreiben, wenn man sowas haben möchte. Wie das bei Visual C++ ist, weiß ich ned.
  • Das ist nicht off-topic, weil es um die Eingrenzung eines Typen geht.

    void* ist nicht "dereferenzierbar". id ist "dereferenzierbar". void* Referenzen können daher nur zur Zuweisung einschließlich Casting verwendet werden, id Referenzen kann wie jede Objektreferenz verwendet werden.

    Man kann das auch nicht selbst implementieren, denn man hat die Objekterzeugung nicht immer in der Hand. Man müsste also Cocoa neu schreiben. Abgesehen davon ist es blödsinnig, in einer Sprache etwas antikonzetionelles selbst zu implementieren, wenn es in einer anderen Sprache zum Konzept gehört.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?