Recent

Author Topic: how to do indirection  (Read 6504 times)

Kays

  • Hero Member
  • *****
  • Posts: 575
  • Whasup!?
    • KaiBurghardt.de
how to do indirection
« on: February 02, 2019, 12:45:45 am »
Hi,

how do I implement an indirect function call? How can I assign the address of the routine, that implements my foo, from my initialization block. The following complains generalImplementation is unknown. I mean, I get it: Only routines at the same or higher level (scope) are known.
Code: Pascal  [Select][+][-]
  1. unit indirection;
  2. {$mode ISO}
  3. {$modeswitch allowinline+}
  4. {$modeswitch initfinal+}
  5.  
  6. // PUBLIC =================================================== //
  7. interface
  8.  
  9. function foo(const x: int64): int64;
  10.  
  11. // PRIVATE ================================================== //
  12. implementation
  13.  
  14. var
  15.         /// directs to the right foo implementation
  16.         fooImplementation: procedure is nested;
  17.  
  18. function foo(const x: int64): int64; inline;
  19.         procedure generalImplementation;
  20.         begin
  21.                 foo := 42;
  22.         end;
  23.         procedure alternativeImplementation;
  24.         begin
  25.                 foo := -1;
  26.         end;
  27. begin
  28.         fooImplementation;
  29. end;
  30.  
  31. initialization
  32.         //if soAndSo then fooImplementation := alternativeImplementation else
  33.         fooImplementation := generalImplementation;
  34. end.
I want to somehow ensure, fooImplementation only refers to routines that are actually implementing foo (i.e. a nested procedure within foo).
Yours Sincerely
Kai Burghardt

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: how to do indirection
« Reply #1 on: February 02, 2019, 01:27:10 am »
IMO it is not possible and doesn't make a sense.

Why generalImplementation and alternativeImplementation are local for Foo?
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

Kays

  • Hero Member
  • *****
  • Posts: 575
  • Whasup!?
    • KaiBurghardt.de
Re: how to do indirection
« Reply #2 on: February 02, 2019, 01:45:05 am »
IMO it is not possible and doesn't make a sense.
Ahehee, that's why the compiler doesn't like it, sure. It's just a piece of code showing what I mean to achieve.
Why [are] generalImplementation and alternativeImplementation […] local [to] Foo?
Err, access to the function's parameters and the result variable? In general, meaningful arrangement of routines?
Yours Sincerely
Kai Burghardt

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: how to do indirection
« Reply #3 on: February 02, 2019, 02:10:00 am »
Have two procedures of the same prototype in global space but in the implementation section.

and then you can assign one of them to your variable procedure
The only true wisdom is knowing you know nothing

furious programming

  • Hero Member
  • *****
  • Posts: 858
Re: how to do indirection
« Reply #4 on: February 02, 2019, 02:10:24 am »
Code: Pascal  [Select][+][-]
  1. unit Indirection;
  2.  
  3. {$mode ISO}
  4. {$modeswitch allowinline+}
  5. {$modeswitch initfinal+}
  6.  
  7. interface
  8.  
  9.   function Foo(const X: Int64): Int64;
  10.  
  11. implementation
  12.  
  13. var
  14.   FooImplementation: function(const X: Int64): Int64;
  15.  
  16.   function GeneralImplementation(const X: Int64): Int64;
  17.   begin
  18.     GeneralImplementation := X + 100;
  19.   end;
  20.  
  21.   function AlternativeImplementation(const X: Int64): Int64;
  22.   begin
  23.     AlternativeImplementation := X - 50;
  24.   end;
  25.  
  26.   function Foo(const X: Int64): Int64; inline;
  27.   begin
  28.     Foo := FooImplementation(X);
  29.   end;
  30.  
  31. initialization
  32. begin
  33.   FooImplementation := GeneralImplementation;
  34. end;
  35.  
  36. end.
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: how to do indirection
« Reply #5 on: February 02, 2019, 03:40:13 pm »
The other way of doing it and maybe a little on the dangerous side is the make a initiation function that
creates the assignment within..

Procedure SetupMyCall( WitchCall:integer);

   Function FirstOne(….):Int64;
    Begin
     ….
    end;
   Function Second(…):Int64;
     Begin
      …
    end;
begin
   Case WhichCall of
     1:Foo := FisrtOne;
     2:Foo := SecondOne;
   End;
end;

Etc.
 Not sure if that is safe, I would say if any of those nested functions call any local parent variables it will
crash!


The only true wisdom is knowing you know nothing

sash

  • Sr. Member
  • ****
  • Posts: 366
Re: how to do indirection
« Reply #6 on: February 02, 2019, 04:30:02 pm »
You cannot have a pointer to a nested procedure, because it is nested: the point of such procedure is to share variables of parent procedure, and thus to be isolated from the rest of code.

Just keep it simple, until you have some serious reasons, which I didn't see.
Instead, everything I saw in your code could be solved by simple if/case blocks.
Lazarus 2.0.10 FPC 3.2.0 x86_64-linux-gtk2 @ Ubuntu 20.04 XFCE

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: how to do indirection
« Reply #7 on: February 02, 2019, 05:47:42 pm »
You cannot have a pointer to a nested procedure

{$modeswitch nestedprocvars}

Code: Pascal  [Select][+][-]
  1. {$modeswitch nestedprocvars}
  2. ...
  3. type  
  4.   tnestedprocvar = procedure is nested;
