[C++] Templates gebruiken in de constructor

Pagina: 1
Acties:

  • Sponge
  • Registratie: Januari 2002
  • Laatst online: 12:46

Sponge

Serious Game Developer

Topicstarter
Ik heb vandaag een aantal uren geprobeert een 'gem' uit Game Programming Gems II wat bij te werken. Het gaat hier om een 'home-made' RTTI implementatie. Deze wil ik koppelen aan mijn ObjectFactory, zodat elk object wat geregistreerd staat, ook te creeren is via de stringnaam.

Helaas blijkt het niet zo simpel als ik dacht. Ik krijg het niet voor elkaar om het object wat geregistreerd moet worden mee te geven aan de constructor van de RTTI class. Dit kan niet via een instance uiteraard ;).

De ObjectFactory zelf gebruikt een template hiervoor:

C++:
1
2
3
4
5
   template<typename ClassType>
   bool Register(UniqueIdType unique_id)
   {
    ....
   }


Bruikbaar als:

C++:
1
.Register<CABuilding_Store>("CABuilding_Store");


Het leek me dus alleen mooi als dit vanzelf mee zou gaan met de home-made RTTI implementatie. Deze RTTI werkt simpel weg met een enkele instantie van een class, bovenaan elke .cpp file:

C++:
1
   dtiClass CABuilding_Bank::Type("CABuilding_Bank", &CEActorRoot::Type,  _CABuilding_Bank );


Het bovenstaande werkt. De string gaat mee, de parent, en het ID. No problem. Echter: "CEActorRoot" is het root element van alle objecten in de gehele applicatie. Ik kan helaas geen CEActorRoot* object gebruiken bij de class factory. Dit moet echt een niet geinstantieerde object (naam) zijn.

Daarom probeerde ik dit:

C++:
1
2
3
4
5
    template<typename T>
    dtiClass(std::string szSetName, dtiClass* pSetParent, UINT ObjectTypeID)
    {
                   ....
                }


En voor het initializen:

C++:
1
 dtiClass CABuilding_Bank::Type<CABuilding_Bank>("CABuilding_Bank", &CEActorRoot::Type,  _CABuilding_Bank );


Echter begint de compiler fouten te geven op de < en ;'s e.d. Geen success.

Wanneer ik Template T als argument probeer te gebruiken (en de functie dus aanpas):

illegal use of this type as an expression with [Type=CEActorRoot]

Ook geen success ;).

Een simpele test zonder inwat anders dan een constructor:

C++:
1
2
3
4
    template<typename T> void test()
    {
        CSingleton<CEActorManager>::GetPointer()->GetFactory().Register<T>(szName);
    }


En de code:

C++:
1
    Type.test<CABuilding_Bank>();


Werkt perfect (in een functie) :)... Dus ik vermoed dat het onmogelijk is om de template te gebruiken in de constructor :'( ?

Heeft iemand een idee :)

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Sponge schreef op vrijdag 10 februari 2006 @ 22:04:
Ik heb vandaag een aantal uren geprobeert een 'gem' uit Game Programming Gems II wat bij te werken. Het gaat hier om een 'home-made' RTTI implementatie. Deze wil ik koppelen aan mijn ObjectFactory, zodat elk object wat geregistreerd staat, ook te creeren is via de stringnaam.
Dat is meer reflection dan RTTI, maar vooruit.
Helaas blijkt het niet zo simpel als ik dacht. Ik krijg het niet voor elkaar om het object wat geregistreerd moet worden mee te geven aan de constructor van de RTTI class.
C++:
1
2
3
4
5
6
   template<typename ClassType>
   bool Register(UniqueIdType unique_id)
   {
    ....
   }
// bvb Register<CABuilding_Store>("CABuilding_Store");

Het leek me dus alleen mooi als dit vanzelf mee zou gaan met de home-made RTTI implementatie. Deze RTTI werkt simpel weg met een enkele instantie van een class, bovenaan elke .cpp file:

C++:
1
   dtiClass CABuilding_Bank::Type("CABuilding_Bank", &CEActorRoot::Type,  _CABuilding_Bank );

Het bovenstaande werkt. De string gaat mee, de parent, en het ID. No problem. Echter: "CEActorRoot" is het root element van alle objecten in de gehele applicatie. Ik kan helaas geen CEActorRoot* object gebruiken bij de class factory. Dit moet echt een niet geinstantieerde object (naam) zijn.
En nu volg ik het helemaal niet meer. Je class factory wil twee dingen hebben bij de registratie: een type en een string. Die heb je hier toch? Ik neem tenminste aan dat die laatste _CABuilding_Bank een tikfout is (illegale naam). "Niet geinstantieerd object" of "Niet geinstantieerd objectnaam" bestaan niet in C++, dus dat negeer ik maar even.
Daarom probeerde ik dit:
C++:
1
2
3
    template<typename T>
    dtiClass(std::string szSetName, dtiClass* pSetParent, UINT ObjectTypeID)
    {  }
