Recent

Author Topic: Using Generics Q2  (Read 5073 times)

mas steindorff

  • Sr. Member
  • ****
  • Posts: 428
Using Generics Q2
« 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?

windows 7/10 - laz 2.0 / 1.2.6 general releases

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: Using Generics Q2
« Reply #1 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."
« Last Edit: January 14, 2015, 05:29:48 am by engkin »

Leledumbo

  • Hero Member
  • *****
  • Posts: 8114
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Using Generics Q2
« Reply #2 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.

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: Using Generics Q2
« Reply #3 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.

Leledumbo

  • Hero Member
  • *****
  • Posts: 8114
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Using Generics Q2
« Reply #4 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.

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: Using Generics Q2
« Reply #5 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.
« Last Edit: January 14, 2015, 08:52:17 pm by engkin »

Leledumbo

  • Hero Member
  • *****
  • Posts: 8114
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Using Generics Q2
« Reply #6 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.:
  • 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.

engkin

  • Hero Member
  • *****
  • Posts: 2513
Re: Using Generics Q2
« Reply #7 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.

jmm72

  • Jr. Member
  • **
  • Posts: 79
  • Very experienced in being a beginner...
Re: Using Generics Q2
« Reply #8 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.
Lazarus 1.6.4 + FPC 3.0.2 64bits under Windows 7 64bits
Only as a hobby nowadays
Current proyect release: TBA

mas steindorff

  • Sr. Member
  • ****
  • Posts: 428
Re: Using Generics Q2
« Reply #9 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.
windows 7/10 - laz 2.0 / 1.2.6 general releases