Lazarus

Free Pascal => General => Topic started by: mas steindorff on January 14, 2015, 05:01:52 am

Title: Using Generics Q2
Post by: mas steindorff on January 14, 2015, 05:01:52 am
I have a unit defining some generics. in the  initialization section of the unit, I have
  SensorLocMap['N/A']:= -1;
  SensorLocMap['NC' ]:= -1;
  SensorLocMap['NC' ]:= -1;
  SensorLocMap['O1' ]:= 21;
 ... for about 100 strings

the SensorLocMap is defined as a  TSensorLocMap = specialize TFPGMap<String,integer>;  in the interface section of the unit.

in another (formless) unit, I have an object that uses these values in it's initialization section.  when I run, I am getting an EListError when the object tries to i := SensorLocMap[str] where str := 'O1'.

This line works if I call it later in the program so I believe the first unit is not being initialized before the  object is created.  How can I force the unit with generics to be done before the object uses it?
The object is a "system" object who has no master so I need to keep it's creation in the unit's initialization section. I've tried moving it position in the projects .lpr file without success.

Is there a "wait for generics to be mapped" command?

Title: Re: Using Generics Q2
Post by: engkin on January 14, 2015, 05:10:34 am
I believe these units are seen based on their order in your Project Source file.

Edit:
Sorry, you already mentioned changing their position in the "projects .lpr file without success."
Title: Re: Using Generics Q2
Post by: Leledumbo on January 14, 2015, 09:03:38 am
Not reproducible in the attached example. Reversing the uses clause in the .lpr has no effect, since in order u2 uses u1 in the implementation. That defines the actual unit order.
Title: Re: Using Generics Q2
Post by: engkin on January 14, 2015, 06:08:01 pm
Not reproducible in the attached example. Reversing the uses clause in the .lpr has no effect, since in order u2 uses u1 in the implementation. That defines the actual unit order.
Try the attached project.
Title: Re: Using Generics Q2
Post by: Leledumbo on January 14, 2015, 06:30:19 pm
Remove:
Code: [Select]
uses
  Unit2;
from implementation section of Unit1, you don't use anything from Unit2 in Unit1 but this will change unit initialization order.
Title: Re: Using Generics Q2
Post by: engkin on January 14, 2015, 08:33:29 pm
This is a stripped down version to produce the bug. Assume that I do use something from Unit2.

Edit:
Notice that it works as expected if you change the order in the lpr file.
Usually we do not rely on the order of initialization, and we can do the same here by using a function, for instance, instead of SensorLocMap.
Title: Re: Using Generics Q2
Post by: Leledumbo on January 15, 2015, 06:21:46 am
This is a stripped down version to produce the bug. Assume that I do use something from Unit2.

Edit:
Notice that it works as expected if you change the order in the lpr file.
Usually we do not rely on the order of initialization, and we can do the same here by using a function, for instance, instead of SensorLocMap.
The rule is that unit initialization order is defined using left-to-right depth first search manner of the uses clause, following the parser movement. e.g.:
In above case, the parser movement is as follows:
As you can see, the final order is C then B. Now modify above flow with A uses C,B instead of B,C and try to follow the parser movement. See the order of "put X to unit initialization order", that's what you finally get. The documentation actually mentions this, albeit not as comprehensive as this example (probably too verbose).

Attached is a very simple example that demonstrates this behavior. Each unit's initialization section will call WriteLn, the output is the final initialization order.
Title: Re: Using Generics Q2
Post by: engkin on January 15, 2015, 02:27:23 pm
Thanks for the explanation! I take it there is no way to directly change the initialization order.
Title: Re: Using Generics Q2
Post by: jmm72 on January 15, 2015, 10:31:17 pm
I have a project with some weird unit relationships due to bad initial design (it's a pet project which has grew larger than expected so I don't really care). To avoid moving stuff in the uses clause or between units, I just made a little "InitEverything" procedure which takes care of creating the global instances and if needed set them up properly. It's not a very elegant solution, but it's a quick fix that doesn't break anything.

However if your code is more serious and/or you want clean and good code, if B uses C and C uses B and you want to be sure that what you want is initialized without needing to care about strict unit order in the uses clause, just split that part to another unit D, and make B and C use it. D will be initialized before B and C. Splitting stuff to another unit has saved me several headaches.
Title: Re: Using Generics Q2
Post by: mas steindorff on February 01, 2015, 09:59:31 am
just a follow up on my issue.... try several things without success including a full clean and build.  I gave up for the night and when I reopened the project a few days later, could not reproduce the error.  my guess is something left in RAM.   I forgot my first rule in fixing broken software (cycle the power).  thanks to all for your help.