Dat is geen functie (mist een return type) en ook geen class (mist struct/class keyword en een ; ).
het ziet er ook niet echt uit als een constructor (want dan is T niet te bepalen)
Dus ik vermoed dat het onmogelijk is om de template te gebruiken in de constructor :'( ?
Volstrekt legaal. Sterker nog, elke container in de STL heeft templated constructors.

Ik vermoed dat je probleem is dat je iets te hoog reikt. Als je termen als "niet geinstantieerde object (naam)" gebruikt, dan vermoed ik dat je het verschil tussen classes en objecten niet helder genoeg hebt. Dan zijn templates nog te ingewikkeld.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Sponge
  • Registratie: Januari 2002
  • Laatst online: 12:46

Sponge

Serious Game Developer

Topicstarter
Dat is meer reflection dan RTTI, maar vooruit.
Ja, de object factory is dat. Maar de "home-made" RTTI is dus het stukje code wat de naam registreert van de class + unieke objecttype ID.
En nu volg ik het helemaal niet meer. Je class factory wil twee dingen hebben bij de registratie: een type en een string. Die heb je hier toch? Ik neem tenminste aan dat die laatste _CABuilding_Bank een tikfout is (illegale naam). "Niet geinstantieerd object" of "Niet geinstantieerd objectnaam" bestaan niet in C++, dus dat negeer ik maar even.
Nee, dat is geen tikfout, de _<naam> is de prefix die ik gebruik voor de unieke object type ID's om bepaalde objecten met elkaar te kunnen vergelijken (en wil nu met name over naar een IsA() variatie)
Dat is geen functie (mist een return type) en ook geen class (mist struct/class keyword en een ; ).
het ziet er ook niet echt uit als een constructor (want dan is T niet te bepalen)
Een constructor heeft geen return type, en het is de constructor van een class. Het is niet nodig om de hele class definitie te pasten, aangezien het alleen maar om de constructor gaat die moet weten wat type object er meekomt. Let op: Zover ik weet moet het via een template, omdat ik niet non-pointer type mee kan geven als argument (zover ik weet). De class "CABuilding_Bank moet instantieerbaar zijn door de object factory - en dit kan geen instantie van een instantie zijn.
Volstrekt legaal. Sterker nog, elke container in de STL heeft templated constructors.

Ik vermoed dat je probleem is dat je iets te hoog reikt. Als je termen als "niet geinstantieerde object (naam)" gebruikt, dan vermoed ik dat je het verschil tussen classes en objecten niet helder genoeg hebt. Dan zijn templates nog te ingewikkeld.
Dat ik in Engels de definities "Present continuous" of "Past simple" niet precies weet, betekend nog niet dat ik geen Engels kan ;).

Maar goed, het moet zover ik weet toch echt via een template, maar het lukt me niet met iets zoals dit:

C++:
1
dtiClass CABuilding_Bank::Type<CABuilding_Bank>("CABuilding_Bank", &CEActorRoot::Type,  _CABuilding_Bank );


