Lazarus
Free Pascal => General => Topic started 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?
-
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."
-
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.
-
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.
-
Remove:
uses
Unit2;
from implementation section of Unit1, you don't use anything from Unit2 in Unit1 but this will change unit initialization order.
-
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.
-
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.:
- suppose we have program A, unit B and unit C
- A uses B,C
- B uses C
- C uses B
In above case, the parser movement is as follows:
- Parse A
- Encounter B in A's uses clause, parse B
- Encounter C in B's uses clause, parse C
- Encounter B in C's uses clause, do nothing (B is being parsed)
- End of C parsing, put C to unit initialization order
- End of B parsing, put B to unit initialization order
- Encounter C in A's uses clause, do nothing (C is already parsed)
- End of A parsing, parsing done
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.
-
Thanks for the explanation! I take it there is no way to directly change the initialization order.
-
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.
-
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.