Board index » cppbuilder » How to write a second FormCreate for a derived form

How to write a second FormCreate for a derived form


2005-09-15 06:43:45 PM
cppbuilder111
Hello,
I have defined a base form, to handle dragging and docking, that however
contains a FormCreate method. When I derive new Forms from this dockable one,
those forms often need to contain their own FormCreate method. Defining a
derived FormCreate function always hides the base one, and will thus no longer
be called. I don't want to add an explicit line to the derived function that
calls the base function.
With e.g. the Paint function, one can write this:
//* In the TBaseDockableForm...
void __fastcall TBaseDockableForm::Paint( void )
{
//* Do some painting stuff...
if( OnPaint != NULL )
{
OnPaint( this );
}
}
//* And in the derived form...
void __fastcall TDerivedForm::FormPaint( TObject *Sender )
{
//* Do the derived painting...
//* The base painting stuff has already been performed
//* and we don't need to write an explicit call to it.
}
The Paint function of the TBaseDockableForm overrides the virtual base function,
and takes care of calling the derived function when it has been defined.
My question is:
Is there a similar way to do the same for the FormCreate function?
Thanks,
Wiljo
 
 

Re:How to write a second FormCreate for a derived form

On Thu, 15 Sep 2005 12:43:45 +0200, Wiljo wrote:
Quote
When I derive new Forms from this dockable one,
those forms often need to contain their own FormCreate method
No. Put all of that in the constructor.
Don't use FormCreate.
--
liz
 

Re:How to write a second FormCreate for a derived form

liz wrote:
Quote
On Thu, 15 Sep 2005 12:43:45 +0200, Wiljo wrote:


>When I derive new Forms from this dockable one,
>those forms often need to contain their own FormCreate method


No. Put all of that in the constructor.

Don't use FormCreate.
Thanks,
This is a good solution, if it were only that simple. It all comes down to the
order of execution. I would like the derived FormCreate, or constructor for that
matter, to be called before the FormCreate in the base form. When I put all my
stuff in both constructors, the base constructor is called before the derived
one. See this example:
//* In the UnitDockHost.cpp
void __fastcall TBaseDockHost::FormCreate( TObject *Sender )
{
//* Parse application components and collect
//* appropriate data from program ini file
...
}
//* In the UnitDerivedMainForm.cpp
void __fastcall TDerivedMainForm::FormCreate( TObject *Sender )
{
//* Create dockable forms
//* and do other stuff
//* Now call the base function
TBaseDockableForm::FormCreate( this );
}
This piece of example code demonstrates my problem. The derived form creates
instances of dockable Forms (Components in the application), then the base
FormCreate can be called to parse these components and read their data from an
ini file. I don't want to burden every form with reading it's stuff from the ini
file, and I don't want to burden every dockable form with an explicit call to
the base class.
I want my base code to be as abstract as possible. This base code handles
dockable forms, of any derived type, and docking hosts of three types: the main
window with docking panels, a tabbed form and a stacked form. I want the layout
of the desktop to be the same between program sessions. An ini file thus
contains the necessary information to do this, but I want the code to handle the
desktop layout hidden from these derived forms. When designing a dockable form,
the only thing I want to do is derive it from the base dockable form, and the
rest should be automatic.
Therefore I want a construction much like this:
//* In the UnitDockHost.cpp
void __fastcall TBaseDockHost::Create( TObject *Sender )
{
if( OnCreate != NULL )
{
//* Call the derived FormCreate function here
OnCreate( Sender );
}
//* Now we can parse the Application components
//* and read their stull from the ini file.
for( .... )
{
}
}
//* And in the UnitDerivedainForm.cpp
void __fastcall TDerivedMainForm::FormCreate( TObject *Sender )
{
//* Only create dockable forms here
//* or do some other stuff
}
Just writing a Create function in the base class will not work, because there is
no virtual base function to derive with this name. Therefore I want to know if
this construction is possible in another way, by maybe deriving another base
function.
thanks
Wiljo.
 

{smallsort}

Re:How to write a second FormCreate for a derived form

On Thu, 15 Sep 2005 14:35:42 +0200, Wiljo wrote:
Quote
I would like the derived FormCreate, or constructor for that
matter, to be called before the FormCreate in the base form. When I put all my
stuff in both constructors, the base constructor is called before the derived
one. See this example:
Yes. Because that's how c++ works.
Please rethink it, or even try it in Delphi.
And by the way, don't use FormCreate anyway, it really screws things
up in builder.
--
liz
 

Re:How to write a second FormCreate for a derived form

