Recent

Author Topic: Resolving procedure symbols directly?  (Read 2584 times)

TCH

  • Sr. Member
  • ****
  • Posts: 256
    • Oldschool computer
Resolving procedure symbols directly?
« on: January 21, 2025, 12:49:39 am »
AFAIK, it has to be done by this:
Code: Pascal  [Select][+][-]
  1. type Tmyproc = procedure (a: integer; b: whatever);
  2.  
  3. var myproc: Tmyproc;
  4.  
  5. ...
  6.  
  7. myproc := Tmyproc(GetProcedureAddress(libhnd, 'myproc'));
This works, but if the unit's name is the same as the procedure, then it collides with the variable. Is this possible in another way, where i can resolve the procedure directly? Like this:
Code: Pascal  [Select][+][-]
  1. procedure myproc(a: integer; b: whatever);
  2.  
  3. ...
  4.  
  5. myproc := Tmyproc(GetProcedureAddress(libhnd, 'myproc'));

cdbc

  • Hero Member
  • *****
  • Posts: 1808
    • http://www.cdbc.dk
Re: Resolving procedure symbols directly?
« Reply #1 on: January 21, 2025, 01:32:54 am »
Hi
Code: Pascal  [Select][+][-]
  1. type Tmyproc = procedure (a: integer; b: pointer);
  2. ...
  3. Tmyproc(GetProcedureAddress(libhnd, 'myproc'))(123,Self);
...Skip the variable and call it directly, should work...  %)
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

TCH

  • Sr. Member
  • ****
  • Posts: 256
    • Oldschool computer
Re: Resolving procedure symbols directly?
« Reply #2 on: January 21, 2025, 03:39:19 am »
Thanks for the tip, but unfortunately i cannot really do this, since i need to provide an API to a library; i am not the one who will call the procedure.

TRon

  • Hero Member
  • *****
  • Posts: 3928
Re: Resolving procedure symbols directly?
« Reply #3 on: January 21, 2025, 04:29:30 am »
Simplest solution is to rename your unit.

But, in case insisting, create a variable Foo of type TMyproc and set it's value by GetProcAddress as usual. Create a (inlined) procedure named myproc using the same parameters as the library function and inside it 'invoke; variable Foo inside.

It is the same as implementing a c-macro that calls a library function.
« Last Edit: January 21, 2025, 04:31:29 am by TRon »
I do not have to remember anything anymore thanks to total-recall.

TCH

  • Sr. Member
  • ****
  • Posts: 256
    • Oldschool computer
Re: Resolving procedure symbols directly?
« Reply #4 on: January 21, 2025, 04:43:22 am »
DOH! I should have thought of that...
Thank you, the wrapper function will be the solution.

TRon

  • Hero Member
  • *****
  • Posts: 3928
Re: Resolving procedure symbols directly?
« Reply #5 on: January 21, 2025, 04:57:10 am »
You're welcome. Indeed it is named a wrapper function (I was not sure if you were familiar with it)

Would be nice if everything was just as easy to solve  :)

I personally prefer the solution:
uses myproc in 'myproc.xyz';

but that is because I also prefer to name library wrapper units as 'binding.nameoflib.pas' or 'api.nameoflib.pas'.
I do not have to remember anything anymore thanks to total-recall.

Thaddy

  • Hero Member
  • *****
  • Posts: 16520
  • Kallstadt seems a good place to evict Trump to.
Re: Resolving procedure symbols directly?
« Reply #6 on: January 21, 2025, 06:23:34 am »
DOH! I should have thought of that...
Thank you, the wrapper function will be the solution.
For now only, because that may be repaired in the future:
Even the procedure with the same name as the unit may be  invalid in the future.
As I wrote in your other thread, that may well be a bug.
So you are possibly making things worse. Rename the unit.
But I am sure they don't want the Trumps back...

TCH

  • Sr. Member
  • ****
  • Posts: 256
    • Oldschool computer
Re: Resolving procedure symbols directly?
« Reply #7 on: January 21, 2025, 01:07:45 pm »
I personally prefer the solution:
uses myproc in 'myproc.xyz';

