cpp code unklar

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
lisan
Beiträge: 658
Registriert: 22.02.2003 19:05:04
Wohnort: Berlin
Kontaktdaten:

cpp code unklar

Beitrag von lisan » 02.07.2004 08:16:09

Hi,

Ich moechte klassen in c++ auf diese weise erzeugen

Das* das = createClass("Das");

Ein user einer lib, die ich schreibe, soll sich selbst klassen bauen, welche ich dann mit meiner lib instanziiere, der string "Das" kommt aus einem xml-config-file.

Ich bin auf folgenden code gestossen der das tun soll.

Code: Alles auswählen

/**
 * Register class. This defines a factory object which makes it possible
 * to create an object by the passing class name to the createOne() function.
 * The class must be a subclass of cPolymorphic, otherwise a compile-time error
 * will occur: <i>"cannot convert..."</i>
 *
 * @hideinitializer
 */
#define Register_Class(CLASSNAME) \

  cPolymorphic *CLASSNAME##__create() {return new CLASSNAME;} \
  EXECUTE_ON_STARTUP(CLASSNAME##__class, classes.instance()->add(new cClassRegister(#CLASSNAME,CLASSNAME##__create));)


/**
 * Allows code fragments to be collected in global scope which will
 * then be executed from main() right after program startup. This is
 * used by in OMNeT++ for building global registration lists of
 * module types, network types, etc. Registration lists in fact
 * are a simple substitute for Java's Class.forName() method...
 *
 * @hideinitializer
 */
#define EXECUTE_ON_STARTUP(NAME, CODE)  \
 static void __##NAME##_code() {CODE;} \
 static ExecuteOnStartup __##NAME##_reg(__##NAME##_code);

cPolymorphic *createOne(const char *classname)
{
    cClassRegister *p = (cClassRegister *)classes.instance()->get(classname);
    if (!p)
        throw new cException("Class \"%s\" not found -- perhaps its code was not linked in, or the class wasn't registered via Register_Class()", classname);
    return p->createOne();
}
class SIM_API cClassRegister : public cObject
{
...
    /**
     * Creates an instance of a particular class by calling the creator
     * function. The result has to be cast to the appropriate type
     * (preferably by dynamic_cast or check_and_cast).
     */
    cPolymorphic *createOne() const  {return creatorfunc();}
    //@}
};
die Methode createOne erzeugt das objekt.
Was ich nun nicht verstehe:
1. wozu wird immer wenn ich das program starte erstmal zu jeder klasse eine instanz erzeugt ?
Das macht das macro Register_Class().

Ich vermute, weil er so merkt, ob die Klasse existiert oder nicht, also ob eine instance von der Klasse "Das" vorliegt.

2. das macro EXECUTE_ON_STARTUP schafft es dass code direkt nach main ausgefuehrt wird, was ist das denn verruecktes ?

3. Was bedeuten die hashes in den macros # und ##.

Gruss,
Lisan.

p.s. ich hoffe die frage ist nicht ganz so off topic.

Benutzeravatar
bitbieger
Beiträge: 179
Registriert: 23.10.2003 08:26:00
Kontaktdaten:

Beitrag von bitbieger » 02.07.2004 11:10:29

Hi,

zu 1)
eine Factory funktioniert immer nach dem Schema:
"Ich weiss nicht, wie man eine Instanz erzeugt, aber ich weiß, wo es steht".

Darum wird auch beim Registrieren keine Instanz der jeweiligen Klasse erzeugt, sondern es wird ein Eintrag der Art: "Klasse A wird mit der Funktion X erzeugt" gemacht.

Stell dir folgende Klasse vor:

Code: Alles auswählen

class X
{
public:
   ...
   static X* create_new_instance()
   {
      return new X();
   }
};
Eine neue Instanz kann man jederzeit mit X::create_new_instance() erzeugen.
Die Factory braucht also nur einen Hinweis, dass man Klassen vom Typ "X" mit der o.g. Funktion erzeugen kann. Genau dafür ist die Klasse cClassRegister da.

zu 2)
Das Macro würde nach dem Präprozessordurchlauf für unsere Klasse X so aussehen:

Code: Alles auswählen

cPolymorphic *X__create() 
{
   return new X;
}
  
static void __X__class_code() 
{
   classes.instance()->add(new cClassRegister("X",X__create));
} 

static ExecuteOnStartup __X__class_reg(__X__class_code);
Es wird eine Funktion X__create() angelegt, analog zu unserer statischen create_new_instance() Funktion, um später eine Instanz der Klasse X erzeugen zu können. Außerdem wird eine Hilfsfunktion angelegt, die die Erzeugerfunktion (X__create()) bei der Factory registriert. Diese Hilfsfunktion wird dann an den Constructor einer Instanz der Klasse ExecuteOnStartup übergeben und aufgerufen. Da die Instanz der Klasse ExecuteOnStartup global ist, wird sie Direkt am Anfang des Programmablaufes angelegt.

zu 3)
In einem Macro werden Parameter mit # als einfacher String ersetzt, wogegen ## einfach eingefügt wird.

Code: Alles auswählen

#define BLA(X) printf( #X "=%s\n", ##X##->toString() )
BLA(pTest);
wird so zu

Code: Alles auswählen

printf( "pTest=%s\n", pTest->toString());
hth,
bitbieger

PS: Ich hoffe, die Erklärung ist halbwegs nachvollziebar. Ansonsten frag einfach noch mal genauer nach. Beschäftige dich doch mal mit Design Pattern...

