Toon posts:

[c++] pluggable factories & multiple inheritance

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
ik ben een 3d engine aan het maken, ik zal in het kort de structuur uitleggen zoals ik die nu heb:

class node
hierin wordt de positie en rotatie van alles wat er onder hangt opgeslagen. onder een node kunnen andere nodes of entities hangen.

class entity
van dit object zijn meerdere subentities afgeleid, zoals een entity_camera, entity_drawable etc.

een scene in de praktijk:

node *root
|-- node *auto
| |-- entity_drawable *wiel1
| |-- entity_drawable *carosserie
|-- node *camera
| |-- entity_camera *mijn_camera

etc.

deze entities worden gemaakt door pluggable factories; dwz: er is een entityfactory_drawable die zich bij program startup registreert bij de entityfactory, als de gebruiker iets doet als 'entityfactory.create(DRAWABLE, "wiel1");' wordt de juiste subfactory gezocht en het drawable object door deze subfactory in een map met drawables (in de zgn scenemanager) geplaatst.

maar het probleem is: als ik nu een drawable wil die via multiple inheritance net-aware en/of physics-aware is, moet ik dan bakken met subfactories maken? (entityfactory_drawable, entityfactory_drawable_physics, entityfactory_net, entityfactory_drawable_net, entityfactory_drawable_physics_net ..)

lijkt me niet praktisch. sowieso vraagt ik me af of ik alle entities wel van een hoofdentity moet afleiden (ivm de 'dreaded diamond' bij multiple inheritance), maar dan nog blijft het probleem. is wat ik wil wel mogelijk, of zijn factories hier gewoon niet geschikt voor?

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

Imho is je design verkeerd. Even los van het hele factory verhaal, voor elke mogelijke permutatie aan object-typen dat je bij elkaar in een entity wilt heb je een aparte class nodig - dat is gewoon onhandig. In plaats daarvan, gebruik een niet-overerfbare entity class die je gewoon kunt instantieren, waar je vervolgens speciale componenten aan kunt hangen. Bijvoorbeeld een Drawable, een PhysicsObject, een Light, etc. Een Entity is dan gewoon een soort container waar al deze verschillende typen in opgeslagen kunnen worden, en die verder nog standaard gedeelde data bevat.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • roy-t
  • Registratie: Oktober 2004
  • Laatst online: 19-09 10:19
.oisyn schreef op zondag 14 oktober 2007 @ 18:43:
Imho is je design verkeerd. Even los van het hele factory verhaal, voor elke mogelijke permutatie aan object-typen dat je bij elkaar in een entity wilt heb je een aparte class nodig - dat is gewoon onhandig. In plaats daarvan, gebruik een niet-overerfbare entity class die je gewoon kunt instantieren, waar je vervolgens speciale componenten aan kunt hangen. Bijvoorbeeld een Drawable, een PhysicsObject, een Light, etc. Een Entity is dan gewoon een soort container waar al deze verschillende typen in opgeslagen kunnen worden, en die verder nog standaard gedeelde data bevat.
*sneaked in topic*

Goddammit Oisyn ik ga gewoon maar je hele posthistory door pluizen.. alweer een goede tip!
*kloot verder met xna, en geeft topic terug aan TS*

~ Mijn prog blog!


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
tnx voor het idee oisyn, ik ga overdenken wat dat verder voor implicaties heeft :)

(heb alles heel netjes met resourcemanagers, modulair etc dus voor ik zoiets verander mag ik eerst uren peinzen .. ;( was ik maar ietsjes slimmer, dan ging dat wat sneller ;))

*edit: voor de zekerheid; hoe had je die componenten (drawable, light etc) in gedachten? allemaal afgeleid van een enkel type? anders moet ik in entity containers maken voor elk mogelijke type en dat verwoest de uitbreidbaarheid een beetje ..

