[C++] Bedingte Funktion in Template-Klasse

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

[C++] Bedingte Funktion in Template-Klasse

Beitrag von Schrompf »

Guten Tag,

ich möchte je nach Template-Parameter in einer Klasse eine Funktion drin haben oder auch nicht. Also sowas in der Art:

Code: Alles auswählen

template <typename T, bool ja>
class Bla {

  ...Magic happens here...
  if( ja )
    virtual void TuWas();
};
Wie baue ich sowas? Die Funktion darf im Nein-Fall wirklich nicht existieren, da ihre reine Existenz dafür sorgt, dass der Compiler in einer großen Reihe aus Klassen, Ableitungen und Komponenten überall die Erstellung eines Kopierkonstruktors erzwingt und damit nach gefühlt 20 Zwischenstufen eine Klasse erreicht, die von boost::noncopyable ableitet. Ich brauche von dieser Template-Klasse also zwei Versionen: eine mit dieser Funktion und eine ohne. Im schlimmsten Fall muss ich den Code halt unter anderem Namen duplizieren, aber da wir hier von einigen hundert Zeilen reden, dache ich, ich frage vorher besser mal die Profis.

Danke im Voraus!
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
B.G.Michi
Establishment
Beiträge: 163
Registriert: 07.03.2006, 20:38
Alter Benutzername: B.G.Michi
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von B.G.Michi »

Das virtual macht Probleme. Ohne würde es mit C++11 und Default-Function-Template-Argumenten funktionieren. Aber vielleicht kannst du dir daraus was basteln:

Code: Alles auswählen

template<bool ja>
class bla {
    virtual void TuWasVirtual();

    template<typename = typename enable_if<ja>::type>
    inline void TuWas() { TuWasVirtual(); }
};
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von CodingCat »

Eventuell kannst du mit folgendem davonkommen:

Code: Alles auswählen

template <typename T, bool ja>
class Bla {
  ...Magic happens here...
};
template <typename T>
class Bla<T, true> : public Bla<T, false> {
  virtual void TuWas();
};
Das wirft allerdings offensichtlich die Frage auf, ob man das ganze nicht besser gleich ohne Templateparameter in zwei entsprechend verwandte Klassen zerlegt. Hängt vom Kontext ab.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von Schrompf »

Danke für den Hinweis auf enable_if! Damit sollte es laut boost-Seite auch für nicht-C++11-Compiler klappen - man muss halt einen Dummy-Parameter nehmen anstatt des template-Parameters. Leider finde ich den Boost type_traits nichts, was die Existenz und Erreichbarkeit eines Kopierkonstruktors prüft. es gibt has_trivial_copy_constructor und Artverwandte, aber kein schlichtes has_copy_constructor oder is_copyable. Grmpf.

Nunja, mit einem manuellen Bool als template-Parameter sollte es trotzdem gehen. Mal ausprobieren.

@CodingCat: Danke auch für Deinen Hinweis. Ich kann natürlich zwei Klassen draus machen, aber dazu muss ich wie gesagt drei Klassen mit insgesamt ~500 Zeilen Code duplizieren, die darauf angepassten Helfer-Makros verdoppeln und an jeder Verwendungsstelle entscheiden, ob ich die Version mit oder ohne diese Funktion haben will. Die Version hatte ich gerade, und sie ist hässlich. [edit] Und ableiten geht auch nicht, weil die Ableitung eine interne MemberVar mit angepasstem Typ braucht.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von CodingCat »

Ja, wenn das tatsächlich von Typen/Compile-Zeit-Konstanten abhängt, ist ein Template-Parameter natürlich der richtige Weg.
Schrompf hat geschrieben:Und ableiten geht auch nicht, weil die Ableitung eine interne MemberVar mit angepasstem Typ braucht.
Bezieht sich das auf den geposteten generellen Lösungsansatz? Wenn ja, dann musst du das ggf. nochmal genauer erläutern, sollte das Problem weiter bestehen.

In wieweit enable_if hier hilft, bin ich mir btw. auch nicht schlüssig, weil der Code für virtual-Methoden wohl immer erzeugt wird.
Zuletzt geändert von CodingCat am 19.06.2012, 19:49, insgesamt 1-mal geändert.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von Schrompf »

