Jump to content

  • Log In with Google      Sign In   
  • Create Account


One-Step vs Two-Step Initialization (C++)


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
33 replies to this topic

#1 metsfan   Members   -  Reputation: 652

Like
0Likes
Like

Posted 15 July 2012 - 10:14 PM

Hi all, I am currently in the process of making some critical decisions for my game's code, and deciding on paradigms that I am going to stick with throughout the project and I just wanted to get some opinions on this subject. As far as I can tell, there are positives and negatives to both of these methods. I've done some research around the internet, and it seems to be a pretty split decision about which practice is better in general, so I put the question to all of you, which practice do you prefer and use in your games, and do you differ the practice used based on situation?

For those who are not familiar with this terminology:

One-Step Initialization - Constructor sets up the entire class and throws an exception on failure
Two-Step Initialization - Constructors simply allocates the object and all initialization is done through a separate method, which will usually have a return value of a boolean or some error code.

Thanks all.

Sponsor:

#2 King Mir   Members   -  Reputation: 1909

Like
0Likes
Like

Posted 15 July 2012 - 11:15 PM

I would say one step, as much as possible. Exceptions make for cleaner code.

#3 rnlf   Members   -  Reputation: 1092

Like
1Likes
Like

Posted 15 July 2012 - 11:45 PM

I prefer the same. Do as much as possible in the constructor and throw an exception if anything goes wrong. If you use return values to indicate failure, you can be sure to forget to check it every once in a while and you will get unpredictable behaviour sooner or later. If you forget to catch an exception, you get at least a well defined shutdown of your application.

my blog (German)


#4 SiCrane   Moderators   -  Reputation: 9387

Like
1Likes
Like

Posted 16 July 2012 - 12:00 AM

This isn't a one-size-fits-all situation. There are many platforms with sufficiently bad exception support that even enabling exceptions as a compiler options is a mistake. Even if you do enable exceptions in your project, there are certain operations that have a sufficiently high chance of failure that throwing an exception would be inappropriate. Examples include opening a file or a network connection. Consider the C++ standard library. With a std::fstream you can specify a file name as constructor argument, but if the file isn't opened, it doesn't throw an exception. In comparison, if any of the std::string constructors fail an exception is thrown.

#5 Hodgman   Moderators   -  Reputation: 27585

Like
4Likes
Like

Posted 16 July 2012 - 12:06 AM

IMHO - when writing C++ code:
* Never use C++ exceptions (do use C SEH for crash report handling though).
* For C++ classes, use the C++ idiom of constructors/destructors instead of the C idiom of init/shutdown function.
* For C-style data structures, use the C idioms where applicable.
* Design as much code as possible so that failure simply isn't possible, then you don't need error handling code...
** If for some reason a constructor can fail (which usually is just a bad design), you do have the option of returning an error though an out param, or setting a 'zombie object' flag.

[Edit] Time travelling quote because I'm too lazy to add a new reply and start another "why C++ exceptions are great and/or evil" holy war thread -- there's enough of them already.

The specific domain here is game programming, so the bad exception support platforms aren't likely to be used.

Game consoles are the platforms that have bad C++ exception support. Microsoft/Sony tell you to disable exception support on their compilers in order to produce better binary code. Even x86/Win32 is pretty crap here, though x64 Windows is a lot better.

Edited by Hodgman, 16 July 2012 - 02:27 AM.


#6 King Mir   Members   -  Reputation: 1909

Like
0Likes
Like

Posted 16 July 2012 - 12:41 AM

This isn't a one-size-fits-all situation. There are many platforms with sufficiently bad exception support that even enabling exceptions as a compiler options is a mistake. Even if you do enable exceptions in your project, there are certain operations that have a sufficiently high chance of failure that throwing an exception would be inappropriate. Examples include opening a file or a network connection. Consider the C++ standard library. With a std::fstream you can specify a file name as constructor argument, but if the file isn't opened, it doesn't throw an exception. In comparison, if any of the std::string constructors fail an exception is thrown.

The specific domain here is game programming, so the bad exception support platforms aren't likely to be used.

I don't agree with these examples of when not to use exceptions. If a critical resource, like a file is missing, that might be a good time to throw an exception.

Also a major consideration if an exception is appropriate is the cause of the error. If it is user error, then that's almost always not cause to use an exception. If it's a terminally missing resource, then throw one. Loading a missing save file -- don't throw. Corrupted save data - likely throw.

As an aside, one thing to watch out for is that you don't want to throw an exception in an exception handing routine. So don't make failure to log an error an exception, for example.

#7 Muzzy A   Members   -  Reputation: 622

Like
0Likes
Like

