Recent

Author Topic: [SOLVED] Does Lazarus export qualified?  (Read 616 times)

tfurnivall

  • Jr. Member
  • **
  • Posts: 80
[SOLVED] Does Lazarus export qualified?
« on: November 02, 2025, 12:01:29 am »
I have a TestProgram (TestVersions) and a set of three units (ModelUnit, Unit1, Unit2). All the units are 'identical' except for their name (see attached project). I want to develop the ModelUnit so that at any point I can call <UnitName<>.GetVersion, and get the current version of that Unit.

Significant Code is included here (as well as in the attachment - TestVersions.zzip).

ModelUnit
Code: Pascal  [Select][+][-]
  1. Unit ModelUnit;
  2.  
  3. Interface
  4.  
  5. const VERSIONLINE   = '*.00.000:000';
  6. const VERSIONDATE   = '2025-11-01';
  7.  
  8.  
  9. Function GetVersionLine(): string;
  10. Function GetVersionDate() : String;
  11.  
  12. Implementation
  13.  
  14. Function GetVersionLine () : string;
  15. begin
  16.   GetVersionLine := VERSIONLINE;
  17. end;
  18.  
  19. Function GetVersionDate () : string;
  20. begin
  21.   GetVersionDate := VERSIONDATE;
  22. end;
  23.  
  24. end.
  25.  
Unit1 & Unit 2 are identical except for the name and the last digit in the Version string


TestVersion
Code: Pascal  [Select][+][-]
  1. program TestVersions;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.     {$IFDEF UNIX}
  7.     Cthreads,
  8.     {$ENDIF}
  9.     Classes, Sysutils, Custapp,
  10.     ModelUnit, Unit1, Unit2
  11.     { you can add units after this };
  12.  
  13. type
  14.  
  15.     { TVersions }
  16.  
  17.  {Lazarus boilerplate excluded - it's in the attachment}
  18.     writeln('Unqialified reference (this might fail!)');
  19.     writeln(GetVersionLine());
  20.     writeln(GetVersionDate());
  21.     writeln();
  22.     writeln('Access Unit1');
  23.     writeln(Unit1.GetVersionLine());
  24.     writeln(Unit1.GetVersionDate());
  25.  
  26.     writeln();
  27.     writeln('Access Unit2');
  28.     writeln(Unit2.GetVersionLine());
  29.     writeln(Unit2.GetVersionDate());
  30.  
  31.     writeln();
  32.     write();
  33.     readln();
  34.  
  35.  
  36.  
  37. end.

When I run TestVersions I get the identical value from GetVersion - when I hope to get the value from the Unit which is specified.

The problem, as I understand it is that the first reference to GetVersionLine becomes the only reference available. I think that Ada has something along the lines of
Code: Pascal  [Select][+][-]
  1. export qualified
which allows only the qualified reference (as I have in TestVersions).

I hope my goal is clear - is there an easier way to accomplish it?

* I dowant to have the same identifier in each unit, so that I can simply cycle through the units in any application and pull out the relevant data.
* A certain stubbornness on my part wants to keep the constant definitions as close to the front of a source file as possible, so that its position can be predicted by any tools which use this feature.

I'd appreciate any comments, feedback or suggestions!

Thanks,

Tony
« Last Edit: November 02, 2025, 02:48:38 am by tfurnivall »

440bx

  • Hero Member
  • *****
  • Posts: 5805
Re: Does Lazarus export qualified?
« Reply #1 on: November 02, 2025, 12:47:32 am »
I'm not sure I understood exactly what you're after but, here are a few comments that you may find helpful.

First, the constants VERSIONLINE and VERSIONDATE should _not_ be in the interface section of the unit.  There is no point in having functions that return those values if they can be accessed directly (which putting them in the interface section does.)

Second, if what you want is to get some sort of error if GetVersionLine and/or GetVersionDate are called without being qualified then, the only way I can think of accomplishing that is to create another unit that also exports GetVersionLine and GetVersionDate AND ensure that unit is the LAST unit used.  The implementation in that unit is the one that will be called when the calls are unqualified and, that unit can choose to raise an exception (or display an error message or whatever else you feel is appropriate.)

In a nutshell, TTBOMK, there is no way to tell the compiler to emit an error for an unqualified call to a function as long as that function is somehow accessible (in a unit for instance.)

HTH.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

tfurnivall

  • Jr. Member
  • **
  • Posts: 80
Re: Does Lazarus export qualified?
« Reply #2 on: November 02, 2025, 02:48:13 am »
Thanks, 440bx,

I couldn't find any suggestion that Lazarus supported a "qualified" adjective on functions (or even a "private" adjective in an interface section, which is what I'm also trying to get at), but I thought I'd ask anyway!