« Last Edit: February 02, 2019, 05:54:03 pm by engkin »

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: how to do indirection
« Reply #8 on: February 02, 2019, 05:56:44 pm »
{$modeswitch nestedprocvars}
Sample:
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2. {$MODE OBJFPC}
  3. {$MODESWITCH NESTEDPROCVARS}
  4.  
  5. type
  6.   TNestedProc = procedure is nested;
  7.  
  8. procedure CallNestedProc(N: Integer; Proc: TNestedProc);
  9. begin
  10.   Proc;
  11. end;
  12.  
  13. function GetProc(N: Integer): TNestedProc;
  14.  
  15.   procedure NestedOne;
  16.   begin
  17.     Writeln('Nested One, N=', N);
  18.   end;
  19.  
  20.   procedure NestedTwo;
  21.   begin
  22.     Writeln('Nested Two, N=', N);
  23.   end;
  24.  
  25. begin
  26.   Result := @NestedOne;
  27. end;
  28.  
  29. var
  30.   P: TNestedProc;
  31. begin
  32.   P := GetProc(55);
  33.   CallNestedProc(44, P);
  34.   Readln;
  35. end.

Kays

  • Hero Member
  • *****
  • Posts: 575
  • Whasup!?
    • KaiBurghardt.de
Re: how to do indirection
« Reply #9 on: February 02, 2019, 08:50:34 pm »
Have two procedures of the same prototype in global space but in the implementation section.

and then you can assign one of them to your variable procedure
That's, I guess, the path to take then.

[…] Just keep it simple, until you have some serious reasons, which I didn't see.
Instead, everything I saw in your code could be solved by simple if/case blocks.
Yeah, no: The kernel version won't change during run-time. The processor's capabilities will stay the same. You evaluate it once, which implementation works, and you're done.

So there's no way to enforce only functions that actually implement foo are assignment compatible? Going by the formal signature I could just assign any function accepting and returning one int64. That's quite broad.
Yours Sincerely
Kai Burghardt

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: how to do indirection
« Reply #10 on: February 02, 2019, 10:24:48 pm »
How about using compiler switches to decide on which one you want based on a target?
{$IFDEF PROC6502}

 Foo(….):int64;
 
{$ELSE}
  FOO(…)Int64...

{$ENDIF}

So somewhere in some main common module you define a compiler constant..
{$DEFINE PROC6502}

 At least this way dead code won't be compiled into your stuff.
The only true wisdom is knowing you know nothing

sash

  • Sr. Member
  • ****
  • Posts: 366
Re: how to do indirection
« Reply #11 on: February 02, 2019, 11:04:35 pm »
Code: Pascal  [Select][+][-]
  1. {$modeswitch nestedprocvars}
  2. ...
  3. type  
  4.   tnestedprocvar = procedure is nested;

Technically yes, but I meant because you still need to call it in context of parent procedure, or from outer scope with wrapper, personally I see this as very doubtful feature with unnecessary overhead.
So, again, you cannot use it as regular proc pointer correctly.

@ASerge, another sample (compiles and runs).
Code: Pascal  [Select][+][-]
  1. program console;
  2.  
  3. {$APPTYPE CONSOLE}
  4. {$MODE OBJFPC}
  5. {$MODESWITCH NESTEDPROCVARS}
  6.  
  7. type
  8.   TNestedProc = procedure is nested;
  9.  
  10. function GetProc(N: Integer): TNestedProc;
  11.   procedure NestedOne;
  12.   begin
  13.     Writeln('Nested One, N=', N);
  14.   end;
  15.  
  16.   procedure NestedTwo;
  17.   begin
  18.     Writeln('Nested Two, N=', N);
  19.   end;
  20.  
  21. begin
  22.   Result := @NestedOne;
  23. end;
  24.  
  25. var
  26.   P: TNestedProc;
  27.  
  28. begin
  29.   P := GetProc(55);
  30.   P; // what is a value of N? A random unassigned garbage.
  31.   Readln;
  32. end.
Lazarus 2.0.10 FPC 3.2.0 x86_64-linux-gtk2 @ Ubuntu 20.04 XFCE

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: how to do indirection
« Reply #12 on: February 03, 2019, 01:15:44 pm »
Code: Pascal  [Select][+][-]
  1.   P; // what is a value of N? A random unassigned garbage.
Certainly. Moreover, if the call is made outside the procedure, i.e. the value of the stack frame pointer is not defined, SIGSEGV is also possible.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5481
  • Compiler Developer
Re: how to do indirection
« Reply #13 on: February 04, 2019, 09:38:20 am »
Code: Pascal  [Select][+][-]
  1. unit indirection;
  2. {$mode ISO}
  3. {$modeswitch allowinline+}
  4. {$modeswitch initfinal+}
  5.  
  6. {...}
  7.  
Not part of your question, but nevertheless: it's not recommended to use the ISO mode for units as the concept of units does not exist in the ISO Pascal Standard. Thus unexpected/undefined behaviour might occur.

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: how to do indirection
« Reply #14 on: February 04, 2019, 10:09:38 am »
Not part of your question, but nevertheless: it's not recommended to use the ISO mode for units as the concept of units does not exist in the ISO Pascal Standard. Thus unexpected/undefined behaviour might occur.
Indeed, but the modeswitches should work. (and do work)

Maybe your remark can be documented.

A unit should not be able not be compiled in ISO mode, it should throw a "program expected error"........... (which it sometimes does, but not always)
An iso mode program can not be compiled with a "uses" clause.....That will throw  a syntax error....
E.g.:
Code: Pascal  [Select][+][-]
  1. program untitled;
  2. {$mode iso}
  3. uses isounit;
  4. begin
  5. end.
Renders:
Code: Bash  [Select][+][-]
  1. default.pas(3,5) Fatal: Syntax error, "BEGIN" expected but "identifier USES" found
Iso mode always requires a begin end.pair.

« Last Edit: February 04, 2019, 10:26:51 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

 

TinyPortal © 2005-2018