Benutzeravatar
lisan
Beiträge: 658
Registriert: 22.02.2003 19:05:04
Wohnort: Berlin
Kontaktdaten:

Beitrag von lisan » 03.07.2004 18:45:51

Danke fuer deine erklaehrungen.

Eine frage hab ich dann noch.

Der code, den ich gelesen habe um factories kennen zu lernen, erzeugt beim registrieren ieine instanz der registrierten klasse. Diese instanz wird in einer liste abgelegt/

Immer wenn ich nun die create funktion aufrufe, durchsucht er ersteinmal diese liste nach einer klasse gleichen types, um zu erfahren ob sich instanzen dieser klasse erzeugen lassen.

Das finde ich recht aufwaendig. Ich erzeuge in einem simulationsprogramm leider
staendig instanzen.
Sollte das ganze nicht auch mit dyncast moeglich sein, herauszubekommen, ob man casten kann. Das prueft die create funktion leider durch testen der klasseninstanzenliste.

Und noch eine frage.

Kennst du ne gute doku zu c++-factories ?

Gruss,
Arvid.

Benutzeravatar
bitbieger
Beiträge: 179
Registriert: 23.10.2003 08:26:00
Kontaktdaten:

Beitrag von bitbieger » 04.07.2004 11:04:42

Hi lisan,

ich verstehe ehrlich gesagt nicht wirklich, was du meinst.
Immer wenn ich nun die create funktion aufrufe, durchsucht er ersteinmal diese liste nach einer klasse gleichen types, um zu erfahren ob sich instanzen dieser klasse erzeugen lassen.
Heisst das, du übergibst der Create-Funktion eine Klasseninstanz anhand derer eine neue Klasse erzeugt wird? Wenn das so wäre, wäre das Quatsch. In dem Fall sollte das entspr. Objekt eine Clone-Methode haben und Instanzen von sich selbst erzeugen können.

Aber vielleicht verstehe ich die Aussage ja auch falsch?!

Die ultimative Doku zu Design Patterns ist meiner Meinung nach:
"Design Patterns, Elements of Reusable Object-Oriented Software"
Erich Gamma, et al.
Addison-Wesley Verlag


Das Buch ist allerdings auf keine bestimmte Programmiersprache ausgelegt.

Ansonsten :google:

cu,
bitbieger

Benutzeravatar
lisan
Beiträge: 658
Registriert: 22.02.2003 19:05:04
Wohnort: Berlin
Kontaktdaten:

Beitrag von lisan » 04.07.2004 14:39:12

1. Danke, ich werd die woche mal anlesen.

2. Ich hatte mich unklar ausgedrueckt.
Der code den ich meine bietet mir ein makro namens Register_class(xy) an

erstelle ich eine Klasse z.B. Worm sieht der code etwa so aus.

Code: Alles auswählen


#include <macros.h>
#include <Worm.h>

Register_class(Worm);

Worm::Worm() {
...
}
Das Register_makro erstellt eine instanz der klassw Worm und fuegt sie in eine liste ein.
Immer wenn ich nun die Methode createOne aus meinem ersten post

Code: Alles auswählen

cPolymorphic *createOne(const char *classname)
{
    cClassRegister *p = (cClassRegister *)classes.instance()->get(classname);
    if (!p)
        throw new cException("Class \"%s\" not found -- perhaps its code was not linked in, or the class wasn't registered via Register_Class()", classname);
    return p->createOne();
} 
aufrufe um eine Klasse zu erstellen anhand des Strings z.B. "Worm" durchsucht er die besagte liste, ob es diese Klasse ueberhaupt gibt. (das macht die get methode des obigen codes)

Meine Frage, da ich von factories wie du sicher bemerkt hast noch keine ahnung habe:
Kann man das nicht anders loesen eine Instanz einer Klasse nach ihrem String namen mit einer factory zu erzeugen ohne, dass man in einer u.a. ewig langen liste suchen muss, ob es diese klasse ueberhaupt gibt ?

Allerdings muss man das ja eigendluch garnicht pruefen, da ja beim kompilieren das makro beim erzeugen der create Methode rumschreien muesste, dass es die Klasse nicht gibt.

z.B. durch tippfehler ala:
createOne(Wom)
statt
createOne(Worm)

Gruss,
Arvid.

Benutzeravatar
bitbieger
Beiträge: 179
Registriert: 23.10.2003 08:26:00
Kontaktdaten:

Beitrag von bitbieger » 04.07.2004 14:50:56

Hi Arvid,

die Funktion createOne() sucht aus der Liste eine Instanz der Klasse cClassRegister . Diese wiederum ist in der Lage eine Instanz deiner Klasse zu erzeugen. Da die Liste bestimmt eine map ist, sollte die Suche eigentlich sehr schnell sein.

Ist der Code, den du meinst, nicht der Code aus deinem 1. Post?

cu,
bitbieger

Benutzeravatar
lisan
Beiträge: 658
Registriert: 22.02.2003 19:05:04
Wohnort: Berlin
Kontaktdaten:

Beitrag von lisan » 04.07.2004 18:11:09

Ach jaa, stimmt, irgendwer muss ja die methode zum erstellen einer klase x kennen, also die klasse x selbst *indietischplattebeiss*.

Ich ueberleg mir mal, wie ich das schneller hinbekomme aber erstmal lese ich was dazu.

Danke schoen.

Gruss,
Arvid.

Antworten