Allegro.cc - Online Community

Allegro.cc Forums » Storing objects » Reply

 Forums: Storing objects: Reply
This section is only available to registered Allegro.cc members.
 Thread Review
DanielH:

Advice please.

In my object handler class, I had a unordered_map of objects.

// all ids are in the form of "@type/name"
std::unordered_map<std::string id, object*> m_map;

// object is a node with 2 variables type and data;

// accessing
ALLEGRO_DISPLAY *d = handler.get<ALLEGRO_DISPLAY>("@display/main");

I changed it to an unordered_map of unordered_maps.

std::unordered_map<int32_t type, std::unordered_map<std::string id, object*>> m_map_list;

// object is the data stored

// accessing
ALLEGRO_DISPLAY *d = handler.get<ALLEGRO_DISPLAY>(Type::Display, "main");

My question is which is more efficient and quicker? Instead of searching using a long id like "@display/main", you are using only "main". There is the extra searching for the map of your specified type.

torhu:

If I had to guess, the first one is faster. And also simpler to use, maybe? If it matters, I would benchmark it. Use a profiler, or put some timing code around a section of your game code that accesses a lot of objects.

APPEND:
I would try to avoid specifying the type twice in the second version, making it harder to mix two different types. That could be an advantage of that version.

So you could just do:

ALLEGRO_DISPLAY *d = handler.get<ALLEGRO_DISPLAY>("main");

At least if that's not too limiting for your needs.

DanielH:

Most of the getting will be done at non-crucial times anyway. See that's why I need advice. I go back and forth. The second way was more complicated to code.

Not really two types in the second version. This is the get function:

// T is the data type that must be cast
// in32_t type is an index to specify which map to look in
template <typename T>
T *get(in32_t type, const std::string& id) {}

In the fix after I broke it, I did some rework of the object system. It should be simple to revert to the first type. I'll probably leave it for now. I spent too much time on it already. Need a break from that section.

torhu:

There are definitely two types in the second version, the template argument and the first call argument. They should probably correspond in a restricted way, depending on what kind of genericity you need.

Use the first system if you need a break, it's probably not that important unless you are making a framework for creating games :)

Mark Oates:

My two cents:

I think you run the risk of abstracting too early if your framework is not designed with hard-coded elements at this layer.

The way I think about it is this: A framework has several layers. Generally speaking, the bottom is Allegro. The layer above that is your system (al_init management, display creation, event loop, etc). Above that, are some light game engine features (scene graph, scripting engine, tile map renderer). And, above that is game-specific code with game-specific set of objects (game-specific scripts, game-specific scene graph objects, game-specific interactions).

Essentially, the design all the way up to the game code itself should be relatively hard-coded. The objects should be concrete, and you should use the language-specific features of your programming language (defined variable types, compile-time type checking, explicit functions names get_primary_display() that get explicit objects, etc).

What happens if you abstract too early at the bottom layer of your design (creating abstract-sounding classes like "thing", "object", "entity"), is that you end up re-writing all the language-specific features that you would otherwise get for free in your programming language, and that foundation has to be carried up all the way through the framework to the game code.

I think it's common for higher layers (game-specific scripts, objects, etc) to have a design that begins getting a little more abstract. For example, you might decide to provide a scene graph that contains "objects" so that the game programmer can interface with it to write game-specific "objects". (As an analogy, in a browser stack, HTML/JS documents would be like this layer, and DOM objects are relatively generic.)

When you over-abstract too early, code starts to become unmanageable. It's might begin to look like this:

ALLEGRO_DISPLAY *display = nullptr;
System::Thing *thing = System::Instance::thing_from(my_std_string);
if (thing && thing->is_type(SYSTEM_THING))
{
   display = (ALLEGRO_DISPLAY*)(thing);
}
if (display != nullptr)
{
   do_actual_thing_with(display);
}

When code gets here, you start fussing with templates, your error messages get harder to understand, strings get stuffed with more metadata, you write more and more type-checking and type-extracting code, and finally, you have to drink more coffee to keep the design running in your brain's RAM.

An alternative to this is to be more direct with the code you write. Your functions (and more importantly your design intentions) are more explicit. The above code might look more like this:

ALLEGRO_DISPLAY *primary_display = get_primary_display();
do_actual_thing_with(display);

DanielH:

Thanks! Not being in the industry and as a hobby, some of these insights are helpful.

Edgar Reynaldo:

No. Abstraction is fine when useful and necessary.

All important objects in my library derive from EagleObject. There is an ObjectRegistry that tracks them. Their important data is stored in the registry, to prevent it from going out of scope when the object does.

Anyway, what this allows me to do is output any kind of EagleObject to an ostream& using a virtual function.

I use abstraction everywhere in my GUI library, and it works fine. In fact, it wouldn't work without it and everything would be hardcoded which is suicide.