Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: #pragma startup execution order is weird!


This question is answered.


Permlink Replies: 5 - Last Post: Jun 1, 2016 4:19 PM Last Post By: Remy Lebeau (Te...
Ahmed Sayed

Posts: 173
Registered: 8/9/07
#pragma startup execution order is weird!  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 1, 2016 10:02 AM
Hi

I am having a problem while using (#pragma startup) in
two of my units. One that create a class that register
other __classid and in the other unit i use that created
class to register a TFrame descendant.

Here is the code:

//Header 
 
//---------------------------------------------------------------------------
#ifndef ClassFactoryH
#define ClassFactoryH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <System.SysUtils.hpp>
#include <System.IOUtils.hpp>
#include "MapList.h"
#include "Strings.h"
//---------------------------------------------------------------------------
typedef TMapList<UnicodeString,TClass> TRegisteredClassList;
//---------------------------------------------------------------------------
class TClassFactory : public TObject
{
	typedef TObject inherited;
 
private:
//Fields
TRegisteredClassList *FRegisteredClassList;
 
//---------------------------------------------------------------------------
protected:
//Setters/Getters
virtual bool __fastcall GetClassExists(UnicodeString AClassName);
virtual TClass __fastcall GetRegisteredClass(UnicodeString AClassName);
virtual int __fastcall GetCount();
 
//Methods
virtual void __fastcall DoRegisterClass(UnicodeString AClassName, TClass AClass);
virtual void __fastcall DoUnRegisterClass(UnicodeString AClassName);
 
//---------------------------------------------------------------------------
public:
//Fields/Properties
__property int Count = {read = GetCount};
__property TRegisteredClassList *RegisteredClassList = {read = FRegisteredClassList, write = FRegisteredClassList};
__property bool ClassExists[UnicodeString AClassName] = {read = GetClassExists};
__property TClass RegisteredClass[UnicodeString AClassName] = {read = GetRegisteredClass};
 
//Constructor / Destructor.
__fastcall TClassFactory();
virtual __fastcall ~TClassFactory(void);
 
//Methods
virtual void __fastcall RegisterClass(UnicodeString AClassName, TClass AClass);
virtual void __fastcall UnRegisterClass(UnicodeString AClassName);
 
//---------------------------------------------------------------------------
};
//---------------------------------------------------------------------------
extern PACKAGE TClassFactory *ClassFactory;
//---------------------------------------------------------------------------
#endif
 
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "ClassFactory.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
TClassFactory *ClassFactory;
//---------------------------------------------------------------------------
__fastcall TClassFactory::TClassFactory()
	: TObject()
{
FRegisteredClassList = new TRegisteredClassList;
}
//---------------------------------------------------------------------------
__fastcall TClassFactory::~TClassFactory(void)
{
if (FRegisteredClassList)
	delete FRegisteredClassList;
}
//---------------------------------------------------------------------------
/*Important Methods*/
void __fastcall TClassFactory::DoRegisterClass(UnicodeString AClassName, TClass AClass)
{
System::Classes::RegisterClass(AClass);
FRegisteredClassList->Add(AClassName, AClass);
}
//---------------------------------------------------------------------------
void __fastcall TClassFactory::DoUnRegisterClass(UnicodeString AClassName)
{
System::Classes::UnRegisterClass(GetRegisteredClass(AClassName));
FRegisteredClassList->Item[AClassName] = NULL;
FRegisteredClassList->Delete(AClassName);
}
//---------------------------------------------------------------------------
void __fastcall TClassFactory::RegisterClass(UnicodeString AClassName, TClass AClass)
{
if (GetClassExists(AClassName))
	return;
 
DoRegisterClass(AClassName,AClass);
}
//---------------------------------------------------------------------------
void __fastcall TClassFactory::UnRegisterClass(UnicodeString AClassName)
{
if (GetClassExists(AClassName))
	{
	DoUnRegisterClass(AClassName);
	}
}
//---------------------------------------------------------------------------
/*Setters - Getters*/
int __fastcall TClassFactory::GetCount()
{
return FRegisteredClassList->Count;
}
//---------------------------------------------------------------------------
bool __fastcall TClassFactory::GetClassExists(UnicodeString AClassName)
{
return FRegisteredClassList->Exists(AClassName);
}
//---------------------------------------------------------------------------
TClass __fastcall TClassFactory::GetRegisteredClass(UnicodeString AClassName)
{
TClass AClass = NULL;
 
if (GetClassExists(AClassName))
	{
	AClass = FRegisteredClassList->Item[AClassName];
	}
return AClass;
}
//---------------------------------------------------------------------------
void ClassFactoryInit(void)
{
ShowMessage(__FUNC__);
ClassFactory = new TClassFactory;
}
#pragma startup ClassFactoryInit     //This get called second not first
void ClassFactoryUninit(void)
{
ClassFactory->Free();
}
#pragma exit ClassFactoryUninit
 
 
//In another TFrame unit
 
 
void RegisterClientFrame(void)
{
ShowMessage(__FUNC__);
 //Raises exception here because ClassFactory is not created yet.
ClassFactory ->RegisterClass("frame1", __classid(TFrame1));  
}
#pragma startup RegisterClientFrame     //This gets called first before ClassFactoryInit
void UnregisterClientFrame(void)
{
ClassFactory ->UnRegisterClass("frame1");
}
#pragma exit UnregisterClientFrame


Even if changed the priority like this:

#pragma startup ClassFactoryInit 70
#pragma startup RegisterClientFrame 75


They get executed in the same wrong order.
I tested the execution order by using ShowMessage(__FUNC__);

This is weird, because when I change anything in
the "RegisterClientFrame" function like commenting this line:

ClassFactory ->RegisterClass("frame1", __classid(TFrame1)); 


Then build the whole project then un-comment it again and then
re-build the project it works fine and the program runs perfectly.

But when ever i do any kind of change in the main form unit it
gets back to raising the same error.

I tried to change units building order but with no luck either.

I am going nuts. In fact i have another project with a similar
functionality but with TDataModule not TFrame and it works
like a charm. I even reversed the order to force the wrong
function be called first but the IDE made the correct calling
order despite what i did.

#pragma startup RegisterClientFrame 0  //even with priority 0 this is called second
#pragma startup ClassFactoryInit 75 //this is called first


Any help would be appreciated.

--
The limits of my language mean the limits of my world
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: #pragma startup execution order is weird! [Edit]
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 1, 2016 12:56 PM   in response to: Ahmed Sayed in response to: Ahmed Sayed
Ahmed wrote:

I am having a problem while using (#pragma startup) in two of my
units.

The order in which startup procedures are called is not guaranteed unless
you assign priorities to them.

One that create a class that register other __classid and in
the other unit i use that created class to register a TFrame
descendant.

Rather than use a startup procedure to initialize the global ClassFactory
variable, I would suggest getting rid of the 'extern' declaration altogether
so the variable is not publically accessible anymore, and then use a standalone
function, or even a static class method, to retreive the factory when needed:

class TClassFactory
{
    ...
};
 
TClassFactory* GetClassFactory();


static std::auto_ptr<TClassFactory> ClassFactory(new TClassFactory);
 
TClassFactory* GetClassFactory()
{
    return ClassFactory.get();
}


Or:

class TClassFactory
{
    ...
public:
    static TClassFactory* GetClassFactory();
};


TClassFactory* TClassFactory::GetClassFactory()
{
    static std::auto_ptr<TClassFactory> ClassFactory(new TClassFactory);
    return ClassFactory.get();
}


Eitehr way, other units can then do this:

void RegisterClientFrame(void)
{
    GetClassFactory()->RegisterClass("frame1", __classid(TFrame1));
    // or: TClassFactory::GetClassFactory()->RegisterClass("frame1", __classid(TFrame1));
}


#pragma startup ClassFactoryInit 70
#pragma startup RegisterClientFrame 75

They get executed in the same wrong order.

Then that would be a bug that needs fixing.

I tested the execution order by using ShowMessage(__FUNC__);

I would not use ShowMessage() for that. DebugOutputString() would be much
safer.

BTW, why are you manually re-creating what the RTL's native RegisterClass()
functionality already provides?

--
Remy Lebeau (TeamB)
Ahmed Sayed

Posts: 173
Registered: 8/9/07
Re: #pragma startup execution order is weird! [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 1, 2016 2:00 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for the fast reply. I am just creating another
registering mechanism because there will be an extra
functionality for TClassFactory and its descendants.

But can i ask you where should i put these exactly:
I know the declaration in the header file

class TClassFactory
{
    ...
};
 
TClassFactory* GetClassFactory();


But what about this line with auto_ptr i know it goes in the cpp file
i didn't use auto_ptr before when this ClassFactory will get destroyed

static std::auto_ptr<TClassFactory> ClassFactory(new TClassFactory);
 
TClassFactory* GetClassFactory()
{
    return ClassFactory.get();
}


--
The limits of my language mean the limits of my world
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: #pragma startup execution order is weird! [Edit] [Edit]
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 1, 2016 3:36 PM   in response to: Ahmed Sayed in response to: Ahmed Sayed
Ahmed wrote:

But what about this line with auto_ptr i know it goes in the cpp file

The first example declares the variable in global scope for the .cpp file
(at the same level where your 'extern'ed "TClassFactory *ClassFactory" variable
originally resides). I simply replaced TClassFactory* with std::auto_ptr<TClassFactory>,
and added 'static' (so it can't be accessed directly by other units).

The second example declares the variable as a static local variable inside
of TClassFactory::GetClassFactory(). A static local variable is preserved
between multiple calls to the function/method, and is initialized only on
the first call.

i didn't use auto_ptr before when this ClassFactory will get destroyed

std::auto_ptr is a smart pointer wrapper. It is a convenient way to let
the compiler automatically free the 'new'ed object for you when the auto_ptr
goes out of scope. This way, you don't need do call 'delete' at all, and
thus can get rid of ClassFactoryUninit().

--
Remy Lebeau (TeamB)
Sean Hoffman

Posts: 126
Registered: 3/28/99
Re: #pragma startup execution order is weird! [Edit]
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 1, 2016 3:44 PM   in response to: Ahmed Sayed in response to: Ahmed Sayed
Just a quick note, take care with auto_ptr. This article (http://www.acodersjourney.com/2016/05/top-10-dumb-mistakes-avoid-c-11-smart-pointers/ ) is worth a quick read.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: #pragma startup execution order is weird! [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 1, 2016 4:19 PM   in response to: Sean Hoffman in response to: Sean Hoffman
Sean wrote:

Just a quick note, take care with auto_ptr. This article (...)
is worth a quick read.

That article mainly describes pitfalls with the new C++11 smart wrappers
(and even then, it primarily focuses on std::shared_ptr). They were introduced
in C++11 to replace std::auto_ptr, which itself predates C++11. One of the
big shortcomings of std::auto_ptr is that it cannot be used in STL containers,
but C++11 smart wrappers can.

BTW, regarding the solution for Mistake #7, one can alternatively use std::default_delete<T[]>
instead of a custom deleter or lambda. And there is a draft proposal for
C++17 to add a specialization of std::shared_ptr for arrays.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02