[ Voor 32% gewijzigd door Verwijderd op 14-10-2007 21:14 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

Nee het idee is nou juist een entity een generieke container is met een aantal properties (zoals bijv. een transformatiematrix), waar je de verschillende typen tegelijk in kunt hangen. Je kan dan idd werken met verschillende typen door in je entity voor elk type een pointer op te nemen met wat accessors erbij, of je maakt een (geassocieerde) array van pointers die je dmv een enum-veld voor elk type kunt indexeren (maar dan moet je weer casten).

Met wat template trucs kun je het overigens wel modulair en typesafe krijgen.

[ Voor 8% gewijzigd door .oisyn op 15-10-2007 13:24 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
die transformatiematrix zit nu in mijn node type, waaronder nodes of entities kunnen hangen. eigenlijk kan ik het dus zo laten, maar wordt mn node type juist de container voor van alles (scheelt weer wat reorganisatie)

wat ik nog niet snap is hoe je het modulair houdt; als ik nou bijv als gebruiker van de game engine een type 'banaan' wil maken, dan zal er toch ergens in het entity (of node dus in mijn geval) type een pointer naar een 'banaan' instance moeten zitten?

* oh wacht, templates zeg je.. weinig ervaring mee, weer wat te lezen dus :)


waarom kijk ik elke keer weer hoopvol naar het mannetje in je icon, of hij het ditmaal misschien wel overleeft ..

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je zou natuurlijk een "dynamische" enum kunnen simuleren:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class EntityTag
{
public:
    EntityTag() : m_id(s_numTags++) { }
    int GetID() const { return m_id; }

    static int GetNumTags() { return s_numTags; }

private:
    EntityTag& operator=(const EntityTag &); // not assignable

    int m_id;
    static int s_numTags;
};

int EntityTag::s_numTags = 0;


class Entity
{
public:
    Entity() : m_instances(EntityTag::GetNumTags())
    {
    }

    void * Get(EntityTag tag) const { return m_instances[tag.GetID()]; }
    void Set(EntityTag tag, void * ptr) { m_instances[tag.GetID()] = ptr; }

private:
    std::vector<void*> m_instances;
};


// usage:
class Light
{
    // ...
};

EntityTag tagLight;


class PhysicsObject
{
    // ...
};

EntityTag tagPhysicsObject;

int main()
{
    Entity * entity = new Entity();
    Light * light = new Light();
    entity->Set(tagLight, light);

    if (entity->Get(tagPhysicsObject))
        // etc
}


't vervelende is nu echter dat je bij Entity::Get() moet casten naar het juiste type. Dit is met wat template trucs wel op te lossen:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// de oude EntityTag
class EntityTagBase
{
    // hier de implementatie van de vorige EntityTag
};

// de template class
template<class IFACETYPE>
class EntityTag : public EntityTagBase
{
public:
    typename IFACE_TYPE interface_type;
};

class Entity
{
    // etc

    template<class TAG>
    typename TAG::interface_type * Get(TAG tag) const
    {
        return static_cast<typename TAG::interface_type*> m_instances[tag.GetID()];
    }

    template<class TAG>
    void Set(TAG tag, typename TAG::interface_type * ptr)
    {
        m_instances[tag.GetID()] = ptr;
    }
};


class Light { };
EntityTag<Light> tagLight;

int main()
{
    entity = new Entity();
    entity->Set(tagLight, new Light());
    Light * light = entity->Get(tagLight);
}
Verwijderd schreef op maandag 15 oktober 2007 @ 20:09:
die transformatiematrix zit nu in mijn node type, waaronder nodes of entities kunnen hangen. eigenlijk kan ik het dus zo laten, maar wordt mn node type juist de container voor van alles (scheelt weer wat reorganisatie)
Mja, dat zou kunnen idd... maar dan ga je een Node wel al gauw behandelen als een enkele entiteit, terwijl niets je ervan weerhoudt om bijv. meerdere physics objecten eronder te hangen. Aan de andere kant, misschien wil je dat juist ook wel :)

[ Voor 12% gewijzigd door .oisyn op 15-10-2007 22:43 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
ahh bedankt, dat ziet er gaaf uit, doet pcies wat ik wil :) tnx ook voor het heldere voorbeeld.

vraag me wel af, wat doet het als je die get doet terwijl er geen light? die templatecode is nl nog een tikje wazig voor me, maar ik neem aan dat je voor bijv de netwerkcode met een loop alle entities bij langs gaat om te kijken of er een net element aan hangt?

is dit eigenlijk een beetje een algemeen geaccepteerde manier om dit dit soort problemen op te lossen? heb het nl nog niet eerder zo gezien (of ik heb gewoon nog niet genoeg rondgekeken ;))

nogmaals bedankt, ik zat echt al dagen vast, nu kan ik weer verder :D

*edit: intussen geimplementeerd en works like a charm :D zit nog wel met bovenstaande vraag trouwens ..

[ Voor 13% gewijzigd door Verwijderd op 17-10-2007 03:12 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
*bump

oisyn: werk nu een tijdje met deze opzet, werkt prima, nogmaals bedankt :)
heb nu wel een probleem waar ik niet uitkom:

ik heb bijv een entity waar een drawable-tag onder hangt. op een gegeven moment gooi ik de drawable instance wat in het rond en moet ik ergens opvragen onder welke entity deze drawable hangt. ik wil dus in de 'entity->set(tag_drawable, eendrawable)' iets doen als 'tag->entity_set(this)', alleen weet de entity natuurlijk niet wat voor type 'tag' is en dus ook niet dat die een entity_set methode heeft.

mogelijkheden:

- ik zou al mn tags kunnen afleiden van een hoofdtag met een entity_set functie, maar het lijkt me dat dan het hele idee van de dynamische enum een beetje weg is.

