N00b here: Why is there so much code in the Constructor?


#1

When I look through the samples (which have an odd format when copied through the clipboard) I see that way too much things are done in the constructors!

This clearly is not a good idea. But where could I put my code in else? In MFC I have several methods (e.g. OnInitDialog, etc.) where I can get active. Is there something comparable in Juce?
Of course such a message switch-case monster as with Win32 isn’t a good solution as well. But with a decent Event system this shouldn’t be a problem at all.

Thanx, pat :twisted:


#2

Don’t take this the wrong way, but if you’re such a noob then what gives you the right to critique Jules’ code? I write embedded software for a living (for about the last, oh, 15 years), and I think JUCE is extremely well designed and coded. Very logically laid out, and quite impressive technically.

But by no means is it sacrosanct or anything, I’m just saying that newbies should be humble and appreciative, and learn all they can.


#3

if there’s stuff that needs doing to an object on initialisation then what difference does it make if it gets called in the constructor or if it’s divided into functions that are also called at construction?

one thing to bear in mind is that the demo application is made up of demo components and are examples of the code that would achieve simple examples. any calls to ‘setBounds()’ and the like would be better off in the component’s resize() function. you can put your code in other places if you wish, but there’s nothing ‘clearly bad’ about how the demos are coded.

i’m not sure what your problem with it is but i think you’re a little paranoid! i’m sure you’re free to code to your own standards and designs regardless of how the demos are made. JUCE is an incredibly well coded and thought out library.


#4

Is there some golden rule about not doing too much in the constructor? I think there is but I’m no guru.
I tend to give complex classes an init() function that returns error/status or such like and the constructor just has an initialisation list and not much else.
For JUCE::Component derived classes I just create all contained Components in the constructor though.

I’m actually having to go the other direction from the original poster. I learned C++ with JUCE and now I’ve got a job I have to do MFC.

Which is shit.


#5

Just FYI that Microsoft have no big plans for MFC:

“Will Microsoft continue to support and add features to MFC?
Microsoft has no plans to discontinue support for MFC. New features will in most cases enable MFC applications to better interoperate with the .NET Framework.”


#6

Without trying to start a war here :wink: , let’s explain what are the main differences between MFC and Juce.

In MFC (except it is only for Win32) :
-=- Each window is an object.
-=- The framework hides (but not too much) the underlying platform. To achieve this, there is a lot of Win32 structure wrappers (which are not useful at all most of time).
-=- MFC intercept each call to the main Win32 procedure to map each Window object with the window handle (HWND). This is done dynamically, the mapping is stored in the TLS (thread local storage).
-=- This also means that there is a lot of things you can not do (like intercepting window creation (WM_NCCREATE), destruction (WM_NCDESTROY), interacting with the thread local storage) without ugly hacks.
-=- So, to allow you to do such things (as it is required for most applications), MFC developers created a lot of hierarchical callbacks (OnCreate, PreCreate, OnInitialUpdate and so on).

Looking from the outside, the constructors are not very useful because the creation, destruction etc, are done mainly in auxiliary methods you must implement (that’s why the ClassWizard does it for you not to forget).

MFC is old and not very well designed, bug-prone due to hidden features in ugly macros. On the opposite, MFC have a huge amount of classes so you should find the right class to do whatever you think of.

Okay, I won’t say that using MFC is like trying to kill a mosquito with a 50mm gun, but I’m thinking it.

In Juce, (cross platform) :
-=- Juce borrows many concept from Java. You should learn Java because it provides some “up-to-date” concepts and designs.
-=- Juce hides the platform specific code so that there is no more need to use wrappers like in MFC.
-=- Juce follows strict C++ standard.
-=- In C++ standard, you should construct an object in the constructor, and destruct it in the destructor. If you have a large amount of objects to constructs in your constructor it is mainly because you haven’t designed your classes well. Group by functionnality in specialized structure or class, simplify your code. It is not Juce’s fault if you code is ugly and large.
-=- If you have errors while constructing, throw an exception like specified in the standard, but don’t rely on “OnPreCreateBeforeRealCreationBeforeConstructorBeforeMalloc” hack included in MFC to handle your errors. This leads to cleaner code when debugging as you don’t have to dig into the MFC hierarchy.
-=- The Windows messages doesn’t exists anywhere else than in Windows. So why recreating a similar concept ?
-=- Any event is dispatched to the right component if it decided to receive it. It should then implement the correct interface (called a Listener, like in Java), and register with the sender component. Unlike MFC, messages are not always send & received, and you don’t have to implement any thing if you don’t need to.
-=- The component looks cool (as it is no more a Win32 default component).
-=- It is very simple to use.

