Pseudo Associative

Random thoughts on software development

0 notes

Managing coupling in a data oriented way

When your code is tightly coupled it is hard to change one part of the code without affecting other parts. When your modules are loosely coupled they can be modified or removed independently with ease. 

I just came across a blog run by the guys behind BitSquid, a new high end game engine. They have some great entries about numerous things related to game development. But today I thought I’d highlight the tree blog entries by Niklas about managing coupling: 

This is not your regular advice on how to manage coupling in an OOP setting, where you would talk about usage of abstract interfaces, composition, which classes should know about each other etc.  The kind of stuff Java developers would be used to. Rather this is a data oriented design, which is very C-style and low level.

Using IDs to reduce coupling

Niklas details why you should refer to objects most of the time using IDs rather than pointers. An ID is typically just a 32 bit integer. Unlike pointers IDs can be moved to different CPUs or cores using different address space, serialized to disk etc. The naive approach would be to create a hash table or STL map to lookup pointers based on integer ID. But the blog entry shows how we can do very quick lookup by storing offsets inside the ID. Noel at Games from Within also talks about using IDs instead of pointers in Managing Data Relationships. I think you should read these before starting to use GUIDs e.g.

Event management

In the second blog post Niklas has the novel idea that instead of you pushing events to an event handling system where stuff happens, each subsystem just stores events as they happen and the higher level system then polls the lower level system for events. This way you don’t couple any low level system to a specific events notification system.

I have worked with these kind of problems before. You might have global loggers or message objects, so that in your algorithms you might write something like:

  if (wrong)
    Msg::Error("Something wrong happened!")

Or you might have some logger object or singleton class:

  if (wrong)
    Logger::GetLogger("error")->Log("Something wrong happened");

The problem with this approach is that your algorithms gets strongly coupled to your framework or particular way of sending messages. You also lose control over how and when to display messages. Niklas doesn’t go into details but one way I have found useful is to pass a message handling object to functions or objects and have them fill this message handling object with messages or events:

  void process(MsgObj *msg) {  
    
if (wrong) 
      msg->addError("Something wrong happened!");

Apple’s Cocoa API often uses a similar approach for dealing with errors. In the Visualization Toolkit, VTK, they extensively use an observer pattern. A lot of software has classes that represents algorithms. When the algorithms want to report on progress to the main UI or report errors they would typically use a central message object. Often using a global function or singleton. In VTK they make algorithm objects observable. That way progress and errors are reported by sending messages to observers rather than to a centralized object. These approaches should not be confused with what Niklas talks about because these are OO solutions to similar problems that he outlines. 

Duck Typing in C++

In the duck typing in C++ blogpost Niklas talk about stuff that might seem a bit similar to QVariant class found in the Qt application and UI framework. With QVariant you can also create arbitrary data structures by combining simple QVariant types with QVariantMap and QVariantList. But Niklas show an interesting twist in how you can in a way define something that can best be described to a Qt developer as a QVariantMap template for QVariantMap objects with fixed number of members. This allows very efficient storage of each variant map. Niklas doesn’t talk about Qt at all, but that is a way you might think about it. You have similar mechanism in Clojure and other LISPs and Scheme versions.