- elke keer als ik in een functie een entity->set() doe daarna de tag-instance in kwestie ->entity_set'en (maar ook dan moet ik de tags afleiden van een hoofdklasse, lijkt me)

- nooit de drawables/lights etc zelf rondpassen, maar slechts de overkoepelende entity; alleen kan ik dan natuurlijk weinig nuttige code in de drawables/lights etc stoppen omdat die geen idee hebben waar ze onder hangen..

zie ik een mogelijkheid over het hoofd? want deze 3 oplossingen lijken me allemaal niet echt handig..

Acties:
  • 0 Henk 'm!

  • YopY
  • Registratie: September 2003
  • Laatst online: 13-07 01:14
alleen weet de entity natuurlijk niet wat voor type 'tag' is en dus ook niet dat die een entity_set methode heeft.
Dit is een typisch iets waarbij je een Interface maakt. Een interface is (voor zover ik weet) beschikbaar in elke OO-taal, en je kunt het zien als een soort van 'contract'. Een interface kun je zien als een klasse met alleen maar methods, zonder dat die methods geimplementeerd zijn:

Java:
1
2
3
4
5
interface Tag {

   public x entity_set();

}


Wanneer je nu aangeeft dat je klasse deze interface gebruiken moet (dmv het interface keyword - "class X implements Tag"), verplicht je de klasse nu om een methode entity_set te hebben. Wanneer je die niet toevoegd, krijg je een compiler error wanneer je probeert te compilen.

Vervolgens kun je een lijstje met tags opslaan als zijnde Tag (het type van je interface), en dan weet je altijd zeker dat de operaties die je aanroept de entity_set() heeft.

Er zijn vast mensen die het concept beter uit kunnen leggen, inclusief C++ code voorbeelden, maar als je zoekt op goegel naar C++ interfaces kom je vast een heel eind.

En dan is het een kwestie van het hele interface gebeuren doorhebben :/

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op donderdag 01 november 2007 @ 16:06:
ik wil dus in de 'entity->set(tag_drawable, eendrawable)' iets doen als 'tag->entity_set(this)', alleen weet de entity natuurlijk niet wat voor type 'tag' is en dus ook niet dat die een entity_set methode heeft.
Als je de template methode gebruikt dan is dat geen probleem, je kunt gewoon ptr->SetEntity(this) doen. Anders zul je idd een generieke interface moeten implementeren op ieder type.

Je wilt trouwens niet je entity zetten op de tag zelf, dat is namelijk een object dat tussen alle entities geshared wordt. Je wilt 'm instellen op de instance die aan die tag voldoet.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
bedankt voor de reacties, sorry voor mijn late reactie, ben de laatste week bezig geweest met octrees, ook een erg gaaf onderwerp :D
.oisyn schreef op vrijdag 02 november 2007 @ 10:39:
Als je de template methode gebruikt dan is dat geen probleem, je kunt gewoon ptr->SetEntity(this) doen. Anders zul je idd een generieke interface moeten implementeren op ieder type.
ik snap de internals niet helemaal; de ptr wijst naar een 'TAG::interface_type' ofwel (in jouw voorbeeld) een 'EntityTag<Light>::interface_type' (snap ik dat goed?)

maar dat kan dus een class light of een class drawable of wat dan ook zijn; hoe weet de compiler dan dat die classes een methode 'setentity()' hebben als je geen interface gebruikt? ze zijn immers niet afgeleid van een of ander met die methode..

maareh, met een interface voor die 'light' en 'drawable' etc classes zou het dus (ook?) moeten kunnen?

[ Voor 8% gewijzigd door Verwijderd op 08-11-2007 17:34 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-09 16:37

.oisyn

Moderator Devschuur®

Demotivational Speaker

Omdat het templates zijn, is de class bekend bij template instantiatie, en dán wordt pas gekeken of de class in kwestie (in het geval van EntityTag<Light> is EntityTag<Light>::interface_type dus gekijk aan Light) een methode SetEntity() heeft.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
ah tnx, ic :) t wordt langzaam wat helderder; hoewel ik nu en dan nog niet gelijk inzie waar ik abc's, interfaces of templates moet toepassen. maarja, alle begin is lastig (omdat ik al die dingen nog nooit had gebruikt bij vorige projecten)

hier een screenshotje van mn huidige enginestatus om even te patsen :P (waarbij ik de light class de lightmaps laat berekenen door em nog heel smerig de overkoepelende entity als param van die lightmapfunctie mee te geven.. dat kan ik nu dus fatsoenlijk gaan aanpassen ;))

Afbeeldingslocatie: http://ranchorelaxo.student.utwente.nl/bitplane/gfxmisc/blunted02.jpg
Pagina: 1