I’ve tried many “platform specific” code (I’m still using MFC, but less often, ATL and WTL for Win32 only development), and many cross platform code.
If you don’t want to change your programming habits, switch to wxWidgets as it is closer to MFC.

If you want a true C++ cross platform GUI code, either choose Qt or Juce. Juce is more fun to use, but Qt is very well supported. They both implement many C++ concept MFC programmer don’t even know of (for example not using macros, but templates).

Don’t start flaming saying that Juce is complex and fatty. If you preprocess a MFC code (I mean expanding the macros), then you’ll see that MFC is largely fatter. Juce doesn’t include any construction in ugly (and not obvious) macros, so the code might looks bigger but it is clearly not the case. (Could you even tell me what the DECLARE_DYNCREATE or DDX_EXCHANGE macro do exactly ?).

With Juce, you know what the code does because it is not hidden.

The demonstration code doesn’t embed many struct because it has to fit in a single file. But you should (must) embed your data in a specialized object, add serialization / construction / destruction / assignement, and then (and only then) the construction code will be simplier. This doesn’t mean it is not there. This simply means it is correctly defined.

I hope you’ll try to understand the concepts above, and re-read the code, you’ll see it is very easy to start with. I’ve posted a ClassWizard like for dialog in the “Useful Tools” section today. Feel free to create the same dialog in Juce and MFC, and compare the code size you’ll see.

Sincerly,
Cyril


#7

thanks for the kind words, guys.

I can’t really see any terrible sin in putting code in the constructor. And if your class has no derived classes, then it really makes no difference at all whether you do stuff in the constructor or another method. So for these demos, it’s really not a bad thing at all. Makes it much easier to see what’s going on, I think.

In fact, having an init() method is less safe in some ways - you still have just as much chance to mess up by making calls that reference a not-yet-initialised variable, but you’ve also got to be extra-careful in the constructor because when it’s called, none of the superclasses will have been initialised properly. And there’s the danger that someone might forget to call init()… etc. Or forget to call the init() method of the parent class. etc. And although it avoids the pure-virtual method call problem, it doesn’t really fix it because such a call will be calling a not-yet-initialised base class, so will probably still crash something.

I do use init() methods for some of my classes, but I’d suggest deciding on a case-by-case basis, and don’t bother unless there’s actually a good reason for it.


#8

I wasn’t saying Juce is shit or bad code!! I only referred to 1 small spot in the demo codes. Demos, as I encountered it often, are not coded in the most effective or elegant way, so they can focus on short and easy to understand code. So please don’t take this as a personal offense!! This is sensless.

According to Mr. Scott Meyers (who is the guru I’m not :lol:) one should avoid doing any more then initializations in a constructor. It can lead to memory leaks or crashes if you ever encounter an exception there. Exception handling only cleans up (destroys) fully created objects. Yet it wouldn’t be able to handle the class in whose constructor the exception was thrown.

Of course, not every class needs an init(), I totally agree with you Jules. But on “window” classes (like widgets, dialogs, etc.) I need an Init() method which gets called automagically by the framework.
BTW: This Init() method doesn’t change in any way your coding style. You can continue to code in the constructor or use Init() instead. That’s the nice part about it - Everyone would be happy with it.

Thanx, pat le cleaner :twisted:

PS: Nowadays one can even openly critizise the pope, so I see no harm in my posting guys…


#9

Hey, criticism is good - bring it on!

Yeah, an unhandled exception in the constructor is bad, but so is an exception in the init() method… or any other, come to that!

In fact, I don’t think there’s any way of automatically calling a post-initialiser method - because the user is calling the new operator, you’d have to leave it up to them to call it, which they’d never ever remember to do. The only way would be for a new component to post a message to itself, but that’d be a nightmare, because then when you create a component, you wouldn’t be able to actually do anything with it until the message loop had run for a bit longer, so you’d have uninitialised components kicking around waiting to be used, extra messages + things happening in the wrong order. Nasty. MFC probably does it that way, or uses a macro or some kind of horrible hack to do it.

There is Component::parentHierarchyChanged(), which is called when a comp is added to another one (or removed), so you could use that.


#10

I’m gonna quote X-Ryl669’s post to my boss!
It’s taking me AGES to do anything in MFC cos I’ve got to learn it and it’s an obscure pile of shite.
I mean I’ve got this in house data capture app to write and it would be done by last week using JUCE! All this DDX crap and SHITHOLE_MAP crap is getting me down.


#11

I’m glad you understood my posting right.