but that is because I also prefer to name library wrapper units as 'binding.nameoflib.pas' or 'api.nameoflib.pas'.
I am not really sure what this means; you can use simply a function from an entire unit and can give an alias for it?
For now only, because that may be repaired in the future:
Even the procedure with the same name as the unit may be  invalid in the future.
As I wrote in your other thread, that may well be a bug.
So you are possibly making things worse. Rename the unit.
And i wrote in my other thread, that this is completely illogical. If the procedures' name do not collide with the units', then why do the variables'? This makes no sense. So, if this will be changed for the worse in the future - i mean something will not work, what did -  then that is hardly repairing, but rather wrecking. If something is to be repaired, that is the variable-unit name collision.

I do not consider a bug that the names of procedures and units can co-exist, but rather a feature. It seems that the environment is fully aware of this and handle it properly. An example:

ize.pas:
Code: Pascal  [Select][+][-]
  1. unit ize;
  2.  
  3. interface
  4.  
  5. procedure ize;
  6. procedure ize2;
  7.  
  8. implementation
  9.  
  10. procedure ize;
  11. begin
  12.         write('ize' + #10);
  13. end;
  14.  
  15. procedure ize2;
  16. begin
  17.         write('ize2' + #10);
  18. end;
  19.  
  20. end.
testit.pas:
Code: Pascal  [Select][+][-]
  1. program testit;
  2.  
  3. uses ize;
  4.  
  5. begin
  6.         ize;
  7.         ize2;
  8. end.
fpc testit.pas:
Code: [Select]
Free Pascal Compiler version 3.2.2 [2021/07/09] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling testit.pas
Compiling ize.pas
testit.pas(6,5) Fatal: Syntax error, "." expected but ";" found
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode
fpc testit.pas with scoped call:
Code: Pascal  [Select][+][-]
  1. program testit;
  2.  
  3. uses ize;
  4.  
  5. begin
  6.         ize.ize;
  7.         ize2;
  8. end.
fpc testit.pas:
Code: [Select]
Free Pascal Compiler version 3.2.2 [2021/07/09] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling testit.pas
Linking testit
7 lines compiled, 0.1 sec
The compiler can interpret the unit without any problems and only the call is problematic as it does not know if i refer to the unit's, or the procedure's name, but when i use the scope, it works perfectly. And it is good. Logical. Why would this be needed to "fix"? Why cannot the variables work like this? This makes no sense. If something is a bug, then that it is the variables' name colliding with units', not the procedures' name do not.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8181
Re: Resolving procedure symbols directly?
« Reply #8 on: January 21, 2025, 01:30:15 pm »
I personally prefer the solution:
uses myproc in 'myproc.xyz';

Caution there: that form's implicated in an unresolved problem where the compiler sometimes inexplicably fails to recompile some units.

If we're really talking about dynamic (rather than static) loading, I normally wrap the API of a library in an object: I've got (unpublished) code that helps with that but a substantial example is at https://github.com/MarkMLl/asound

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

TCH

  • Sr. Member
  • ****
  • Posts: 256
    • Oldschool computer
Re: Resolving procedure symbols directly?
« Reply #9 on: January 21, 2025, 02:46:08 pm »
If we're really talking about dynamic (rather than static) loading
Is there any kind of symbol resolving with static linking?
I normally wrap the API of a library in an object: I've got (unpublished) code that helps with that but a substantial example is at https://github.com/MarkMLl/asound
Thanks for the tip, but i have to remain compatible with the "full" Pascal unit which is not OOP. (There is a Pascal unit which is the equivalent of the C source of the library and there is a Pascal unit which only links to the C library and is roughly the equivalent of the C header.)

MarkMLl

  • Hero Member
  • *****
  • Posts: 8181
Re: Resolving procedure symbols directly?
« Reply #10 on: January 21, 2025, 02:58:05 pm »
If we're really talking about dynamic (rather than static) loading
Is there any kind of symbol resolving with static linking?

Yes, what the compiler and linker does.

Quote
I normally wrap the API of a library in an object: I've got (unpublished) code that helps with that but a substantial example is at https://github.com/MarkMLl/asound
Thanks for the tip, but i have to remain compatible with the "full" Pascal unit which is not OOP. (There is a Pascal unit which is the equivalent of the C source of the library and there is a Pascal unit which only links to the C library and is roughly the equivalent of the C header.)

You misunderstand. If you structure things correctly then you can either present the library as a unit for static linkage (i.e. resolved by the compiler and linker) or as an object for dynamic linkage (with the library location as a parameter when the object is instantiated).

The syntax at the point of invocation is identical.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

TCH

  • Sr. Member
  • ****
  • Posts: 256
    • Oldschool computer
Re: Resolving procedure symbols directly?
« Reply #11 on: January 21, 2025, 04:53:22 pm »
Yes, what the compiler and linker does.
I meant runtime resolving.
You misunderstand. If you structure things correctly then you can either present the library as a unit for static linkage (i.e. resolved by the compiler and linker) or as an object for dynamic linkage (with the library location as a parameter when the object is instantiated).

The syntax at the point of invocation is identical.
Sorry, but i do not understand, what do you mean by correctly structured. I think i will illustrate my setup and then you can pinpoint the error in my approach.

First, the original C unit, example.c, which is also the C library:
Code: C  [Select][+][-]
  1. #ifndef _EXAMPLE_C
  2. #define _EXAMPLE_C
  3.  
  4. int example(int whatever)
  5. {
  6.         return whatever + 1;
  7. }
  8.  
  9. #endif
  10.  
Then, it's Pascal equivalent, example.pas:
Code: Pascal  [Select][+][-]
  1. unit example;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. function example(whatever: integer): integer;
  8.  
  9. implementation
  10.  
  11. function example(whatever: integer): integer;
  12. begin
  13.         result := whatever + 1;
  14. end;
  15.  
  16. end.
Then, the C header file for libexample.so (compiled from example.c), example.h:
Code: C  [Select][+][-]
  1. #ifndef _EXAMPLE_H
  2. #define _EXAMPLE_H
  3.  
  4. #ifdef LIBEXAMPLE_MANUAL_LINKING
  5. #include <dlfcn.h>
  6. #include <stdlib.h>
  7. #endif
  8.  
  9. #ifdef LIBEXAMPLE_MANUAL_LINKING
  10.  
  11. #define LIBEXAMPLE_EBADPTR      1
  12. #define LIBEXAMPLE_EDLOPEN      2
  13. #define LIBEXAMPLE_EDLSYM       3
  14.  
  15. typedef int (*example_t)(int);
  16. example_t example;
  17.  
  18. int open_libexample(void **libhnd_out)
  19. {
  20.         void *libhnd;
  21.  
  22.         if (libhnd_out == NULL)
  23.         {
  24.                 return LIBEXAMPLE_EBADPTR;
  25.         }
  26.  
  27.         libhnd = dlopen("libexample.so", RTLD_NOW | RTLD_GLOBAL);
  28.         if (libhnd == NULL)
  29.         {
  30.                 return LIBEXAMPLE_EDLOPEN;
  31.         }
  32.  
  33.         example = (example_t)dlsym(libhnd, "example");
  34.         if ((example == NULL))
  35.         {
  36.                 dlclose(libhnd);
  37.                 return LIBEXAMPLE_EDLSYM;
  38.         }
  39.  
  40.         *libhnd_out = libhnd;
  41.         return 0;
  42. }
  43.  
  44. #else
  45.  
  46. extern int example(int whatever);
  47.  
  48. #endif
  49.  
  50. #endif
  51.  
And finally, the Pascal unit for the library (roughly the equivalent of the C header), example.pp:
Code: Pascal  [Select][+][-]
  1. unit example;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. {$ifdef LIBEXAMPLE_MANUAL_LINKING}
  8.  
  9. type
  10.         PLibHandle = ^TLibHandle;
  11.  
  12. const
  13.         LIBEXAMPLE_EBADPTR      = 1;
  14.         LIBEXAMPLE_EDLOPEN      = 2;
  15.         LIBEXAMPLE_EDLSYM       = 3;
  16.  
  17. type
  18.         Texample = function (whatever: integer): integer; cdecl;
  19.  
  20. var
  21.         example: Texample;
  22.  
  23. function open_libexample(libhnd_out: PLibHandle): integer;
  24.  
  25. {$else}
  26.  
  27. {$LinkLib c}
  28. function example(whatever: integer): integer; cdecl; external 'libconfparse';
  29.  
  30. {$endif}
  31.  
  32. implementation
  33.  
  34. {$ifdef LIBEXAMPLE_MANUAL_LINKING}
  35.  
  36. uses DynLibs;
  37.  
  38. function open_libexample(libhnd_out: PLibHandle): integer;
  39. var libhnd: TLibHandle;
  40. begin
  41.         if (libhnd_out = nil) then
  42.         begin
  43.                 result := LIBEXAMPLE_EBADPTR;
  44.                 exit;
  45.         end;
  46.  
  47.         libhnd := LoadLibrary('libexample.so');
  48.         if (libhnd = DynLibs.NilHandle) then
  49.         begin
  50.                 result := LIBEXAMPLE_EDLOPEN;
  51.                 exit;
  52.         end;
  53.  
  54.         example := Texample(GetProcedureAddress(libhnd, 'example'));
  55.         if ((example = nil)) then
  56.         begin
  57.                 FreeLibrary(libhnd);
  58.                 result := LIBEXAMPLE_EDLSYM;
  59.                 exit;
  60.         end;
  61.  
  62.         libhnd_out^ := libhnd;
  63.         result := 0;
  64.         exit;
  65. end;
  66.  
  67. {$endif}
  68.  
  69. end.
I link the program using the header/library unit by dynamically linking to the library. Either by manually (-DLIBEXAMPLE_MANUAL_LINKING/-dLIBEXAMPLE_MANUAL_LINKING), or automatically (-lexample for C and Pascal does the job for me).

The original Pascal unit is usable, as the procedure will not collide with the unit. The for-library version on the other hand do (at least in the case of manual linking), because the procedure there is defined as a variable to make it possible to resolve it manually. And this was solved by TRon's suggestion (thanks again), to rename the variable to example_wrap, resolve that and provide a wrapper function:
Code: Pascal  [Select][+][-]
  1. function example(whatever: integer): integer;
  2. begin
  3.         result := example_wrap(whatever);
  4. end;
What would you do differently?

TRon

  • Hero Member
  • *****
  • Posts: 3928
Re: Resolving procedure symbols directly?
« Reply #12 on: January 21, 2025, 09:53:38 pm »
Caution there: that form's implicated in an unresolved problem where the compiler sometimes inexplicably fails to recompile some units.
Thank you for the warning MarkMLI. I am aware. At the same time the dotted (also rtl as well as some lcl) units suffer the same faith so eventually it should be fixed.

I am not really sure what this means; you can use simply a function from an entire unit and can give an alias for it?
Not exactly. What it does is using the unit e.g. myproc.xyz.pas (which then allows to name your variable myproc to be declared in that unit) in the 'namespace' myproc. You can then access the function myproc in unit myproc.xyz as myproc.myproc(parameters ...). Ofc the 'namespace' myproc is just an example as it can be named anything you want to as long as it doesn't collide with anything else.

Point I was trying to get across is that you can name your header unit anything you'd like (e.g. I do not fully grasp why you seem to feel the need to stick to the original name as it is arbitrary but I do respect the each to its own prerogative).
I do not have to remember anything anymore thanks to total-recall.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8181
Re: Resolving procedure symbols directly?
« Reply #13 on: January 21, 2025, 10:08:15 pm »
I've just uploaded the stuff I use to manage dynamically-loaded libraries to https://github.com/MarkMLl/dynamod after realising that I really didn't have it in me to write a 50-page manual: there's an adequate demo project.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

TCH

  • Sr. Member
  • ****
  • Posts: 256
    • Oldschool computer
Re: Resolving procedure symbols directly?
« Reply #14 on: January 22, 2025, 12:13:54 am »
Not exactly. What it does is using the unit e.g. myproc.xyz.pas (which then allows to name your variable myproc to be declared in that unit) in the 'namespace' myproc. You can then access the function myproc in unit myproc.xyz as myproc.myproc(parameters ...). Ofc the 'namespace' myproc is just an example as it can be named anything you want to as long as it doesn't collide with anything else.
But what is .xyz? AFAIK, there cannot be a dot in the names of units. Can you please give me some example code? Because i cannot get a grip on the concept, how can i workaround the unit/variable name colliding within the uses clause.
Point I was trying to get across is that you can name your header unit anything you'd like (e.g. I do not fully grasp why you seem to feel the need to stick to the original name as it is arbitrary but I do respect the each to its own prerogative).
Because the header unit must be a drop-in replacement for the full unit.
I've just uploaded the stuff I use to manage dynamically-loaded libraries to https://github.com/MarkMLl/dynamod after realising that I really didn't have it in me to write a 50-page manual: there's an adequate demo project.
Thanks, i'll check it more in-depth some time; now i just peeked into dynamod.pas. I am not sure, if i got it right, but what i have seen was a very simliar function wrapping than TRon suggested, but with OOP approach.

 

TinyPortal © 2005-2018