Want als ik de hele class een template geef, dan gaan er problemen komen omdat het 2e argument (de parent type) niet matched met de interne class. De class is (ingekort):

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class dtiClass
{
private:
    
    std::string szName;
    
    dtiClass* pdtiParent;
    
public:

    dtiClass(std::string szSetName, dtiClass* pSetParent, UINT ObjectTypeID)
    {
        SetName( szSetName );
        pdtiParent = pSetParent;
        //SetParent( pSetParent );
        //CSingleton<CEActorManager>::GetPointer()->GetFactory().Register<name>(szSetName);
    };


als deze class een gehele class template krijgt, zal "pdtiParent" dezelfde type krijgen als het object wat zich registreer. Dit betekend dat het dus niet zal matchen met het 2e argument (pSetParent) omdat dit een ander type object is. Ik heb heel wat geprobeert, maar kreeg het niet voor elkaar dat de pdtiParent het andere object type kreeg.

[ Voor 6% gewijzigd door Sponge op 11-02-2006 18:51 ]


  • Sponge
  • Registratie: Januari 2002
  • Laatst online: 12:46

Sponge

Serious Game Developer

Topicstarter
Ik heb een oplossing gevonden door het volgende te gebruiken:

C++:
1
2
3
4
5
6
7
8
9
template<typename Town, typename Tparent>
class dtiClass
{
....
}

en

dtiClass<CABuilding_Track, CABuilding> CABuilding_Track::Type();


:)

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Sponge schreef op zaterdag 11 februari 2006 @ 18:50:
Nee, dat is geen tikfout, de _<naam> is de prefix die ik gebruik voor de unieke object type ID's om bepaalde objecten met elkaar te kunnen vergelijken (en wil nu met name over naar een IsA() variatie)
't Blijft een illegale naam, _[A-Z] is gereserveerd voor macro's e.d. van de compiler zelf. s/tikfout/denkfout/
[...]
Een constructor heeft geen return type, en het is de constructor van een class. Het is niet nodig om de hele class definitie te pasten, aangezien het alleen maar om de constructor gaat die moet weten wat type object er meekomt. Let op: Zover ik weet moet het via een template, omdat ik niet non-pointer type mee kan geven als argument (zover ik weet). De class "CABuilding_Bank moet instantieerbaar zijn door de object factory - en dit kan geen instantie van een instantie zijn.
Een constructor definieer je als X::X(...) { }. Bovendien mag een constructor wel een template argument hebben, maar je moet wel even nadenken hoe je vervolgens dat argument meegeeft. De enige mogelijkheid is Template Argument Deduction.

Ik snap nog steed niet waarom je denkt dat het onmogelijk is om een non-pointer type mee te geven als argument. void foo(int) is het klassieke voorbeeld van een functie, en dat lijkt me erg non-pointer. C++ is geen Java?

Bovendien snap ik niet wat je probleem is met "instanties van instanties"? Sowieso is "instantie" een erg brede term. Een object is een instantie van een type. Een instantie van een class template is een type. Een instantie van een functie template is een functie.
Maar goed, het moet zover ik weet toch echt via een template, maar het lukt me niet met iets zoals dit:

C++:
1
dtiClass CABuilding_Bank::Type<CABuilding_Bank>("CABuilding_Bank", &CEActorRoot::Type,  _CABuilding_Bank );

Want als ik de hele class een template geef, dan gaan er problemen komen omdat het 2e argument (de parent type) niet matched met de interne class. De class is (ingekort):

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class dtiClass
{
private:
    
    std::string szName;
    
    dtiClass* pdtiParent;
    
public:

    dtiClass(std::string szSetName, dtiClass* pSetParent, UINT ObjectTypeID)
    {
        SetName( szSetName );
        pdtiParent = pSetParent;
        //SetParent( pSetParent );
        //CSingleton<CEActorManager>::GetPointer()->GetFactory().Register<name>(szSetName);
    };


als deze class een gehele class template krijgt, zal "pdtiParent" dezelfde type krijgen als het object wat zich registreer. Dit betekend dat het dus niet zal matchen met het 2e argument (pSetParent) omdat dit een ander type object is. Ik heb heel wat geprobeert, maar kreeg het niet voor elkaar dat de pdtiParent het andere object type kreeg.
Nee, je doet nu een onterachte aanname. Als dtiClass een template wordt, dan is het niet vanzelfsprekend dat dtiClass<T> een member van type dtiClass<T> heeft. Zoals je zelf al merkt moet je rekening houden met het feit dat je een andere parent hebt. Dit kun je op twee manieren oplossen: introduceer een non-template dtiClassBase, of definieer het template als dtiClass<T, dtiClass<Parent> >

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Sponge
  • Registratie: Januari 2002
  • Laatst online: 12:46

Sponge

Serious Game Developer

Topicstarter
Ik snap nog steed niet waarom je denkt dat het onmogelijk is om een non-pointer type mee te geven als argument. void foo(int) is het klassieke voorbeeld van een functie, en dat lijkt me erg non-pointer. C++ is geen Java?
Het is onmogelijk in mijn situatie. Aangezien alle objecten inheritten van een root-object. Als ik een building wil maken met de ObjectFactory, kan ik niet het root object maken.

Ik kan dus niet gebruiken:

RootObject* object;
RootObject object;

Want als ik deze classes gebruik om een object te maken, zal ik weinig functies en properties vinden ;). Met een template los je dit dus op, de argumenten komen in een simepele <rootobject*, std::string> map via de register function van de Object Factory, waardoor het wel werkt.

Overigens werkt het nu allemaal perfect, en kan ik weer verder met de save game/load game :)
Pagina: 1