Absolutely, but we are talking of 2 different technologies. 1 is the C++ standard and it’s implementation in compilers. 2 is a windowing framework, like in Juce. I was talking about the underlying C++ and it’s inability to handle exception correctly if occurring in constructors. This cannot be avoided unless you move “functional” code out of the ctor.
For #2, exceptions should not happen, yet they can be handled within your framework, so no problem here.

Not at all, this is an easy task actually :stuck_out_tongue: Unlike MFC and the even worse pure Win32, you don’t have to lay the burden of initializing and message/event registering onto the programer.
You can have “window” classes register themselves within their ctor. This way the programer has an Init() method, that gets called automagically by the framework, therefore he is not forced to call it manually, and since it has to be “virtual” he can even ignore it - meaning leave it empty.

BTW: There are many nice design-patterns that help with this. And I’ve found an excellent functor class for the event system, which squeezes jumps down to 2 (in words two) assembler calls.

[quote=“jules”]There is Component::parentHierarchyChanged(), which is called when a comp is added to another one (or removed), so you could use that.[/quote] Ok I see. But that wouldn’t be the right way.


#12

erm, what exactly would call this init() method? It can’t be called until the new operator has returned, and a that point you’re back in user code.[/code]


#13

Well the framework does. A button must be treated by Juce so it can be displayed. This can only happen after the object has been fully created. There you call the Init() of every “window” class, which either has some user code or is empty. I don’t know what the structure of Juce is or how the framework really works. It is to obscure to me and I find no help in documentation. But I suppose you do keep track somehow of what objects are created, especially displayable objects, this would be the registering I mentioned before. Then your framework must somehow do something with those objects, there the Init() should be called.


#14

like…

addAndMakeVisible( new Component() );

?


#15

I think its worth mentioning that MFC and wxWidgets use native windows for their windows, buttons, etc. in the process they get all the WM_XXX messages for each object/window.

JUCE (and many other frameworks, probably also Qt) have their own windows object and do not use native windows, they need to do all the windows management and widget drawing.

There are cons and pros to each method but it means that JUCE does not inherit the ‘framework’ behaviour from Windows like MFC does.


#16

This is wrong. I think you misunderstood Scott. The standard says that when leaving a constructor (either successfully, either due to a throw), the object must be either fully constructed, either fully destructed.

For example, see the following code:
CMyClass::CMyClass(whatever_arg_you_have) : initialisation_list
{
member_b = 12;
try
{
member_a = new ObjectA(some_stuff);
} catch (…) { throw; }
}

If an exception is thrown in the initialisation list, you’ll never reach CMyClass constructor, hence recursively, this means that when entering the constructor body, every object in the initialization list is correctly initialized.
In the constructor body, if something fails (throw an exception or bad return code), you simply rethrow the previous exception.

All new operators are clever enough to catch all exceptions during construction, and automatically calls the delete operator with the allocated memory. If you want to test this, compile that simple code :
tryme.cpp and move the throw© around to see what happens.

An example new operator would look like this :
::operator new(size_t size)
{
void * pointer = 0;
try{
pointer = malloc(size); // Or whatever memory allocator you think of
if (!pointer) return pointer;
new (pointer) ObjectCtr(); // Call the constructor on the memory at pointer
} catch(…)
{
::delete(pointer); // Call the destructor of created classes
// And free the heap to avoid memory leaking
throw; // rethrow the exception
}
return pointer;
}

And as I’ve written, any failing construction during initialisation list will be deleted.
See for MS compiler

Of course, if you’re afraid with the try / throw / catch code, you can embed every construction with a scope guard (like smart pointer, ScopeGuard)

This is clearly not a framework issue but a standard misunderstanding.

To reply to [quote]You can have “window” classes register themselves within their ctor. This way the programer has an Init() method, that gets called automagically by the framework, therefore he is not forced to call it manually, and since it has to be “virtual” he can even ignore it - meaning leave it empty[/quote]
making construction a two phase setup doesn’t guarantee anymore that there are no zombie object (allocated but not initialized) while the standard does.
Worst, what prevent then you from calling the Init method twice or more ?

Haven’t you already had a “ERROR: Object destructor called while window is still alive” debug message in MFC because you’ve posted a WM_QUIT message (PostQuitMessage()), and not followed the MFC way ?
This is because destruction (and creation) is made as a two step procedure in MFC, and nothing prevent you from making mistakes and calling the destructor directly.
In JUCE code, you won’t be able to do such error because the creation is done in constructors (you can’t call directly), and destruction is done in destructors (you can’t call directly too). In JUCE, either the object is initialized, either it is destructed.