Vielleicht denke ich aber auch zu komplex. Die eigentliche Aufgabe des Codes ist eine Class Factory. Die sieht in Kurzform so aus:

Code: Alles auswählen

template <typename Basis> class FabrikHelferBasis
{
  virtual Basis* ErzeugeObjekt() const = 0;
};

template <typename Basis, typename Ableitung> class FabrikHelferAbleitung
  : public FabrikHelferBasis<Basis>
{
  Basis* ErzeugeObjekt() const override { return new Ableitung(); }
}

template <typename Basis> class Fabrik
{
  std::map<std::string, FabrikHelferBasis<Basis>*> mErzeuger;

  void AddKlasse( const std::string& name, FabrikHelferBasis* erzeuger) { mErzeuger[name] = erzeuger; }
  void ErzeugeInstanz( const std::string& name) const { return mErzeuger[name]->ErzeugeObjekt(); }
};
Beispiel gekürzt um Fehlerbehandlung und all den Drumrum-Kram. Aber es sollte klar werden, was es werden soll. In dieser Klasse habe ich vor langer Zeit zusätzlich eine Funktion "Basis* FabrikHelferBasis::ErzeugeKopie( const Basis*)" eingeführt, die nun aber mit jeder Klasse Ärger macht, die nicht kopiert werden kann. Rausnehmen kann ich sie aber auch nicht, weil ich regelmäßig ganze Ableitungshierarchien kopieren muss. Da wär es doch prima, wenn man von dem Klassenverbund automatisch eine kopie-fähige Version bekäme, wenn die Traits der Basisklasse eine Kopie erlauben.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von CodingCat »

Hm, ich verstehe noch nicht ganz, was du am Ende haben willst. Unanhängig, wie du die Sache angehst (ob per Templates oder seperater Klassen), entstehen doch am Ende nach Instantiierung immer neue Klassenhierarchien, wobei manche eine Kopiermethode enthalten, andere nicht. Um eine wie auch immer geartete Spezialisierung der drei geposteten Klassen kommst du damit eigentlich nicht drumrum. Wenn es dir nur um die Automatisierung geht, sähe die Lösung wohl irgendwie so aus:

Code: Alles auswählen

template <typename Basis, bool Kopierbar> class FabrikHelferBasisDefinition
{
  virtual Basis* ErzeugeObjekt() const = 0;
}
template <typename Basis> class FabrikHelferBasisDefinition<Basis, true> : public FabrikHelferBasisDefinition<Basis, false>
{
  virtual Basis* KopiereObjekt(const Basis*) const = 0;
}
template <typename Basis> class FabrikHelferBasis
   : public FabrikHelferBasisDefinition<Basis, Kopierbar<Basis>::Wert>
{ }

template <typename Basis, typename Ableitung, bool Kopierbar> class FabrikHelferAbleitungImplementierung
  : public FabrikHelferBasis<Basis>
{
  Basis* ErzeugeObjekt() const override { return new Ableitung(); }
}
template <typename Basis, typename Ableitung> class FabrikHelferAbleitungImplementierung<Basis, Ableitung, true>
  : public FabrikHelferAbleitungImplementierung<Basis, Ableitung, false>
{
  Basis* KopiereObjekt(const Basis*o) const override { return new Ableitung(o); }
}
template <typename Basis, typename Ableitung> class FabrikHelferAbleitung
  : public FabrikHelferAbleitungImplementierung<Basis, Ableitung, Kopierbar<Basis>::Wert>
{ }

template <typename Basis, bool Kopierbar> class FabrikImplementierung
{
  std::map<std::string, FabrikHelferBasis<Basis>*> mErzeuger;

  void AddKlasse( const std::string& name, FabrikHelferBasis<Basis>* erzeuger) { mErzeuger[name] = erzeuger; }
  void ErzeugeInstanz( const std::string& name) const { return mErzeuger[name]->ErzeugeObjekt(); }
};
template <typename Basis> class FabrikImplementierung<Basis, true> : public FabrikImplementierung<Basis, false>
{
  void KopiereInstanz(const Basis *o) const { return mErzeuger[name]->KopiereObjekt(o); }
};
template <typename Basis> class Fabrik
  : public FabrikImplementierung<Basis, Kopierbar<Basis>::Wert> { };
