Factory & Reflection
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Factory & Reflection
Ein Problem im Zusammenhang mit http://zfx.info/viewtopic.php?f=7&t=794 aber es ist eigentlich ein getrenntes Thema.
Ich möchte meinen Entitys Komponenten hinzufügen können. Dafür muss ich ein Objekt einer Komponentenunterklasse erstellen, dessen Typ ich anhand eines Strings bestimme (damit ich das dynamisch zur Laufzeit machen kann). Jetzt ist die Frage, wie programmiere ich am besten eine Klasse, die einen String bekommt und das entsprechende Objekt erzeugt? (Die Klassen haben alle die selbe Oberklasse) Ich möchte natürlich möglichst einfach neue Klasse hinzufügen können, das sollte nicht mehr als eine Zeile benötigen.
Die einfachste Lösung wäre ein großes if/elseif aber das sieht hässlich aus und ich muss die Implementierung immer ändern.
Bis jetzt möchte ich das Prototypmuster benutzen. beim registrieren übergebe ich den Namen als String und ein Objekt der Klasse, beim erstellen suche ich den String aus einer map raus und kopiere das Objekt, das mit in der Map liegt und gebe das dann zurück. Ich kann mit einem Funktionsaufruf neue Klassen registrieren und muss nie die Implementierung meiner Factory ändern, was beides sehr schön ist, allerdings muss ich dafür sorgen, dass jede Komponentenklasse kopierbar ist.
Mein zweites Problem ist, dass sinnvolle bearbeiten von Komponenten im Editor. Ich würde gerne wxPropertyGrid benutzen, das Problem ist, dass ich dafür ja alle GUI Elemente erzugen müsste. Ferner will ich natürlich Komponenten laden und speichern können, das sind eine Menge simpler Funktionen, die ich eigentlich nicht selber schreiben will. Ich brauche als Reflection für C++.
Meine bisherige Idee ist, ein paar Makros zu definieren, die ich dann für jede Memberdeklaration benutze. Sie erstellen einerseits den Member mit dem normalen Namen, fügt aber andererseits dessen Adresse in eine Liste ein. So kann ich im Spiel ohne performanceverlus alle Member ganz normal nutzen, aber trotzdem über sie iterieren. Wie genau ich das mit der Liste jetzt mache, weiß ich allerdings noch nicht, man kann ja während der Deklaration nicht direkt Code ausführen, und void* Zeiger reichen sicherlich auch nicht aus.
Ich hätte also gerne Meinungen zu meiner Factory Planung und konkrete Vorschläge für Reflection.
Ich möchte meinen Entitys Komponenten hinzufügen können. Dafür muss ich ein Objekt einer Komponentenunterklasse erstellen, dessen Typ ich anhand eines Strings bestimme (damit ich das dynamisch zur Laufzeit machen kann). Jetzt ist die Frage, wie programmiere ich am besten eine Klasse, die einen String bekommt und das entsprechende Objekt erzeugt? (Die Klassen haben alle die selbe Oberklasse) Ich möchte natürlich möglichst einfach neue Klasse hinzufügen können, das sollte nicht mehr als eine Zeile benötigen.
Die einfachste Lösung wäre ein großes if/elseif aber das sieht hässlich aus und ich muss die Implementierung immer ändern.
Bis jetzt möchte ich das Prototypmuster benutzen. beim registrieren übergebe ich den Namen als String und ein Objekt der Klasse, beim erstellen suche ich den String aus einer map raus und kopiere das Objekt, das mit in der Map liegt und gebe das dann zurück. Ich kann mit einem Funktionsaufruf neue Klassen registrieren und muss nie die Implementierung meiner Factory ändern, was beides sehr schön ist, allerdings muss ich dafür sorgen, dass jede Komponentenklasse kopierbar ist.
Mein zweites Problem ist, dass sinnvolle bearbeiten von Komponenten im Editor. Ich würde gerne wxPropertyGrid benutzen, das Problem ist, dass ich dafür ja alle GUI Elemente erzugen müsste. Ferner will ich natürlich Komponenten laden und speichern können, das sind eine Menge simpler Funktionen, die ich eigentlich nicht selber schreiben will. Ich brauche als Reflection für C++.
Meine bisherige Idee ist, ein paar Makros zu definieren, die ich dann für jede Memberdeklaration benutze. Sie erstellen einerseits den Member mit dem normalen Namen, fügt aber andererseits dessen Adresse in eine Liste ein. So kann ich im Spiel ohne performanceverlus alle Member ganz normal nutzen, aber trotzdem über sie iterieren. Wie genau ich das mit der Liste jetzt mache, weiß ich allerdings noch nicht, man kann ja während der Deklaration nicht direkt Code ausführen, und void* Zeiger reichen sicherlich auch nicht aus.
Ich hätte also gerne Meinungen zu meiner Factory Planung und konkrete Vorschläge für Reflection.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
https://jonathank.de/games/
- Krishty
- Establishment
- Beiträge: 8264
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Factory & Reflection
Hi,
Code ist hingehackt, darum keine Gewähr auf Korrektheit oder Vollständigkeit. Ansonsten aber sparsamer, schneller und vom Konzept her intuitiver als Default-Objekte.
Gruß, Ky
Das ist eigentlich schon ein schöner Ansatz. Statt in der Map Default-Objekte zu speichern, könntest du Zeiger auf die Funktionen speichern, die dir so ein Objekt erzeugen:Jonathan hat geschrieben:beim registrieren übergebe ich den Namen als String und ein Objekt der Klasse, beim erstellen suche ich den String aus einer map raus und kopiere das Objekt, das mit in der Map liegt und gebe das dann zurück. Ich kann mit einem Funktionsaufruf neue Klassen registrieren und muss nie die Implementierung meiner Factory ändern, was beides sehr schön ist, allerdings muss ich dafür sorgen, dass jede Komponentenklasse kopierbar ist.
Code: Alles auswählen
// Schnittstelle
class IComponent;
// Factory
class CFactory {
public:
// Typ eines Funktionszeigers auf eine Create-Funktion - gibt IComponent & zurück, wird als ComponentCreationFunction angesprochen und erwartet ein int als Parameter
typedef IComponent & (*ComponentCreationFunction)(int);
// Hier registrieren sich neue Komponenten-Implementierungen. Könnte private sein mit Friendship oder so, deine Entscheidung.
void RegisterComponent(
string ItsName,
ComponentCreationFunction ItsCreationFunction
) {
MyMap.insert(ItsName, ItsCreationFunction);
}
// Die Factory-Funktion. Interessante Frage: const oder nicht? Verändert sich der logische Zustand einer Fabrik, wenn sie etwas produziert hat?
IComponent & CreateComponent(
string ItsName,
int ItsDummyParameter
) {
if(MapIterator const ToComponent = MyMap.find(ItsName) != MyMap.end()) // Variablendeklaration in if – äußerst nützlich
return (*ToComponent->second)(ItsDummyParameter); // ruft die hinterlegte Funktion auf und gibt ihr Ergebnis zurück
else
throw Blubb;
}
}; // class CFactory
// Eine Implementierung
class CComponentA : public IComponent {
...
private:
// Die Factory-Funktion dieser Implementierung
static // darf natürlich kein this erwarten, sonst wird es auch mit den Funktionszeigern schwieriger
CComponentA & Create(
int DummyParameter
) {
return *(new CComponentA(DummyParameter));
}
public:
// Konstruktor.
explicit // weil er nur einen Parameter hat
CComponentA(
int DummyParameter // nur dazu da, damit du siehst, wie man mit Funktionszeigern und Parametern umgeht, falls du das brauchst
) … }
// Trägt diese Klasse in der gegebenen Factory ein. Deine Entscheidung, wann und wo und wie.
static // an keine Instanz gebunden
void RegisterAt(
CFactory & Factory
) {
Factory.AddComponent("A", &Create); // Funktionszeiger hinterlegen – unnütze Information:
// Das & vor Create kannst du auch weglassen. Falls die Zielfunktion ein Template ist, solltest du das sogar, weil
// der Visual C++-Compiler einen Bug innehat und Template-Funktionszeiger nicht optimiert, wenn man ein & davor schreibt.
}
}; // class CComponentA
Gruß, Ky
Re: Factory & Reflection
Was ein wenig unschön daran ist, ist dass die Objekte dann wissen müssen, dass sie von einer Fabrik erstellt werden. Bei der anderen Lösung müssen sie einfach nur kopierbar sein, und ich denke, wenn man ein Objekt, das nur default Werte hat, kopiert, sollte das nicht wirklich langsamer sein, als es neu zu erstellen.
Hat noch jemand Ideen bezüglich Reflection?
Hat noch jemand Ideen bezüglich Reflection?
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
https://jonathank.de/games/
- Krishty
- Establishment
- Beiträge: 8264
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Factory & Reflection
Kannst den Erzeugungs-Code natürlich auch von der Komponente ins globale Namespace verlagern, solange dort der Konstruktor verfügbar ist:Jonathan hat geschrieben:Was ein wenig unschön daran ist, ist dass die Objekte dann wissen müssen, dass sie von einer Fabrik erstellt werden.
Code: Alles auswählen
template <
typename CComponent
> IComponent & Create(
int DummyParameter
) {
return *(new CComponent(DummyParameter));
}
Factory.RegisterComponent("A", &Create<CComponentA>);
Re: Factory & Reflection
Ist schon lange her das ich mit wxWindows (wxWidgets) was gemacht habe, ich meine mich aber dunkel daran zu erinnern das es da ne Menge Macros gab um zur Laufzeit Infos über die Objekte und Methoden zu bekommen.Jonathan hat geschrieben: Mein zweites Problem ist, dass sinnvolle bearbeiten von Komponenten im Editor. Ich würde gerne wxPropertyGrid benutzen, das Problem ist, dass ich dafür ja alle GUI Elemente erzugen müsste. Ferner will ich natürlich Komponenten laden und speichern können, das sind eine Menge simpler Funktionen, die ich eigentlich nicht selber schreiben will. Ich brauche als Reflection für C++.
Meine bisherige Idee ist, ein paar Makros zu definieren, die ich dann für jede Memberdeklaration benutze. Sie erstellen einerseits den Member mit dem normalen Namen, fügt aber andererseits dessen Adresse in eine Liste ein. So kann ich im Spiel ohne performanceverlus alle Member ganz normal nutzen, aber trotzdem über sie iterieren. Wie genau ich das mit der Liste jetzt mache, weiß ich allerdings noch nicht, man kann ja während der Deklaration nicht direkt Code ausführen, und void* Zeiger reichen sicherlich auch nicht aus.
Re: Factory & Reflection
Also so ganz bekomme ich das noch nicht hin:
Im Prinzip speicher ich in der Map Funktionszeiger die ich je nach Key aufrufe. Damit ich diese erstellungsfunktionen nicht selber schreiben muss, will ich das mit dem Template mahen, der Compiler müsste dann ja bei jedem Registeraufruf eine entsprechende Funktion mit dem Typ erstellen.
Allerdings kriege ich jetzt die Syntax nicht ganz hin, was muss ich jetzt statt dem BLA jeweils schreiben (2 mal in Register und in der map Deklaration)?
Code: Alles auswählen
#include <map>
template <class tClass> tClass* FactoryWorker()
{
return new tClass();
}
template<class tBaseClass, typename tKey> class Factory
{
public:
tBaseClass* Create(tKey Key)
{
return m_Workers[Key]();
}
void Register(tKey Key, BLA)
{
m_Workers.insert(Key, BLA);
}
private:
map <tKey, BLA> m_Workers;
};
Factory.Register("ClassA", &FactoryWorker<ClassA>);
ClassA* p=Factory.Create("ClassA");
Allerdings kriege ich jetzt die Syntax nicht ganz hin, was muss ich jetzt statt dem BLA jeweils schreiben (2 mal in Register und in der map Deklaration)?
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
https://jonathank.de/games/
- Krishty
- Establishment
- Beiträge: 8264
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Factory & Reflection
In den public-Teil der Factory gehört noch der Typ der Funktionszeiger:
Dannund
Code: Alles auswählen
// Typ eines Funktionszeigers auf eine Create-Funktion - gibt tBaseClass * zurück, wird als ComponentCreationFunction angesprochen und erwartet keine Parameter
typedef tBaseClass * (*ComponentCreationFunction)();
Code: Alles auswählen
void Register(tKey Key, ComponentCreationFunction Function)
{
m_Workers.insert(Key, Function);
}
Code: Alles auswählen
map <tKey, ComponentCreationFunction> m_Workers;
Re: Factory & Reflection
Ok, danke, es funktioniert jetzt soweit.
Doch hat noch jemand eine Idee, wie ich eine Variable Anzahl an Konstruktorparametern unterstützen kann, ohne für jede Anzahl die Klasse zu kopieren? Ich will erstmal, dass alle Konstruktoren gleich aufgerufen werden, d.h. die Create Methode bekommt immer die selben Parameter (in Bezug auf Anzahl und Typen), aber ich will halt verschiedene Factory Objekte haben können, die dann unterschiedliche Parameter verarbeiten können.
Das mit "unterschiedlich" ist in Bezug auf die jeweiligen Typen ja mit templates sehr einfach, aber in Bezug auf die Anzahl wüsste ich nicht, wie ich das schaffe, ohne die Factory Klasse für jede Anzahl zu kopieren und verändern.
Ich bin mir nichtmal sicher, ob sowas überhaupt geht, aber Fragen kann ja nicht schaden.
Hier mal der Code wies bisher ist (ist durch Fehlermeldungen ein wenig aufgebläht):
Doch hat noch jemand eine Idee, wie ich eine Variable Anzahl an Konstruktorparametern unterstützen kann, ohne für jede Anzahl die Klasse zu kopieren? Ich will erstmal, dass alle Konstruktoren gleich aufgerufen werden, d.h. die Create Methode bekommt immer die selben Parameter (in Bezug auf Anzahl und Typen), aber ich will halt verschiedene Factory Objekte haben können, die dann unterschiedliche Parameter verarbeiten können.
Das mit "unterschiedlich" ist in Bezug auf die jeweiligen Typen ja mit templates sehr einfach, aber in Bezug auf die Anzahl wüsste ich nicht, wie ich das schaffe, ohne die Factory Klasse für jede Anzahl zu kopieren und verändern.
Ich bin mir nichtmal sicher, ob sowas überhaupt geht, aber Fragen kann ja nicht schaden.
Hier mal der Code wies bisher ist (ist durch Fehlermeldungen ein wenig aufgebläht):
Code: Alles auswählen
///Factory with 1 arguments
template<class tBaseClass, typename tKey, typename t0> class Factory1
{
public:
typedef tBaseClass* (*WorkerFun)(t0 v0);
typedef std::map<tKey, WorkerFun> tMap;
///Register a new class
void Register(tKey Key, WorkerFun Fun)
{
tMap::iterator It=m_Workers.find(Key);
if(It!=m_Workers.end())
{
stringstream s;
s << Key << " is already registered";
BOOST_THROW_EXCEPTION(std::exception(s.str().c_str()));
}
else
m_Workers.insert(pair<tKey, WorkerFun>(Key, Fun));
}
///This functions builds the object for you
tBaseClass* Create(tKey Key, t0 v0)
{
tMap::iterator It=m_Workers.find(Key);
if(It==m_Workers.end())
BOOST_THROW_EXCEPTION(std::exception(CreateNotFoundMessage(Key).c_str()));
else
return It->second(v0);
}
///This is a intern function to create the actual object
template <class tClass> static tBaseClass* Worker(t0 v0)
{ return new tClass(v0); }
private:
tMap m_Workers;
std::string CreateNotFoundMessage(tKey Key)
{
std::stringstream s;
s << "Couldn't find Class " << Key << "\n"
<< "Registered classes are: ";
pair<tKey, WorkerFun> P;
bool a=false;//we only want ", " between the elements
foreach(P, m_Workers)
{
if(a)
s << ", ";
s << P.first;
a=true;
}
return s.str();
}
};
//Benutzung:
Factory1<WorldObject, string, SceneManager*> m_ObjectFactory;
m_ObjectFactory.Register("Lumberjack", m_ObjectFactory.Worker<WorldObject>);
WorldObject* NewHouse=m_ObjectFactory.Create("Lumberjackl", &m_SceneManager);
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
https://jonathank.de/games/
- Krishty
- Establishment
- Beiträge: 8264
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: Factory & Reflection
Das wird wohl erst mit C++0x’s Variadic Templates und Perfect Forwarding gehen.Jonathan hat geschrieben:Doch hat noch jemand eine Idee, wie ich eine Variable Anzahl an Konstruktorparametern unterstützen kann, ohne für jede Anzahl die Klasse zu kopieren?