The first issue means that if I want to get VERSIONLINE (and related values) from a Unit, I have to:

1) Make the definition of those values take place in the Implementation section, and

2) Find some mechanism (such as the one you describe) to trap unqualified references.

I'm still unclear if a qualified reference will succeed (the TestVersions program actually does make qualified calls to the functions, but the returned result is always the first Unit (ModelUnit). It's as much about the qualified references to work as it is about preventing unqualified references.

In my ideal world, ModelUnit (and all its derivatives) would have source which looked like this:

Code: Pascal  [Select][+][-]
  1. Unit ModelUnit;
  2.  
  3. Interface
  4.  
  5. private const VERSIONLINE   = '*.00.000:000';
  6. private const VERSIONDATE   = '2025-11-01';
  7.  
  8.  
  9. qualified Function GetVersionLine(): string;
  10. qualified Function GetVersionDate() : String;
  11.  
  12. Implementation
  13.  

where "private" would effectively shift the following definition s to the implementation section while keeping them up at the front of the source file, and "qualified" would notify the linkage and run-time systems that references were legal only via the qualification with the name of the executable unit. I guess until I rewrite the entire Universe, I'll have to wait!

Thanks, again

Tony

n7800

  • Hero Member
  • *****
  • Posts: 542
Re: [SOLVED] Does Lazarus export qualified?
« Reply #3 on: November 02, 2025, 03:50:46 am »
Your archive contains multiple projects, not just one with multiple units. And it won't compile because it can't find the units.

How did you create them? A unit is a separate file with a PAS extension. An LPR is the program code (the main file), and an LPI is the project as a whole (for IDE).

If I delete the extra LPI files (except "TestVersions.lpi") and rename all LPR files to PAS (except "TestVersions.lpr"), the program runs.

n7800

  • Hero Member
  • *****
  • Posts: 542
Re: [SOLVED] Does Lazarus export qualified?
« Reply #4 on: November 02, 2025, 04:08:02 am »
An unqualified reference first looks for an identifier (function, variable, etc.) in the current file, otherwise, in the included units. If there are multiple, it uses the one from the last one. That's why it shows the value from "Unit2".

For example, I can include the "math" unit and use the min/max functions without specifying the unit name before each one.

The interface and implementation sections are responsible for what will be visible to other units. In your case, you can to declare it as suggested above:

Code: Pascal  [Select][+][-]
  1. Unit Unit2;
  2.  
  3. Interface
  4.  
  5. Function GetVersionLine(): string;
  6. Function GetVersionDate() : String;
  7.  
  8. Implementation
  9.  
  10. const VERSIONLINE   = '*.00.000:002';
  11. const VERSIONDATE   = '2025-11-01';
  12.  
  13. Function GetVersionLine () : string;
  14. begin
  15.   GetVersionLine := VERSIONLINE;
  16. end;
  17.  
  18. Function GetVersionDate () : string;
  19. begin
  20.   GetVersionDate := VERSIONDATE;
  21. end;
  22.  
  23. end.

This will prevent from accidentally changing the value of version variables from other units.

n7800

  • Hero Member
  • *****
  • Posts: 542
Re: [SOLVED] Does Lazarus export qualified?
« Reply #5 on: November 02, 2025, 04:23:52 am »
Regarding the question of whether it's possible to require a qualified name for a function, I'm not sure the language has such a modifier.