Posted 16 July 2012 - 12:50 AM

i definately prefer 2 step, to me it makes code look nice, and it's more flexible on it's initialization and makes re-initializatioin even easier. And in c++ if you're old school you probably use memalloc,calloc, or what have you, which dont call constructors anyway.

#8 Krohm   Crossbones+   -  Reputation: 2960

Like
0Likes
Like

Posted 16 July 2012 - 01:01 AM

This isn't a one-size-fits-all situation. There are many platforms with sufficiently bad exception support that even enabling exceptions as a compiler options is a mistake.

For further elaboration. I've sometimes found myself to have the need to work with "non-trivial" ctors. Those kind of ctors that need object-specific behaviour by calling virtual functions.
Granted, I've mostly refactored those. But there are still a few cases in my current codebase where reliance on "virtual-based init" was the only possible way.
In this case, one-step init is just impossible.

The specific domain here is game programming,

Let me rephase this for you.
The specific domain here is jogging - where? city, offroad, mixed, gym. How? speed-oriented, long distances, cardio, weight loss...

#9 King Mir   Members   -  Reputation: 1909

Like
0Likes
Like

Posted 16 July 2012 - 01:51 AM


This isn't a one-size-fits-all situation. There are many platforms with sufficiently bad exception support that even enabling exceptions as a compiler options is a mistake.

For further elaboration. I've sometimes found myself to have the need to work with "non-trivial" ctors. Those kind of ctors that need object-specific behaviour by calling virtual functions.
Granted, I've mostly refactored those. But there are still a few cases in my current codebase where reliance on "virtual-based init" was the only possible way.
In this case, one-step init is just impossible.

The specific domain here is game programming,

Let me rephase this for you.
The specific domain here is jogging - where? city, offroad, mixed, gym. How? speed-oriented, long distances, cardio, weight loss...

There are indeed case where One-Step Initialization is not a good idea or even possible, but they should be the exception, not the rule.

As to the other point, I don't think see what you're trying to tell me. Yes game programming is a broad domain, but what I said about it still holds.

#10 Drew_Benton   Crossbones+   -  Reputation: 1713

Like
1Likes
Like

Posted 16 July 2012 - 05:32 AM

I would say one step, as much as possible. Exceptions make for cleaner code.


I prefer the same. Do as much as possible in the constructor and throw an exception if anything goes wrong. If you use return values to indicate failure, you can be sure to forget to check it every once in a while and you will get unpredictable behaviour sooner or later. If you forget to catch an exception, you get at least a well defined shutdown of your application.


What makes you think that you can't have your "initialize" function throw an exception? The concept of return codes is just one of the two ways to implement two step Initialization. If you prefer one step initialization, great! But why? If you believe exceptions make for cleaner code, and throwing an exception from an initialize function is perfectly valid and typical of C++ exception styled programming, then you haven't made an argument for either yet. Poorly constructed exception handling is no better than poorly constructed non-exception handling either, so saying you have a "well defined shutdown of your application" is not always true.

What SiCrane said about "This isn't a one-size-fits-all situation." is pretty much the main focus point that should be addressed. Looking past exceptions, which is only one of the main aspects to this issue, the cost of "creation, copy, and destruction" have to be kept in mind as well. How expensive is it to create, copy, or destroy your objects? Are your objects going to need pools for managing memory? It's just going to depend on a case by case basis, thus there is no one-size-fits-all solution.

