tags:

views:

62

answers:

2

I'm trying to build a simple entity/component system in c++ base on the second answer to this question : Best way to organize entities in a game?

Now, I would like to have a static std::map that returns (and even automatically create, if possible).

What I am thinking is something along the lines of:

PositionComponent *pos = systems[PositionComponent].instances[myEntityID];

What would be the best way to achieve that?

+1  A: 

You should create some constants (ex. POSITION_COMPONENT=1) and then map those integers to instances.

Maz
Thanks for the answer, but is there a way to avoid creating constants like that?
sharvey
Why don't you want to use constants?
Maz
Coming from Python/javascript/..., I just don't find them particularly elegant. Not that it's a valid reason to avoid them :)
sharvey
No. C++ does not have introspection. There's nothing a sane person wants to use that corresponds to retrieving the class object for an instance in Java. If you want a runtime type system, you build it yourself.
bmargulies
Thanks, I just wanted to make sure there was no way.
sharvey
Syntactically, they aren't so elegant, but they can be implemented to use much less memory than the counterpart which you have suggested. Additionally, why don't you just add a static function which returns the instance? But you gain quite a bit of bulk (in memory and complexity) when you write a type system versus using numerical constants which can be used with the char type to save 3 bytes.
Maz
+1  A: 

Maybe this?

std::map< std::type_info*, Something > systems;

Then you can do:

Something sth = systems[ &typeid(PositionComponent) ];

Just out of curiosity, I checked the assembler code of this C++ code

#include <typeinfo>
#include <cstdio>

class Foo {
    virtual ~Foo() {}
};

int main() {
    printf("%p\n", &typeid(Foo));
}

to be sure that it is really a constant. Assembler (stripped) outputted by GCC (without any optimisations):

.globl _main
_main:
LFB27:
        pushl   %ebp
LCFI0:
        movl    %esp, %ebp
LCFI1:
        pushl   %ebx
LCFI2:
        subl    $20, %esp
LCFI3:
        call    L3
"L00000000001$pb":
L3:
        popl    %ebx
        leal    L__ZTI3Foo$non_lazy_ptr-"L00000000001$pb"(%ebx), %eax
        movl    (%eax), %eax
        movl    %eax, 4(%esp)
        leal    LC0-"L00000000001$pb"(%ebx), %eax
        movl    %eax, (%esp)
        call    _printf
        movl    $0, %eax
        addl    $20, %esp
        popl    %ebx
        leave
        ret

So it actually has to read the L__ZTI3Foo$non_lazy_ptr symbol (I wonder though that this is not constant -- maybe with other compiler options or with other compilers, it is). So a constant may be slightly faster (if the compiler sees the constant at compile time) because you save a read.

Albert
I wonder if bmargulies thinks a sane person would use that.
sharvey
Well, I never have seen any usecase where I needed to map classes to something. :) But in case you want to do that, I think there is nothing wrong with this solution. Or is there? This should be perfectly valid C++. And it is also efficient.
Albert
As efficient as using constants?
sharvey
Albert
I'm trying this (and have tried multiple variations):`map<const type_info*, System<Component> > systems; System<PositionComponent> pc; const type_info *tid = systems[ tid ] = pc;`but I get an error saying `No match for 'operator='`, do you have any suggestions?
sharvey
Thanks for the updated answer, thats really informative.
sharvey
The error says: `System<Component>` does not have an `operator=`. Maybe you want to use `System<Component>*` (or `boost::shared_ptr` of it). Or otherwise implement the `operator=`.
Albert