Wiljo < XXXX@XXXXX.COM >writes:
Quote
This is a good solution, if it were only that simple. It all comes
down to the order of execution.
Perhaps you should move some functionality out of the form classes,
and into helper classes. THen you may have greater control over when
they perform their actions.
Fix the design so that it works with constructors, don't even consider
using the FormCreate event. It is a way to ensure uncertainty and
flaws in your program. I think it only exists for Delphi, and is
certainly likely to cause harm to your application. It can even cause
crashes for no reason of your own, operating on objects before they
exist (resulting in highly undesirable undefiend behavior.)
--
Chris (TeamB);
 

Re:How to write a second FormCreate for a derived form

"Wiljo" < XXXX@XXXXX.COM >wrote in message
Quote
It all comes down to the order of execution. I would like the
derived FormCreate, or constructor for that matter, to be called
before the FormCreate in the base form.
Sorry, but that is not possible in C++. C++ does not work that way, and you
cannot make it work that way. A base class *must* be fully constructed
before a descendant class constructor will run.
Quote
When I put all my stuff in both constructors, the base
constructor is called before the derived one.
As it should be, because that is how C++ is designed to operate.
Quote
void __fastcall TBaseDockHost::FormCreate( TObject *Sender )
{
//* Parse application components and collect
//* appropriate data from program ini file
...
}
If you need your code to run after all constructors have been called, then
you need to override the AfterConstruction() method instead:
--- UnitDockHost.cpp ---
void __fastcall TBaseDockHost::TBaseDockHost(TComponent *Owner)
: TForm(Owner)
{
}
void __fastcall TBaseDockHost::AfterConstruction(void)
{
// Parse application components and collect
// appropriate data from program ini file
// ...
}
--- UnitDerivedMainForm.cpp ---
void __fastcall TDerivedMainForm::TDerivedMainForm(TComponent *Owner)
: TBaseDockHost(Owner)
{
// Create dockable forms
// and do other stuff
}
Gambit
 

Re:How to write a second FormCreate for a derived form

Remy Lebeau (TeamB) wrote:
Quote
Sorry, but that is not possible in C++. C++ does not work that way, and you
cannot make it work that way. A base class *must* be fully constructed
before a descendant class constructor will run.

I fully agree with you.

If you need your code to run after all constructors have been called, then
you need to override the AfterConstruction() method instead:

--- UnitDockHost.cpp ---

void __fastcall TBaseDockHost::TBaseDockHost(TComponent *Owner)
: TForm(Owner)
{
}

void __fastcall TBaseDockHost::AfterConstruction(void)
{
// Parse application components and collect
// appropriate data from program ini file
// ...
}

--- UnitDerivedMainForm.cpp ---

void __fastcall TDerivedMainForm::TDerivedMainForm(TComponent *Owner)
: TBaseDockHost(Owner)
{
// Create dockable forms
// and do other stuff
}


Gambit


Thanks very much. Using the AfterConstruction method worked like a charm, but as
you might expect I need the same on destruction. Alas the BeforeDestruction
function is called too late and the program crashes on exit.
After some investigation I came up with the following nice solution. Instead of
using AfterConstruction and BeforeDestruction, I derive the functions DoCreate
en DoClose in the base form:
--- UnitDockHost.cpp ---
void __fastcall TBaseDockHost::DoCreate( void )
{
if( OnCreate != NULL )
{
//* Call the derived FormCreate function, that
//* is responsible for creating dockable forms
OnCreate( this );
}
//* Now all forms exist, I can load the last desktop
//* configuration
LoadDesktopConfiguration();
}
void __fastcall TBaseDockHost::DoClose( TCloseAction &Action )
{
//* Before the derived form destroys the dockable forms
//* save the current desktop configuration.
SaveDesktopConfiguration();
if( OnClose != NULL )
{
OnClose( this, Action );
}
}
--- UnitDerivedMainForm.cpp ---
void __fastcall TDerivedMainForm::FormCreate( TObject *Sender )
{
//* Create dockable forms required for this program
}
void __fastcall TDerivedMainForm::FormClose( TObject *Sender,
TCloseACtion &Action )
{
//* Destroy dockable forms
}
I have been programming for several years with C++Builder6 and never had a
problem with using the FormCreate function. The FormDestroy function however
does cause some strange behaviour, so it's best to avoid that. When the MainForm
closes it usually closes the Application as well.
Thanks for all your advise.
Wiljo.
 

Re:How to write a second FormCreate for a derived form