Und ja, das ist hässlich, bedingte Methoden wären schöner. Aber C++ Templates sind nunmal minimalistisch, redundant und umständlich.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von Schrompf »

Danke für Deine Geduld! Ich dachte mir, es ginge auch einfacher. So in etwa:

Code: Alles auswählen

template <typename Basis> class FabrikHelferBasis
{
  virtual Basis* ErzeugeObjekt();
  virtual Basis* KopiereObjekt( const Basis* vorlage, boost::enable_if<std::is_copy_constructible<Basis>::value, int>::type dummy = 0);
};
Der Gedanke ist, dass diese Funktion dann nur existiert, wenn is_copy_constructible zu true auswertet. Leider ist is_copy_constructible eine C++11-Erweiterung, die noch nicht in Visual Studio enthalten ist. Boost hat sowas auch nicht, wohl aus dem Grund, dass die Standardlib es jetzt drin hat. Grmpf. Ich habe nicht genug Ahnung von Templates, um mir das selbst zu bauen, und ich finde keinen Code im Netz dafür.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von CodingCat »

Schrompf hat geschrieben:Danke für Deine Geduld! Ich dachte mir, es ginge auch einfacher. So in etwa:

Code: Alles auswählen

template <typename Basis> class FabrikHelferBasis
{
  virtual Basis* ErzeugeObjekt();
  virtual Basis* KopiereObjekt( const Basis* vorlage, boost::enable_if<std::is_copy_constructible<Basis>::value, int>::type dummy = 0);
};
Nein, das geht leider aus einer ganzen Reihe von Gründen nicht: Die Deklarationen von Nicht-Template-Methoden müssen (sinnvollerweise) immer voll übersetzbar sein, unabhängig von deren Instantiierung (die implizit erst bei der ersten Benutzung stattfindet). Damit ist das ganze Klassen-Template für nicht kopierbare Basis-Typen gar nicht kompilierbar. Damit es kompilierbar würde, müsstest du aus KopiereObjekt eine Template-Methode machen. In diesem Fall käme eine Fehlermeldung erst beim ersten Aufruf von KopiereObjekt, wäre die Methode nicht virtual. Virtuelle Template-Methoden kann es offensichtlich nicht geben, weil für solche nicht mal ein endlicher V-Table im Voraus berechenbar wäre. Ich sehe tatsächlich keine elegantere Lösung als die, die ich bereits gepostet habe.
Schrompf hat geschrieben:Der Gedanke ist, dass diese Funktion dann nur existiert, wenn is_copy_constructible zu true auswertet. Leider ist is_copy_constructible eine C++11-Erweiterung, die noch nicht in Visual Studio enthalten ist. Boost hat sowas auch nicht, wohl aus dem Grund, dass die Standardlib es jetzt drin hat. Grmpf. Ich habe nicht genug Ahnung von Templates, um mir das selbst zu bauen, und ich finde keinen Code im Netz dafür.
Eigentlich bringt dir das in diesem Fall eh nichts, weil du anhand der Basisklasse die Kopierbarkeit entscheiden musst, is_copy_constructible könnte das allenfalls für die implementierenden Typen. Hier musst du wohl auf die manuelle Spezialisierung von Tag-Templates ausweichen:

Code: Alles auswählen

// Standard: Klasse kopierbar, wenn nicht von irgendeiner (boost?) noncopyable abgeleitet
template <typename Class>
struct tagged_copyable { static const bool value = !std::is_base_of<boost::noncopyable, Class>::value; };
// Manuelles Tweaking möglich:
template <>
struct tagged_copyable<SpecificNoncopyableClass> { static const bool value = false; };
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von dot »

Wäre es möglich, die Funktion in eine extra Klasse auszulagern und von dieser zu erben?

Um was für ein Template handelt es sich da konkret? Vielleicht gibt es für dein Problem überhaupt eine bessere Alternative...
Benutzeravatar
BeRsErKeR
Establishment
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von BeRsErKeR »