Furthermore, why choose one or the other when both might be more appropriate? Resource Acquisition Is Initialization, RAII, is one such idiom where inherently it is one step initialization, but there are times when adding support for two step initialization can make for more flexible and simplified code. A specific example would be Win32 critical sections (whether you use them or not isn't important as much as understanding the justification). Rather than littering your code with Enter/LeaveCriticalSection calls, you can use RAII to greatly simplify the process. However, there might be times when you need the concept of RAII, but the implementation doesn't make logical sense. E.g., acquiring exclusive access to a CS, but not needing to maintain that access at all times (to prevent deadlocks), so allowing for two step initialization with manual cleanup code support helps keep things more simple/cheap than constructing/destructing new objects over and over.

It's typical in game development, and most other development, to make uses of 3rd party libraries at one point or another. Maybe it's a mysql++ connector wrapper, or a physics library, or an input or audio library, the list goes on. While a lot of people have a DIY mentality, it's important to understand that you can't simply reinvent the wheel for everything and will eventually have to resort to another person's code. A lot of these libraries might be setup to use exceptions, while some might not. In either case, you have to design your code around how the other components are setup.

This means, specifically to the OP, you should not be trying to "commit" to one style or another, especially in a language like C++. It's like saying you want to have dinner tonight, but don't want to eat a meal that would require the use of a fork. To each their own, of course, but it's certainly not a typical concern from that perspective to say the least.

"But I, being poor, have only my dreams. I have spread my dreams under your feet; tread softly, because you tread on my dreams." - William Butler Yeats

#11 CulDeVu   Members   -  Reputation: 535

Like
0Likes
Like

Posted 16 July 2012 - 06:11 AM

Game consoles are the platforms that have bad C++ exception support. Microsoft/Sony tell you to disable exception support on their compilers in order to produce better binary code. Even x86/Win32 is pretty crap here, though x64 Windows is a lot better.


Even x64 Windows has had serious problems in its exception and critical error handling in the past (and present). One such example was described recently by Bruce Dawson on AltDevBlogADay:
http://www.altdevblogaday.com/2012/07/06/when-even-crashing-doesnt-work/
I'm sorry about any spelling or grammar mistakes or any undue brevity, as I'm most likely typing on my phone :P

#12 rnlf   Members   -  Reputation: 1092

Like
0Likes
Like

Posted 16 July 2012 - 07:23 AM

Hm. Okay, I never had to deal with such broken platforms. If this is a serious concern, you're pretty much screwed anyway. Never noticed any of these problems on unix(-derivates). Didn't know, Windows would be such a deal breaker (again?).

If you only aim at Windows or consoles, maybe it may be worth considering not to use exceptions, but I'd rather hope for the vendors to fix their compilers (and operating systems) some day and risk sub-par performance until then.

BTW, the article seems to describe problems with exceptions generated by Windows, not exceptions generated by user code. Are there any known issues with user generated exceptions? What about non-Microsoft compilers?

Edited by rnlf, 16 July 2012 - 07:28 AM.

my blog (German)


#13 Hodgman   Moderators   -  Reputation: 27585

Like
2Likes
Like

Posted 16 July 2012 - 07:55 AM

Even x64 Windows has had serious problems in its exception and critical error handling in the past (and present). One such example was described recently by Bruce Dawson on AltDevBlogADay:
http://www.altdevblo...ng-doesnt-work/

That's about Windows' SEH (which I mentioned earlier as being a good tool for dealing with crashes), not at all anything to do with C++ exceptions (the try/catch/throw language feature that your quoted statement was about).

N.B. these two different kind of exceptions live side-by-side. You can make use of SEH and C++ exceptions in the same program, or just use one or the other, or neither. SEH exceptions are raised by illegal operations that normally crash your program (e.g. trying to write to a NULL pointer) or by calling the Windows RaiseException function. C++ exceptions are raised by the throw keyword.

e.g. Despite my personal choice to never use try/catch/throw ever in C++ (I do use it in other languages where it's implemented better), I do choose to use __try/__except on Windows to generate crash reports and to propagate errors from worker-threads back to the main thread, at which time Bruce Dawson's articles are quite helpful ;)

Edited by Hodgman, 16 July 2012 - 08:13 AM.


#14 Promit   Moderators   -  Reputation: 6087

Like
3Likes
Like

Posted 16 July 2012 - 09:14 AM

I've taken an increasingly dim view of exceptions and pure RAII approaches in games, particularly given the headaches that C++ introduces as far as exception handling. Even with "flawless" platform level support for exception handling, I don't feel that constructors with exceptions are the way to go.

The existence of exceptions at all demands a sane exception handling strategy AND exception safe code. Exceptions are supposed to represent "exceptional" conditions and it turns out that in games, most of those conditions are common in day-to-day development. Since you're not going to crash the game just because an artist mis-named a file, the handling code typically degrades the exception to a return code anyway, swallowing it internally and taking some action like injecting a placeholder. Stack unwind isn't useful here either, since it's invariably caught by the immediate parent. All you've done at this point is blow a bunch of cycles on an internal error condition.

Then there's the practical problems with exceptions in C++ in particular, and the very fact that the term "exception-safe code" exists should be a giant red flag. There are code size consequences to enabling exceptions, and the way the standard or boost libraries reacts to the setting can mess with things too. There's also the question of whether it will interact cleanly with threads. (Checked iterators are globally re-entrant incompatible, for example. Think about that for a bit.) Let's not forget the problems with C++ constructors being shitty, either. Virtual dispatch won't work, chaining won't work, etc.

Last, it's helpful to be able to re-initialize objects and although you can do it with the explicit destructor call plus placement new, doing that just feels weird. Combined with the fact that other languages like C# won't support in-place reinitialization anyway, and explicit init and teardown functions just look that much more appealing.

I just like two-step better.

#15 Cornstalks   Crossbones+   -  Reputation: 6966

Like
1Likes
Like

Posted 16 July 2012 - 09:39 AM

One-Step Initialization - Constructor sets up the entire class and throws an exception on failure
Two-Step Initialization - Constructors simply allocates the object and all initialization is done through a separate method, which will usually have a return value of a boolean or some error code.

I don't think this can properly be answered without asking: what can fail? and why can it fail? and when will it fail?

Things fail, but where they fail is a matter of design (which you're considering now). However, I think you need to consider the above if you're ever to meaningfully decide where something fails. I personally try to keep my initializations minimal so that failure in the constructor is both exceptional and fatal. "Actions" are more likely to fail (i.e. when the object actually does something or "acts"), and I try to put these in member methods (not as "secondary initialization" functions but as logical, "acting" member methods).
[ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

#16 JimC1664   Members   -  Reputation: 179

Like
0Likes
Like

Posted 16 July 2012 - 10:40 AM

I hate this topic, before I was aware of it I was oh-so much happier.
Darn it. I liked my try-catch blocks they looked.. neat.

#17 BinaryPhysics   Members   -  Reputation: 294

Like
0Likes
Like

Posted 16 July 2012 - 10:57 AM

One-Step Initialization - Constructor sets up the entire class and throws an exception on failure


My understanding was that this left the memory in a completely undefined state (please correct me if I'm wrong).

My method is to use the constructor to set built-in types to default values and pointers to NULL. Bigger operations are performed in separate functions that must be called explicitly.

Edited by BinaryPhysics, 16 July 2012 - 10:58 AM.


#18 Telastyn   Crossbones+   -  Reputation: 3718

Like
1Likes
Like

Posted 16 July 2012 - 11:11 AM

Classes that can fail construction is (to me) a fairly stinky code smell. Have sane defaults makes the code far easier to reason about, work with, test, and refactor.

In the rare case where the class really needs some initial state provided in the constructor and that initial state can have some bad value (null, NaN, etc) then just throw an exception there. If you're on a platform that doesn't support exceptions well... I have no idea. I've never had to deal with them; I'd be inclined to have a factory return the object or null.

#19 King Mir   Members   -  Reputation: 1909

Like
0Likes
Like

Posted 16 July 2012 - 11:17 AM

One-Step Initialization - Constructor sets up the entire class and throws an exception on failure


My understanding was that this left the memory in a completely undefined state (please correct me if I'm wrong).

Nope. Constructors throwing exceptions is well defined and safe. If an object you create holds a resource, then some care must be taken to insure that the resource is either acquired and released or not acquired at all, but that's easy enough if you wrap the resource in the smallest object necessary to handle it. For example, you would use two std::vector objects instead of managing two dynamic arrays in the same object manually.


I would say one step, as much as possible. Exceptions make for cleaner code.

I prefer the same. Do as much as possible in the constructor and throw an exception if anything goes wrong. If you use return values to indicate failure, you can be sure to forget to check it every once in a while and you will get unpredictable behaviour sooner or later. If you forget to catch an exception, you get at least a well defined shutdown of your application.

What makes you think that you can't have your "initialize" function throw an exception? The concept of return codes is just one of the two ways to implement two step Initialization. If you prefer one step initialization, great! But why? If you believe exceptions make for cleaner code, and throwing an exception from an initialize function is perfectly valid and typical of C++ exception styled programming, then you haven't made an argument for either yet. Poorly constructed exception handling is no better than poorly constructed non-exception handling either, so saying you have a "well defined shutdown of your application" is not always true.

The OP specifically mentioned using error codes instead of exceptions in two phase initialization. It doesn't make sense to use two phase initialization when there is no explicit benefit. Yes it allows virtual dispatch and returning an error instead of an exception, but when these features aren't used, then there's no point to it. In the absence of any other consideration I would say one phase initialization is better because it's less code, and less places where an error can occur. These reasons are why I suggest using one pass initialization in the general case. I do agree that it's not "one size fits all" though.

As an aside virtual dispatch for initialization can also be achieved by factory objects.

#20 ddn3   Members   -  Reputation: 1248

Like
0Likes
Like

Posted 16 July 2012 - 12:42 PM

Game initialization is usually multi-pass. Most games these days are not simple enough to do all their initialization within a single constructor pass. Sometimes u want to create objects in memory and push in saved state and then do some pointer fix up, such complex multi-pass initialization schemes preclude doing it all in the constructor. My games are multi-pass, there are object pools, script based multi-state initialization, soft reset, etc.. But don't over-engineer, if your game can get away with a single pass constructor go for it. As others have said its really dependent upon the requirements. Good Luck!

Edited by ddn3, 16 July 2012 - 12:43 PM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS