Recent

Author Topic: Reference to unit ?  (Read 726 times)

hedgehog

  • Full Member
  • ***
  • Posts: 116
Reference to unit ?
« on: April 25, 2026, 10:52:01 am »
Please don't ask me why I'm doing this :)

I have two units, Unit1 and Unit2. Each defines the constants strconst1, strconst2, and strconst3.

Code: Pascal  [Select][+][-]
  1. uses
  2.   Unit1, Unit2;
  3.  
  4. procedure MyProc(UseUnit1: boolean);
  5. begin
  6.   if UseUnit1 then
  7.   begin
  8.     Label1.Caption:= Unit1.strconst1;
  9.     Label2.Caption:= Unit1.strconst2;
  10.     Label3.Caption:= Unit1.strconst3;
  11.   end
  12.   else
  13.   begin
  14.     Label1.Caption:= Unit2.strconst1;
  15.     Label2.Caption:= Unit2.strconst2;
  16.     Label3.Caption:= Unit2.strconst3;
  17.   end;
  18. end;

This works well, but is there a way to eliminate this code duplication?

This is a stupid question, but is it possible to do something similar?
Code: Pascal  [Select][+][-]
  1. // PSEUDOCODE ON
  2.   if UseUnit1 then MyUnit:= Unit1 else MyUnit=:= Unit2;
  3.   Label1.Caption:= MyUnit.strconst1;
  4.   Label2.Caption:= MyUnit.strconst2;
  5.   Label3.Caption:= MyUnit.strconst3;
  6. // PSEUDOCODE OFF
  7.  


creaothceann

  • Sr. Member
  • ****
  • Posts: 361
Re: Reference to unit ?
« Reply #1 on: April 25, 2026, 12:12:39 pm »
Code: Pascal  [Select][+][-]
  1. const
  2.         u1_a = 'i';  // in unit 1
  3.         u1_b = 'j';  // in unit 1
  4.         u1_c = 'k';  // in unit 1
  5.  
  6.         u2_a = 'l';  // in unit 2
  7.         u2_b = 'm';  // in unit 2
  8.         u2_c = 'n';  // in unit 2
  9.  
  10.  
  11. const
  12.         StringConstants : array[boolean, 1..3] of string =
  13.                 (
  14.                         (u1_a, u1_b, u1_c),
  15.                         (u2_a, u2_b, u2_c)
  16.                 );
  17.  
  18. procedure MyProc(UseUnit1 : boolean);
  19. begin
  20.         Label1.Caption := StringConstants[UseUnit1, 1];
  21.         Label2.Caption := StringConstants[UseUnit1, 2];
  22.         Label3.Caption := StringConstants[UseUnit1, 3];
  23. end;

cdbc

  • Hero Member
  • *****
  • Posts: 2787
    • http://www.cdbc.dk
Re: Reference to unit ?
« Reply #2 on: April 25, 2026, 12:30:43 pm »
Hi
Easy-Peasy => Alias the bastards  :D
Like this:
Code: Pascal  [Select][+][-]
  1. unit some_unit_that_uses_istrlist;
  2. ...
  3. const
  4.   { alias the interface/service GUIDs from istrlist.pas }
  5.   SGUIDIStrings = istrlist.SGUIDIStrings;
  6.   SGUIDIStringList = istrlist.SGUIDIStringList;  
You can alias to the same name or to another...
Note: The same trick is also used for types  ;D
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

hedgehog

  • Full Member
  • ***
  • Posts: 116
Re: Reference to unit ?
« Reply #3 on: April 25, 2026, 01:58:15 pm »
creaothceann,
I know this method, but it is certainly not very elegant.

cdbc,
But how can this be used in runtime?

cdbc

  • Hero Member
  • *****
  • Posts: 2787
    • http://www.cdbc.dk
Re: Reference to unit ?
« Reply #4 on: April 25, 2026, 02:35:43 pm »
Hi
Hmmm... Maybe
Code: Pascal  [Select][+][-]
  1. {$if defined SOMEDEFINEINUNIT1}...{$else}...{$ifend}
Your job to figure it out, I've got a football game to get back to....
 :P
Regards Benny

edit: Another approach is to let both unit1 and unit2 register their constants with unit3 -- under specific circumstances... Would prolly require 'Static Vars' /or/ as they're also called: 'writable consts'  8-)
« Last Edit: April 25, 2026, 02:39:42 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

LemonParty

  • Sr. Member
  • ****
  • Posts: 470
Re: Reference to unit ?
« Reply #5 on: April 25, 2026, 03:19:58 pm »
Code: Pascal  [Select][+][-]
  1.   if UseUnit1 then MyUnit:= Unit1 else MyUnit=:= Unit2;
  2.   Label1.Caption:= MyUnit.strconst1;
  3.   Label2.Caption:= MyUnit.strconst2;
  4.   Label3.Caption:= MyUnit.strconst3;
I must say this is a bad idea. Compiler should know what you are trying to take at compile time (so it generates a perfect code), if not then something will be calculated at runtime (also we will need to store an additional information for runtime) and in this case we will receive a drop in performance.
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

cdbc

  • Hero Member
  • *****
  • Posts: 2787
    • http://www.cdbc.dk
Re: Reference to unit ?
« Reply #6 on: April 25, 2026, 03:48:01 pm »
Hi again
Pause in football...
If you're not afraid of pointer and records, you could implement something that smells like this:
Code: Pascal  [Select][+][-]
  1. // REALCODE ON
  2. type
  3.   PMyRec = ^TMyRec;
  4.   TMyRec = record
  5.    const
  6.      StrConst1: string = 'one';
  7.      StrConst2: string = 'two';
  8.      StrConst3: string = 'three';
  9.   end;
  10. ...
  11. var MyRec: PMyRec;
  12.   if UseUnit1 then MyRec:= @Unit1.Rec1; else MyRec:= @Unit2.Rec2;
  13.   Label1.Caption:= MyRec^.strconst1;
  14.   Label2.Caption:= MyRec^.strconst2;
  15.   Label3.Caption:= MyRec^.strconst3;
  16.  
Regards Benny

edit: removed //PSEUDO, 'cause it's damn real code  :P
« Last Edit: April 25, 2026, 03:51:01 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

jamie

  • Hero Member
  • *****
  • Posts: 7707
Re: Reference to unit ?
« Reply #7 on: April 25, 2026, 04:12:19 pm »
Create a Base class that has the access functions, not constants for each Constant you want to access but mark them as Virtual ABTRACT;

Then have 2 descendent Classes that override these functions but return the item you need.

At the start of your app or at point you can have a single variable in your code, call it MyConstants, for example and create it using of or the other descendent classes.

Code: Pascal  [Select][+][-]
  1.  
  2. Var
  3.   MyConsts:TConstantBaseClass;
  4. //Somewhere in code
  5.  
  6. if Language1 Then MyConsts := TLanguage1Descendent.Create else
  7.   MyConsts := TLauguage2Descendent.Create;
  8.  
  9. //Somewhere else in code
  10.  
  11. Label1.Caption := MyConsts.GetName;
  12.  
  13.  

and the Classes may look something like this.

Code: Pascal  [Select][+][-]
  1.  TConstBaseClass = Class
  2.    Function GetName:String; Virtual; Abstract;
  3.    ......
  4.   End;
  5. TLanguage1Constants := Class(TconstBaseClass)
  6.    Function GetName:String; Override;
  7.    .....
  8.  End;
  9. TLanguage2Constants ... Same as above
  10. //......
  11.  
  12. In the Imp Sections
  13. Function Tlanguage1Constants.GetName:string;
  14. Begin
  15.   Result := 'Some Persons Name';
  16. End;
  17.    
  18.  


This way, you only need it in one unit or may just the unit you are working with at the time.

Jamie
The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: Reference to unit ?
« Reply #8 on: April 25, 2026, 05:59:04 pm »
The solution may be simple:
Code: Pascal  [Select][+][-]
  1. {$ifdef fpc}{$mode objfpc}{$endif}
  2. uses sysutils;
  3. begin
  4.   {$if declared(sysutils)}
  5.   writeln('You are using sysutils');
  6.   {$endif}
  7. end.
Simply make sure only one of the units is declared in the uses clause.
« Last Edit: April 25, 2026, 06:02:44 pm by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

hedgehog

  • Full Member
  • ***
  • Posts: 116
Re: Reference to unit ?
« Reply #9 on: April 25, 2026, 06:12:17 pm »
Thank you all!

CDBC, your solution is practically perfect!
I say this because I wrote exactly the same code two hours ago (100% match).

Khrys

  • Sr. Member
  • ****
  • Posts: 439
Re: Reference to unit ?
« Reply #10 on: April 27, 2026, 07:28:38 am »
How about identifier shadowing?

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. const
  6.   strconst1 = 'one';
  7.   strconst2 = 'two';
  8.   strconst3 = 'three';
  9.  
  10. implementation
  11.  
  12. end.

Code: Pascal  [Select][+][-]
  1. unit Unit2;
  2.  
  3. interface
  4.  
  5. const
  6.   strconst1 = 'uno';
  7.   strconst2 = 'due';
  8.   strconst3 = 'tre';
  9.  
  10. implementation
  11.  
  12. end.

Code: Pascal  [Select][+][-]
  1. uses
  2.   Unit1, Unit2; // Rightmost unit takes precedence
  3.  
  4. procedure MyProc(UseUnit1: Boolean);
  5. begin
  6.     Label1.Caption:= strconst1;
  7.     Label2.Caption:= strconst2;
  8.     Label3.Caption:= strconst3;
  9. end;

Thaddy

  • Hero Member
  • *****
  • Posts: 19165
  • Glad to be alive.
Re: Reference to unit ?
« Reply #11 on: April 27, 2026, 04:00:11 pm »
I have been playing a bit with this and basically I considered that every unit needs to be initialized before a proper choice can be presented.
Now, IF the compiler supported something alike  AddExitProc(for ExitProc entry) for InitProc, i.e. AddInitProc (which comes after all unit initializations) You could switch at runtime by adding an initproc even before the main program executes.
A version looks like this, must be the first unit - awaiting a system.pas patch I have made:
Code: Pascal  [Select][+][-]
  1. unit AddInitProcUnit;
  2. {$mode delphi}
  3. interface
  4.  
  5. procedure AddInitProc(Proc: TProcedure);
  6.  
  7. implementation
  8.  
  9. type
  10.   PInitProcInfo = ^TInitProcInfo;
  11.   TInitProcInfo = record
  12.     Proc: TProcedure;
  13.     Next: PInitProcInfo;
  14.   end;
  15.  
  16. {$push}{$J+}
  17. const
  18.   InitProcList: PInitProcInfo = nil;
  19. {$pop}
  20.  
  21. procedure DoInitProcs; forward;
  22.  
  23. procedure AddInitProc(Proc: TProcedure);
  24. var
  25.   Info: PInitProcInfo;
  26.   OldProc: TProcedure;
  27. begin
  28.   { If there is an existing InitProc that is not our own dispatcher,
  29.     add it to the list first so it will be called as part of the chain.}
  30.   if (InitProc <> nil) and (InitProc <> @DoInitProcs) then
  31.   begin
  32.     { capture the existing routine }
  33.     OldProc := InitProc;          
  34.     New(Info);
  35.     Info^.Proc := OldProc;
  36.     { existing proc becomes new head (LIFO) }
  37.     Info^.Next := InitProcList;  
  38.     InitProcList := Info;
  39.   end;
  40.  
  41.   { Now add the new procedure (the one passed as parameter) }
  42.   New(Info);
  43.   Info^.Proc := Proc;
  44.   Info^.Next := InitProcList;
  45.   InitProcList := Info;
  46.  
  47.   { Redirect global InitProc to our dispatcher (if not already) }
  48.   if InitProc <> @DoInitProcs then
  49.     InitProc := @DoInitProcs;
  50. end;
  51.  
  52. procedure DoInitProcs;
  53. var
  54.   Info, NextInfo: PInitProcInfo;
  55. begin
  56.   Info := InitProcList;
  57.   { prevent re‑entry }
  58.   InitProcList := nil;          
  59.   while Info <> nil do
  60.   begin
  61.     NextInfo := Info^.Next;
  62.     { execute the "stored" procedure }
  63.     Info^.Proc;  
  64.     { executed once, dispose }              
  65.     Dispose(Info);
  66.     Info := NextInfo;
  67.   end;
  68.   { After all procs have run, clear the dispatcher }
  69.   InitProc := nil;
  70. end;
  71.  
  72. {$ifopt D+}
  73. procedure teststart;
  74. begin
  75.   writeln('executed after all other units are initialized');
  76. end;
  77.  
  78. initialization
  79.   AddInitProc(@teststart);
  80. {$endif D+}
  81.  
  82. finalization
  83.   // Clean up any remaining init procs if DoInitProcs never ran
  84.   if InitProcList <> nil then
  85.     DoInitProcs;
  86. end.
This code works, but without the compiler patch, it needs to be called from a unit, not the main program (which works if system.pas is patched).

I hope you see the relevance, otherwise will add full example.
« Last Edit: April 28, 2026, 09:47:01 am by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

 

TinyPortal © 2005-2018