Vielleicht hab ich das Problem nicht ganz verstanden, aber wieso nicht einfach so:

Code: Alles auswählen

#include <iostream>

class MyHelperClass
{
public:
	virtual void MyFunc(void) { std::cout << "test" << std::endl; }
};

template <typename T, bool cp>
class MyClass
{
};

template <typename T>
class MyClass<T, true> : public MyHelperClass
{
};

template <typename T, bool cp>
class MyFinalClass : public MyClass<T, cp>
{
	// hier kommt der Rest rein
};

int main(int argc, char **argv)
{
	MyFinalClass<int, false> x;
	MyFinalClass<int, true> y;

	//x.MyFunc(); -> error C2039
	y.MyFunc();

	return 0;
}
Falls man die virtuelle Methode überschreiben will braucht man halt entsprechend eine Zwischenstufe mit einer Spezialisierung auf die Version mit 'true', in der man dann die Methode überlädt. Die nächste Ebene kann dann wieder munter parameterunabhängig erben. So wie im Beispiel MyFinalClass von MyClass.

In der Spezialisierung auf 'true' von MyClass könnte man z.B. die virtuelle Methode überschreiben.
Ohne Input kein Output.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von Schrompf »

Nochmal Danke für die Hilfe! Das eigentliche Problem hatte ich ja ein paar Beiträge weiter oben beschrieben: es geht um eine Class Factory, die auch die Möglichkeit anbieten muss, ein Objekt zu kopieren. Dazu wird über einen Mechanismus, der hier nicht zum Thema gehört, der Type des Vorlageobjekts bestimmt, damit die dazugehörige Ableitung des ObjektErzeugeHelfers bestimmt und der erstellt dann mittels Kopierkonstruktor eine eine Instanz diesen Typs.

Und nun ist das Problem, dass manche Klassen nicht kopierbar sind. Hier konkret das Problem, dass boost::signal nicht kopierbar ist und damit auch alle Klassen, die Signale haben. Also sollte die Klassenfabrik selbsttätig herausbekommen, ob die Basisklasse kopierbar ist, und die Kopier-Funktion danach anbieten oder auch nicht. An der Stelle nochmal Danke an CodingCat und seine geduldigen Erklärungen. Der Compiler kann natürlich nicht wissen, ob eine Ableitung der Basisklasse irgendwo ein Signal enthält. Daher ist es immernoch meine Pflicht, in dem Fall die Basisklasse als "nicht kopierbar" zu markieren. Und da ich das eh immer mit boost::noncopyable mache, kann ich auch einfach prüfen, ob die Klasse davon ableitet. CodingCats Vorschlag folgend sieht die Gesamtkonstruktion jetzt so aus:

Code: Alles auswählen

 namespace FabrikHelfer {
  /// FabrikHelfer-Klasse
  template <typename Basisklasse> class BasisHelfer  {
    virtual Basisklasse* ErzeugeObjekt() const = 0;
    virtual Basisklasse* KopiereObjekt( const Basisklasse* pObjekt) const { return nullptr; };
  };

  /// Template-Helfer zur Unterscheidung zwischen kopierbaren und nicht kopierbaren Klassen
  template <typename Basisklasse, typename Klasse, bool IstKopierbar> class HelferDef
    : public BasisHelfer<Basisklasse>
  {  };
  template <typename Basisklasse, typename Klasse> class HelferDef<Basisklasse, Klasse, true>
    : public HelferDef<Basisklasse, Klasse, false>
  { 
    Basisklasse* KopiereObjekt( const Basisklasse* pObjekt) const override { return new Klasse( *(static_cast<const Klasse *> (pObjekt))); };
  };

  /// Helferobjekt für Klassenregistrierung bei einer Objektfabrik
  template <class Basisklasse, class Klasse> class Helfer 
    : public HelferDef<Basisklasse, Klasse, !std::is_base_of<boost::noncopyable, Basisklasse>::value>
  {
    Basisklasse* ErzeugeObjekt() const override { return new Klasse(); }
  };
}

