OOP : shutdown, initialize, reinitialize

Anything and everything that's related to OGRE or the wider graphics field that doesn't fit into the other forums.
Post Reply
User avatar
paul424
Gnome
Posts: 314
Joined: Thu May 24, 2012 7:16 pm
x 13

OOP : shutdown, initialize, reinitialize

Post by paul424 »

I was just poking here and there , and was wondering how it came so many classes does have those methods : shutdown, initialize, reinitialize. For what I can think it comes from the way how the array initializer works , when doing

Code: Select all

T* table = new T [1000] ; 
only the default constructor can be called for each ...
User avatar
Klaim
Old One
Posts: 2565
Joined: Sun Sep 11, 2005 1:04 am
Location: Paris, France
x 56
Contact:

Re: OOP : shutdown, initialize, reinitialize

Post by Klaim »

This is generally called two-steps initialization.
There are several reasons:

1. Ogre have a lot of old-school style c++ which don't always make sense from a modern C++ style perspective, even assuming modern C++98/03.
Two-steps initialization was common practice in particular in video games so I guess it's "good thinking" from real-time graphics of that time that stuck in.
One of the the reason it is/was popular is...

2. In a lot of games and graphic applications, you want to optimize the (soft-)real-time rendering code by preparing everything you can up-front and then
do the minimum work you need when looping. This way of setting up things leads you to having tons of objects constructed, setup for usage but not used yet.
Once the resources of each system and object is setup, you want to initialize these systems. This mean that construction and initialization is not the same thing, one is on the resource level, the
other is all about the semantic of your object.

3. The constructor is a good feature for the objec'ts important data initialization (prepare memory) but it's not good at doing more advanced initialization (setup the object to be reused as if it was new).
Deciding what will go into the constructor and destructor or what should go in separate operations members is very hard. If you want to not have to think too hard about what you should do in which case,
it's simpler to have a good rule of thumb which you'll apply everywhere and in worse case will not hurt what you care about at that specific time: performance and
habits (which leads to simplicity of reading the code, which is full of the same habits everywhere).

4. Also, constructors and destructors have special rules: a destructor throwing an exception could trigger another throw somewhere else which in C++ means that terminate()
(or abort(), I don't remember exactly) will get called. Constructors can't return any state, so they need to either be called fully and provide a usable object or throw an exception,
which might not make sense semantically if you associate some kind of complex initialization to the constructor.
Basically: constructors and destructor are only part of the solution.

5. Two-steps initialization clearly shows that something is either incomplete or wrong into the language design. Constructors and destructors are definitely great
but are considered rightfully too tricky by most average C++ coders to do anything else than preparing/releasing the core resources of the object (which is their purpose).
There are even discussions about this in the C++ proposals forums because the apparent need for two-steps initialization is a clear sign that something is missing.
The main issue is that sometime the semantic of your type is clearly linked to operations changing it's state in a way similar to an initialization, so then you need to provide
these operations as member (or free) functions; while sometime you definitely want some code to be executed *systematically after construction*, which means
after the constructor have been called. This don't exists at the moment and it's annoying sometime because it puts the burden on the user to do the initialization.
It would be nice to have the choice to do so.

Now, here is my personal opinion (I'll get a bit general on C++'s style of code here): in Ogre's code, there is a lot of legacy style, which explain the source of some design issues (old code always seem badder than shiny new code).
There is not a ton of Ogre team member which explains why the design and code is not improved a lot (I mean on the style side, not on the feature side of course), there is no time and people to focus on the style, they need to focus on the actual features first (even if changing the style might improve the speed of feature implementation, it remains to be proved). There is also a massive amount of code. I wish a future version of ogre would work on these problems but I fear it will not be in the coming 5 years. It might mean that there is an opportunity for another library to provide the same features than ogre but with a more recent style, or that there is too much features in ogre for that to happen. No idea what will actually happen but I do fear the statu quo (even with Ogre 2.0)

On the two-step initialization specifically, I would say that more than half of Ogre types doing it should not. However, most Ogre's implementation rely on them so changing that would break so much code that
it would require a massive refactoring, which is very hard to do without the consent of the team (I believe) and at least a year of spare time and back and forth reviews with the team.
Post Reply