Jump to content
Incus J

How to set which of two class constructors executes first?

Recommended Posts

I have two classes (TClassA and TClassB) declared in separate units.  Both have a class constructor which executes automatically when the app starts:

 

type TClassB=class(TObject)
  class constructor Create;

...

class constructor TClassB.Create;
begin
  //Some class initialization code
end

Note that this is a class constructor, not a regular instance constructor.

 

At present, when the app starts up TClassB's class constructor runs first, and then TClassA's class constructor follows afterwards.

In this particular case I want the opposite to occur - I want TClassA to run its class constructor code before TClassB runs its class constructor.

 

How can I achieve that?  (I've tried swapping the order of the unit declarations in the Project source .dpr file, but that had no effect) 

Edited by Incus J

Share this post


Link to post

Thanks - although that didn't work, it did get me thinking more closely about what is actually happening:  TClassA is a Controller that uses TClassB (Model), so B probably gets class construction priority.

 

However although TClassB is unaware of TClassA (as it currently stands unitA uses unitB, but unitB does not use unitA) TClassA initialises a data storage path elsewhere that TClassB happens to use.  I sense a restructuring is required.

Share this post


Link to post
41 minutes ago, Incus J said:

Thanks - although that didn't work, it did get me thinking more closely about what is actually happening:  TClassA is a Controller that uses TClassB (Model), so B probably gets class construction priority.

 

However although TClassB is unaware of TClassA (as it currently stands unitA uses unitB, but unitB does not use unitA) TClassA initialises a data storage path elsewhere that TClassB happens to use.  I sense a restructuring is required.

 

You'll want to read through this thread: 

 

 

 

  • Thanks 1

Share this post


Link to post
3 hours ago, Darian Miller said:

You'll want to read through this thread

Oh - yes, thank you.

 

Some time later:  My head hurts.  This line jumped out at me though:

Quote

Enforcing dependencies in initialisation order is not robust. I prefer to find a different way to solve such problems. 

 

Based on what I've just read I think I might abandon trying to get TClassA to initialise before TClassB, and find another approach where it no longer matters which order the classes get initialised in.  Yet somehow I've got to get that data storage path initialised before TClassB initialises - but since the path will be used by other classes too, I can't really do the obvious and get TClassB to initialise the path itself.

Edited by Incus J

Share this post


Link to post
5 minutes ago, Incus J said:

 This line jumped out at me though

Same here.. which is why I followed-up with a message to David today.  

Share this post


Link to post

I guess I could modify things so neither class initialises at startup.  And have some separate custom startup routine that explicitly initialises each class in the order I desire.  But to make that work I'd need to give each class a non-standard class initialisation method (i.e. not called Create) for me to call.  Which seems a bit ugly.  Definitely interested to hear David's thoughts!

Share this post


Link to post

Make the storage data path (TDataStoragePath) a singleton object that initializes itself with the path. All other units that need the path will have to use the unit where TDataStoragePath is defined hence will force TDataStoragePath to initialize first.

  • Thanks 1

Share this post


Link to post
Guest
16 hours ago, Incus J said:

How can I achieve that?  (I've tried swapping the order of the unit declarations in the Project source .dpr file, but that had no effect) 

In many project i have a singleton logger, and always i want it to be the first of everything to be initialized even before the configurator singleton, which responsible for the logger behaviour, my logger will buffer everything in memory specially the configurator messages and even the dpr messages, when the configurator initialized and have the settings for the logger, then the logger will act based on the settings, what it is, if it to drop the buffer and log nothing then will clear its memory and skip the following messages, if it to save to file then it will flush its memory saved logs to disk and will save following messages to disk directly, or if it is keep in memory until something goes wrong and flush to disk then...

 

Anyway logger must be the first, and i do the following as workaround 

type
  TClassX = class       // we need this to be after A nd B
    class var
      Identifier: Integer;
    class constructor Create;
  end;

  TClassA = class
    class var
      Identifier: Integer;
    class constructor Create;
  end;

  TClassB = class
    class var
      Identifier: Integer;
    class constructor Create;
  end;

  TClassC = class
    class var
      Identifier: Integer;
    class constructor Create;
  end;

{ TClassX }
class constructor TClassX.Create;
begin
  //Identifier := TClassA.Identifier + TClassB.Identifier;    // will initialize A then B first
  //Identifier := TClassB.Identifier + TClassA.Identifier;    // will initialize B then A first
  Writeln('ClassX constructor');
end;

{ TClassB }
class constructor TClassB.Create;
begin
  //Identifier := TClassA.Identifier;      // hint the compiler to initialize A first
  Writeln('ClassB constructor');
end;

{ TClassC }

class constructor TClassC.Create;
begin
  //Identifier := TClassB.Identifier;      // hint the compiler to initialize B first
  Writeln('ClassC constructor');
end;

{ TClassA }
class constructor TClassA.Create;
begin
  Writeln('ClassA constructor');
end;

var
  A: TClassA;
  B: TClassB;
  C: TClassC;
  X: TClassX;
begin
  X := TClassX.Create;
  C := TClassC.Create;
  B := TClassB.Create;
  A := TClassA.Create;
  try

  finally
    C.Free;
    B.Free;
    A.Free;
    X.Free;
  end;
  Readln;
end.

the result for the above is

Quote

ClassX constructor
ClassB constructor
ClassC constructor
ClassA constructor

by uncommenting the commented above in B and C 

Quote

ClassX constructor
ClassA constructor
ClassB constructor
ClassC constructor

by uncommenting the second commented line in X constructor, we have

Quote

ClassB constructor
ClassA constructor
ClassX constructor
ClassC constructor

try to play around and see the effect of such approach, just keep in mind there is no warning of cycle usage it will only bring us back to the unpredictable.

 

The class var in this approach (workaround) have zero effect on the code or its performance.

Share this post


Link to post

Thanks Dave and Kas - some really neat approaches there - I will give them a try!

Share this post


Link to post

Maybe you could use lazy init so the class' state will be independent of c-tor call order?

TClassSmth = class
private
  class function GetProp
public
  class c-tor Create
  property Prop read GetProp
end

class c-tor TClassSmth.Create
  GetProp
end

class function TClassSmth.GetProp
  if not Assigned(FProp) then
    Init(FProp)
  Exit(FProp)
end

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×