/// die eigentliche Class Factory
template <class Basisklasse> class Fabrik
{
protected: 
  // Sammlung aller Erzeuger-Helferobjekte anhand des Namens der erzeugten Klasse
  std::map< std::string, Traum_FabrikHelfer::BasisHelfer<Basisklasse> *> mErzeuger;

public:
  /// Registriert eine neue Klasse mit Name und Erzeuger.
  void AddKlasse( const std::string& pName, Traum_FabrikHelfer::BasisHelfer<Basisklasse> *pKlassenhelfer) 
  {
    mErzeuger[pName] = pKlassenhelfer;
  }

  /// Erzeugt ein Objekt der gegebenen Klasse. 
  Basisklasse* ErzeugeObjekt( const std::string &pKlassenname) const 
  { 
    return mErzeuger[pKlassenname]->ErzeugeObjekt();
  }

  /// Erzeugt eine Kopie des übergebenen Objekts. 
  Basisklasse* KopiereObjekt( const Basisklasse* pObjekt) const 
  { 
    auto derRichtigeFabrikHelfer = IrrelevanteFunktion( pObjekt);
    return derRichtigeFabrikHelfer->KopiereObjekt( pObjekt);
  }
};
Wie man sieht, ist aktuell ein Runtime-Fehler draus geworden: wenn die Basisklasse nicht kopierbar ist, wird die KopiereObjekt()-Funktion nicht überschrieben und die Basis-Implementation gibt halt einfach nullptr zurück. Das werde ich jetzt noch korrigieren, indem ich den Ableitung-Mit-TemplateBool-Trick auch auf die Fabrik selbst anwende. Dann müsste es ein Compiler-Fehler sein, wenn ich die KopiereObjekt()-Funktion einer Fabrik aufrufe, deren Klasse nicht kopierbar ist.

Danke!
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von dot »

Hab ich das richtig verstanden: Du hast Objekte, die von einer, aus einem Template generierten, Factory erzeugt werden!? Manchmal sind diese Objekte kopierbar, manchmal nicht!?
Wieso genau muss die Factory das Kopieren übernehmen? Kannst du nicht einfach den Objekten eine clone() Methode geben? Dann braucht die Factory überhaupt nicht wissen ob die Objekte, die sie erzeugt, kopierbar sind oder nicht. Imo sollte sowas auch rein prinzipiell eher zum Objekt als zur Factory...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von Schrompf »

dot hat geschrieben:Hab ich das richtig verstanden: Du hast Objekte, die von einer, aus einem Template generierten, Factory erzeugt werden!? Manchmal sind diese Objekte kopierbar, manchmal nicht!?
Exakt.
Wieso genau muss die Factory das Kopieren übernehmen? Kannst du nicht einfach den Objekten eine clone() Methode geben? Dann braucht die Factory überhaupt nicht wissen ob die Objekte, die sie erzeugt, kopierbar sind oder nicht. Imo sollte sowas auch rein prinzipiell eher zum Objekt als zur Factory...
Das ist wohl Meinungsfrage. Ich empfinde das als Java-Denkweise. Ich möchte nicht durch jedes Objekt durchgehen und eine clode-()-Methode implementieren, die für jede Klasse gleich aussieht bis auf den Typ-Namen. Was macht man denn als Programmierer, wenn der Code bis auf den Typnamen immer der selbe ist? Man zieht es in ein Template raus. Und genau das ist hier passiert.

Man kann sich streiten, ob die Klon-Fähigkeit einer Factory schon eine eigene Klasse wert ist. Ich persönlich denke, dass sich das Klonen von Objekten, deren Typ erst zur Laufzeit bekannt ist, eigentlich nur im Konstruktorparameter von einer normalen Factory unterscheidet. Daher ursprünglich auch der Gedanke, diese Funktion mit in die Factory aufzunehmen. Nur erzwingt halt die abstrakte Implementation dann auch gleich die Kopierbarkeit bei allen Typen, die man in der Factory verwenden will. Und das war das eigentliche Problem, das es zu lösen galt.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von dot »