"Wiljo" < XXXX@XXXXX.COM >wrote in message
Quote
Alas the BeforeDestruction function is called too late
I doubt that. BeforeDestruction() is called before any destructor is
called.
Quote
and the program crashes on exit.
Then you are not using it correctly. Please show your actual code.
Quote
After some investigation I came up with the following nice solution.
It is not as nice as you think.
Quote
Instead of using AfterConstruction and BeforeDestruction, I
derive the functions DoCreate en DoClose in the base form:
You should not be doing that. Those methods are NOT stable in C++.
DoCreate() can be called by TCustomForm's constructor before any descendant
constructor has been called. Which means that DoCreate() will be called
before TBaseDockHost is created, and thus your code will execute on an
invalid object because none of the descendants have been created yet.
Likewise, DoDestroy() can be called after all descendant destructors have
been called, so again your code will execute on an invalid object because
all of the descendants have already been destroyed.
Quote
I have been programming for several years with C++Builder6 and
never had a problem with using the FormCreate function.
Consider yourself lucky then, because OnCreate is VERY problematic on all
versions of BCB. Many people have reported many problems with it. It is
HIGHLY advisable that you avoid the OnCreate and OnDestroy events at all
costs. They simply do not work properly in the C++ environment. They are
only useful in Delphi where the creation order of objects is reverse from
that of C++.
Gambit
 

Re:How to write a second FormCreate for a derived form

Remy Lebeau (TeamB) wrote:
Quote
"Wiljo" < XXXX@XXXXX.COM >wrote in message
news:432aad3d$ XXXX@XXXXX.COM ...

>Alas the BeforeDestruction function is called too late

I doubt that. BeforeDestruction() is called before any destructor is
called.

>and the program crashes on exit.

Then you are not using it correctly. Please show your actual code.

Well, I don't have any code anymore to show, but the problem is as follows:
In the constructor of my MainForm I create several new forms, with the
Application as owner. When my program exits, I want to save the current desktop
configuration, and I tried to use the MainForm's BeforeDestruction. This
function accesses the attributes of the other forms, but they apparently were
already destroyed, hence the crash on exit. That is also one reason I don't like
to call the OnDestroy event, because the derived class has already been destructed.
Quote

>After some investigation I came up with the following nice solution.

It is not as nice as you think.

>Instead of using AfterConstruction and BeforeDestruction, I
>derive the functions DoCreate en DoClose in the base form:

You should not be doing that. Those methods are NOT stable in C++.
DoCreate() can be called by TCustomForm's constructor before any descendant
constructor has been called. Which means that DoCreate() will be called
before TBaseDockHost is created, and thus your code will execute on an
invalid object because none of the descendants have been created yet.
Likewise, DoDestroy() can be called after all descendant destructors have
been called, so again your code will execute on an invalid object because
all of the descendants have already been destroyed.

>I have been programming for several years with C++Builder6 and
>never had a problem with using the FormCreate function.

Consider yourself lucky then, because OnCreate is VERY problematic on all
versions of BCB. Many people have reported many problems with it. It is
HIGHLY advisable that you avoid the OnCreate and OnDestroy events at all
costs. They simply do not work properly in the C++ environment. They are
only useful in Delphi where the creation order of objects is reverse from
that of C++.

I understand what you are explaining here. I have encountered the same with
other classes I have written. Especially when calling a (pure) virtual function
from the destructor of a base class. Although a pure virtual function must be
derived, I no longer exists on destruction in the base destructor (or on
construction in the constructor of the base for that matter).
So taking all what you have written into consideration, I will try an find
another solution to my problem. I guess that the AfterConstruction is a good
starting point.
Thanks very much for your advise.
Quote

Gambit


 

Re:How to write a second FormCreate for a derived form

"Wiljo" < XXXX@XXXXX.COM >wrote in message
Quote
When my program exits, I want to save the current desktop configuration,
and I tried to use the MainForm's BeforeDestruction. This function
accesses
the attributes of the other forms, but they apparently were already
destroyed,
hence the crash on exit.
Correct. When a component (the Application in this case) is being
destroyed, it frees the components that it owns (the forms) in the *reverse
order* that they are inserted into the owning component (the Application).
So, when your MainForm creates another form that is also owned by the
Application, that form will be destroyed before the MainForm.
A simple solution is to main the MainForm be the owner of the secondary
forms instead of the Application. Then they will remain valid until the
MainForm is actually destroying itself, which is long after
BeforeDestruction() has been called, or even the destructor for that matter.
Quote
I understand what you are explaining here. I have encountered the same
with
other classes I have written. Especially when calling a (pure) virtual
function
from the destructor of a base class. Although a pure virtual function must
be
derived, I no longer exists on destruction in the base destructor (or on
construction in the constructor of the base for that matter).
Constructors and destructors cannot call pure virtual methods that are
abstract in the calling class but overriden in descendants. The descendants
do not exist at those stages.
Gambit