On the other hand, why would you need to do that if you can just specify the unit name? Is it to prevent accidental errors?

Then as another trick, in addition to the one suggested above, you just can declare a procedure with the same name (not a function) in the program:

Code: Pascal  [Select][+][-]
  1. procedure GetVersionLine;
  2. begin
  3. end;

Then the code below won't compile, since a procedure can't return a value:

Code: Pascal  [Select][+][-]
  1. writeln(GetVersionLine());

Thaddy

  • Hero Member
  • *****
  • Posts: 18305
  • Here stood a man who saw the Elbe and jumped it.
Re: [SOLVED] Does Lazarus export qualified?
« Reply #6 on: November 02, 2025, 05:08:07 pm »
Two options:
Code: Pascal  [Select][+][-]
  1. Interface
  2. {$push}{$J-}
  3. const VERSIONLINE:PChar   = '*.00.000:000';
  4. const VERSIONDATE:PChar   = '2025-11-01';
  5. {$pop}
is enough.
You can also do this:
Code: Pascal  [Select][+][-]
  1. Unit ModelUnit;
  2. Interface
  3. {$push}{$J-}
  4. const VERSIONLINE:PChar   = '*.00.000:000';export name 'VERSIONLINE';
  5. const VERSIONDATE:PChar   = '2025-11-01';export name 'VERSIONDATE';
  6. {$pop}
  7. Implementation
  8. end.
And use it like this:
Code: Pascal  [Select][+][-]
  1. program testversion;
  2. {$mode objfpc}
  3. uses ModelUnit;
  4. var
  5.  VERSIONLINE:PChar; external name 'VERSIONLINE';
  6.  VERSIONDATE:PChar; external name 'VERSIONDATE';
  7. begin
  8.   writeln(VERSIONLINE,'-',VERSIONDATE);
  9. end.
Point is, you should have used a typed constant, which can be in both {$J+} or {$J-}. Latter is recommended.
The use of PChar is intentional here. (no refcounts)
In {$J-} state the strings end up in readonly section of executable (.rodata) if supported by the platform (most).
Note that in the second example the external name is case sensitive, but the variable name itself does not matter.

« Last Edit: November 02, 2025, 05:17:09 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

440bx

  • Hero Member
  • *****
  • Posts: 5805
Re: [SOLVED] Does Lazarus export qualified?
« Reply #7 on: November 02, 2025, 06:13:26 pm »
@Tony,

You're welcome.

One possibility that crossed my mind that does what you want about qualifying the call but does not use units goes like this: if instead of using units you use advanced records, then the calls must be qualified because the functions that return the versionxxxxx constants are part of the record.  Those functions only exist within the scope of the advanced record, for that reason, they must be qualified in order to access them.

the one thing that is a bit different than what you originally have is that, you'll need a variable of each advanced record type for an instance of the records to exist.  Following the example code you presented, you'd have a variable named ModelUnit, Unit1, Unit2 (I'd suggest using better names if you go that route.)

Also, the constant declaration can be private which is what they should be while being declared before the function themselves (which seems to be something you want.)

HTH.


FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

PascalDragon

  • Hero Member
  • *****
  • Posts: 6184
  • Compiler Developer
Re: [SOLVED] Does Lazarus export qualified?
« Reply #8 on: November 03, 2025, 09:45:48 pm »
When I run TestVersions I get the identical value from GetVersion - when I hope to get the value from the Unit which is specified.

The problem, as I understand it is that the first reference to GetVersionLine becomes the only reference available. I think that Ada has something along the lines of
Code: Pascal  [Select][+][-]
  1. export qualified
which allows only the qualified reference (as I have in TestVersions).

I can not reproduce. The lines that contain Unit1 will call the functions from Unit1 and those that contain Unit2 will call those from Unit2. Those without a unit prefix will call the functions from the last unit added to the scope, all by design. If it doesn't for you, then please show a full example where you compile and execute this, because you have some mistake then.

 

TinyPortal © 2005-2018