Wenn du mich fragst, ist die Factory haargenau der falsche Ort für sowas. Kopierbarkeit ist eine Eigenschaft des Typs und sollte sich daher auch im Interface des Typs widerspiegeln und nicht in irgendeiner Factory. Eine clone() Methode ist imo die simple und natürliche Lösung für dieses Problem...
Zuletzt geändert von dot am 20.06.2012, 15:35, insgesamt 1-mal geändert.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von Schrompf »

Wie gesagt, Ansichtssache. Du kannst gern clone() in jeder Klasse neu implementieren... ganz wie Du magst.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von dot »

Du musst doch den Kopierkonstruktor sowieso implementieren, das kann die Factory rein prinzipiell scho nicht tun!? Ein weiteres Zeichen dafür, wo derartige Dinge eigentlich hingehören...

Wenn es dir wirklich nur darum geht, die clone Methode nicht tippen zu müssen, dann eben sowas:

Code: Alles auswählen

template <class T>
class Cloneable
{
protected:
  Cloneable() {}
  Cloneable(const Cloneable&) {}
  ~Cloneable() {}
public:
  virtual T* clone(const T& prototype)
  {
    return new T(prototype);
  }
};


class MyThing : public Cloneable<MyThing>
{
  // ...
};
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von Schrompf »

dot hat geschrieben:Du musst doch den Kopierkonstruktor sowieso implementieren, das kann die Factory rein prinzipiell scho nicht tun!?
Nein, muss ich nicht, der compiler-generierte reicht in den meisten Fällen aus.
Wenn es dir wirklich nur darum geht, die clone Methode nicht tippen zu müssen, dann eben sowas
Wie verhält sich dieser Code für abgeleitete Klassen? Nur rein interessehalber, ich habe ja wie gesagt schon Code dafür. Ich vermute, man müsste virtual ableiten, sonst würde in jeder Ableitung ein neuer Eintrag zum vtable dazukommen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von dot »

Schrompf hat geschrieben:
dot hat geschrieben:Du musst doch den Kopierkonstruktor sowieso implementieren, das kann die Factory rein prinzipiell scho nicht tun!?
Nein, muss ich nicht, der compiler-generierte reicht in den meisten Fällen aus.
fair enough... ;)
Schrompf hat geschrieben:
Wenn es dir wirklich nur darum geht, die clone Methode nicht tippen zu müssen, dann eben sowas
Wie verhält sich dieser Code für abgeleitete Klassen? Nur rein interessehalber, ich habe ja wie gesagt schon Code dafür. Ich vermute, man müsste virtual ableiten, sonst würde in jeder Ableitung ein neuer Eintrag zum vtable dazukommen.
Richtig, man müsste die Implementierung und das Interface trennen und virtual ableiten, da man sonst haufenweise verschiedene clone Methoden erben würde:

Code: Alles auswählen

template <class T>
class ICloneable
{
public:
  virtual T* clone() const = 0;
};

template <class T, class A>
class Cloneable : public virtual ICloneable<T>
{
public:
  virtual T* clone() const
  {
    return new A(*static_cast<const A*>(this));
  }
};


class Base : public virtual ICloneable<Base>
{
};

class MyThing : public Base, public Cloneable<Base, MyThing>
{
  // ...
};

int main()
{
  MyThing t;

  Base* x = t.clone();
}
Ich finde die Lösung über das Prototype Pattern jedenfalls wesentlich schöner, schon allein da das Liskovsche Substitutionsprinzip nicht verletzt wird (was bei deiner Factory Copy Operation dagegen der Fall ist)...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4887
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Bedingte Funktion in Template-Klasse

Beitrag von Schrompf »

Hm, ich würde dann wahrscheinlich auf was Plumpes wie z.B. ein Makro ausweichen. Mit C++11 könnte man da evtl. sogar ein bisschen was sparen.

Code: Alles auswählen

#define MAKE_CLONE auto clone() const -> decltype( this)  { return new decltype(*this) (*this); }
Keine Ahnung, ob sowas klappen könnte. Evtl. ist this ja dort außerhalb der Funktion gar nicht definiert. Nuja, war nur so ein Gedanke. Ansonsten muss man die Klasse halt als Makro-Parameter reinreichen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Antworten