JUCE is currently storing the thanks to the addAndMakeVisible, addToDesktop, … methods. This means that at no time an object is in an unknown state (unlike MFC and WTL).
This is very well designed, in fact it is like with Java AWT components.

Sincerly
Cyril


#17

[quote=“X-Ryl669”]According to Mr. Scott Meyers (who is the guru I’m not) one should avoid doing any more then initializations in a constructor. It can lead to memory leaks or crashes if you ever encounter an exception there. Exception handling only cleans up (destroys) fully created objects. Yet it wouldn’t be able to handle the class in whose constructor the exception was thrown.
This is wrong. I think you misunderstood Scott. The standard says that when leaving a constructor (either successfully, either due to a throw), the object must be either fully constructed, either fully destructed.[/quote]
No I meant exactly that. Maybe my english was not precise enough to describe what I meant. I was never talking about a 2 phase initialization of an object as such. Mr. Meyers tip remains true nevertheless in this particular case.

The class in whose ctor the exception is caught cannot be eliminated properly. Now think what happens if you call ctors of other classes in your ctor that act the same, you’ll have a lot of unproperly cleaned up code. Maybe new() can handle some of this, I must look into that first. But 1. not all objects are created on the heap, 2. the c++ standard defines a memory rollback only to happen once an object is fully created. And I believe Juce must stick to the standard, or it won’t be able to run under Linux or Mac.

Meyers also points out, that no smart pointer can help with this issue, which is totally logic.
About MS exception handling one must differenciate between c++ exceptions and the native SEH (Structured Exception Handling) of windows.

Simple, I wouldn’t call the Init() function ever. My english must be very bad, I thought I made thise clear enough more then once. So excuse my english again.

No I never have. MFC surely is an antique piece of junk. But once you understand it’s basics then I don’t see how this can happen.

[quote=“X-Ryl669”]In JUCE code, you won’t be able to do such error because the creation is done in constructors (you can’t call directly), and destruction is done in destructors (you can’t call directly too). In JUCE, either the object is initialized, either it is destructed.
JUCE is currently storing the thanks to the addAndMakeVisible, addToDesktop, … methods. This means that at no time an object is in an unknown state (unlike MFC and WTL).
This is very well designed, in fact it is like with Java AWT components.[/quote]
I wouldn’t call AWT the crown in GUI framework design. But this is just my personal opinion and this is not the place to discuss this or MFC in deep. I however are an adept of strong encapsulation in a framework and of a centralised management of objects/handlers/etc., in contrary of delegating this to the objects themselves (self-mgmt.)
Though we both agree about MFC being badly designed and maintained. Yet this doesn’t make every line/method of it evil :wink:

Greetz, pat le cat :twisted:


#18

I don’t agree with that. I don’t understand why catching an exception would lead to unproperly cleaned code. The construction is a step by step procedure. If any of those step fails, the whole object is left unconstructed because it is assured to be cleaned. What can happen, is that you don’t catch exceptions for an object that could throw. In that case, it’s your fault.

If I remember correctly, creating objects on the heap or on the stack is exactly the same when you construct them (and destruct them too). If a constructor throw, it will throw when called by new AND when instanced in any scope. It is your responsibility to catch the exception. If you don’t, you’ll be leaking, and this will happen in Init method too.

There is no memory rollback defined in C++ standard for throwing constructor, as the standard states (15.2 p294 Exceptions/Constructors and Destructors):

that either the object is fully constructed (and doesn’t throw an exception), either it is fully destroyed (and the exception handler will take care of calling the right destructors so that there isn’t a “unproperly cleaned up code”.

ScopeGuard is a “small” garbage collector, not a MS stuff. I only speaks about the C++ standard, as I never used SEH (except with the ugly “TRY/CATCH” MFC macros).
I highly recommend you play with the code I’ve mentioned above, as you’ll see by yourself that is not possible (if you follow the standard, ie catch exception where they are probably thrown, etc…) to have a zombie object (Not death, nor alive), even if you use new or automatic instanciation.

When I’ve written “What prevent you from”, I meant “What prevent a developper from”. I wasn’t critisizing, just thinking of new developpers with a Init method that could be called twice, or more and the associated nightmare (initialization flags, thread safety, etc…).

If you haven’t been trapped in MFC code yet, well, maybe you’re smarter than me, ever lucky, or you’re not using MFC like I do (and I don’t anymore)

Maybe I haven’t read Scott’s article you are refering to.
However, I never have add an issue with throwing constructors.

I guess I’ve misunderstood what you’ve said, please post an example where an Init method is better than a constructor.

Regards,
Cyril


#19