Recent

Author Topic: How to create C library bindings (using SO or DLL)  (Read 5356 times)

istoica

  • New Member
  • *
  • Posts: 21
How to create C library bindings (using SO or DLL)
« on: January 14, 2019, 03:52:58 pm »
I am trying to create bindings for libgit2 - I am learning while doing it.
I found this https://github.com/libgit2/GitForDelphi/blob/master/uGitForDelphi.pas but it seems to be too Windows specific.
If any of you has some examples of how to achieve this, it is difficult finding proper code as inspiration.
I need to write a function mapping, but don't know how. I want things to be as cross platform as possible.
Any tips are tremendously appreciated.

So far I have this:

Code: Pascal  [Select][+][-]
  1. unit ugit2;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, LCLIntf, LCLType, DynLibs;
  9.  
  10. const
  11.   { sys/commit.h }
  12.   { sys/config.h }
  13.   GIT_CONFIG_BACKEND_VERSION = 1;
  14.   { sys/diff.h }
  15.   GIT_DIFF_PERFDATA_VERSION = 1;
  16.   { sys/filter.h }
  17.   GIT_FILTER_CRLF = 'crlf';
  18.   GIT_FILTER_IDENT = 'ident';
  19.   GIT_FILTER_CRLF_PRIORITY = 0;
  20.   GIT_FILTER_IDENT_PRIORITY = 100;
  21.   GIT_FILTER_DRIVER_PRIORITY = 200;
  22.   GIT_FILTER_VERSION = 1;
  23.   { sys/hashsig.h }
  24.   { sys/index.h }
  25.   { sys/mempack.h }
  26.   { sys/merge.h }
  27.   GIT_MERGE_DRIVER_TEXT = 'text';
  28.   GIT_MERGE_DRIVER_BINARY = 'binary';
  29.   GIT_MERGE_DRIVER_UNION = 'union';
  30.   { sys/odb_backend.h }
  31.   { sys/openssl.h }
  32.   { sys/refdb_backend.h }
  33.   { sys/reflog.h }
  34.   { sys/refs.h }
  35.   { sys/repository.h }
  36.   { sys/stream.h }
  37.   { sys/time.h }
  38.   { sys/transport.h }
  39.   { annotated_commit.h }
  40.   { attr.h }
  41.   { blame.h }
  42.   { blob.h }
  43.   { branch.h }
  44.   { buffer.h }
  45.   { checkout.h }
  46.   GIT_BLAME_OPTIONS_VERSION = 1;
  47.   { cherrypick.h }
  48.   GIT_CHERRYPICK_OPTIONS_VERSION = 1;
  49.   { clone.h }
  50.   { commit.h }
  51.   { common.h }
  52.   {$IFDEF WINDOWS}
  53.   GIT_PATH_LIST_SEPARATOR = ';';
  54.   {$ELSE}
  55.   GIT_PATH_LIST_SEPARATOR = ':';
  56.   {$ENDIF}
  57.   GIT_PATH_MAX = 4096;
  58.   GIT_CLONE_OPTIONS_VERSION = 1;
  59.   GIT_OID_HEX_ZERO = '0000000000000000000000000000000000000000';
  60.   { config.h }
  61.   { cred_helpers.h }
  62.   { describe.h }
  63.   GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS = 10;
  64.   GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE = 7;
  65.   GIT_DESCRIBE_OPTIONS_VERSION = 1;
  66.   GIT_DESCRIBE_FORMAT_OPTIONS_VERSION = 1;
  67.   { diff.h }
  68.   GIT_DIFF_OPTIONS_VERSION = 1;
  69.   GIT_DIFF_HUNK_HEADER_SIZE = 128;
  70.   GIT_DIFF_FIND_OPTIONS_VERSION = 1;
  71.   GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION = 1;
  72.   GIT_DIFF_PATCHID_OPTIONS_VERSION = 1;
  73.   { errors.h }
  74.   { filter.h }
  75.   { global.h }
  76.   { graph.h }
  77.   { ignore.h }
  78.   { index.h }
  79.   GIT_IDXENTRY_NAMEMASK = $0fff;
  80.   GIT_IDXENTRY_STAGEMASK = $3000;
  81.   GIT_IDXENTRY_STAGESHIFT = 12;
  82.   { indexer.h }
  83.   { inttypes.h }
  84.   { merge.h }
  85.   GIT_MERGE_FILE_INPUT_VERSION = 1;
  86.   GIT_MERGE_CONFLICT_MARKER_SIZE = 7;
  87.   GIT_MERGE_FILE_OPTIONS_VERSION = 1;
  88.   GIT_MERGE_OPTIONS_VERSION = 1;
  89.   { message.h }
  90.   { net.h }
  91.   GIT_DEFAULT_PORT = '9418';
  92.   { notes.h }
  93.   { object.h }
  94.   { odb.h }
  95.   { odb_backend.h }
  96.   { oid.h }
  97.   GIT_OID_RAWSZ = 20;
  98.   GIT_OID_HEXSZ = GIT_OID_RAWSZ * 2;
  99.   GIT_OID_MINPREFIXLEN = 4;
  100.   { oidarray.h }
  101.   { pack.h }
  102.   { patch.h }
  103.   { pathspec.h }
  104.   { proxy.h }
  105.   GIT_PROXY_OPTIONS_VERSION = 1;
  106.   { rebase.h }
  107.   GIT_REBASE_OPTIONS_VERSION = 1;
  108.   GIT_REBASE_NO_OPERATION = maxint;
  109.   { refdb.h }
  110.   { reflog.h }
  111.   { refs.h }
  112.   { refspec.h }
  113.   { remote.h }
  114.   GIT_REMOTE_CALLBACKS_VERSION = 1;
  115.   GIT_FETCH_OPTIONS_VERSION = 1;
  116.   GIT_PUSH_OPTIONS_VERSION = 1;
  117.   { repository.h }
  118.   GIT_REPOSITORY_INIT_OPTIONS_VERSION = 1;
  119.   { reset.h }
  120.   { revert.h }
  121.   GIT_REVERT_OPTIONS_VERSION = 1;
  122.   { revparse.h }
  123.   { revwalk.h }
  124.   { signature.h }
  125.   { stash.h }
  126.   GIT_STASH_APPLY_OPTIONS_VERSION = 1;
  127.   { status.h }
  128.   GIT_STATUS_OPTIONS_VERSION = 1;
  129.   { stdint.h }
  130.   { strarray.h }
  131.   { submodule.h }
  132.   GIT_SUBMODULE_STATUS__IN_FLAGS = $000F;
  133.   GIT_SUBMODULE_STATUS__INDEX_FLAGS = $0070;
  134.   GIT_SUBMODULE_STATUS__WD_FLAGS = $3F80;
  135.   GIT_SUBMODULE_UPDATE_OPTIONS_VERSION = 1;
  136.   { tag.h }
  137.   { trace.h }
  138.   { transaction.h }
  139.   { transport.h }
  140.   GIT_TRANSPORT_VERSION = 1;
  141.   { tree.h }
  142.   { types.h }
  143.   { version.h }
  144.   { worktree.h }
  145.   GIT_WORKTREE_ADD_OPTIONS_VERSION = 1;
  146.   GIT_WORKTREE_PRUNE_OPTIONS_VERSION = 1;
  147.  
  148. type
  149.   git_oid = record
  150.    id: array[0..GIT_OID_RAWSZ-1] of Byte;
  151.   end;
  152.   TGitHashSigOption = (
  153.     GIT_HASHSIG_NORMAL = 0,
  154.     GIT_HASHSIG_IGNORE_WHITESPACE = 1 Shl 0,
  155.     GIT_HASHSIG_SMART_WHITESPACE = 1 Shl 1,
  156.     GIT_HASHSIG_ALLOW_SMALL_FILES = 1 Shl 2
  157.   );
  158.   git_hashsig_option_t = Integer;
  159.   TGitTransportFlags = (
  160.     GIT_TRANSPORTFLAGS_NONE = 0
  161.   );
  162.   git_transport_flags_t = Integer;
  163.   TGitSmartService = (
  164.     GIT_SERVICE_UPLOADPACK_LS = 1,
  165.     GIT_SERVICE_UPLOADPACK = 2,
  166.     GIT_SERVICE_RECEIVEPACK_LS = 3,
  167.     GIT_SERVICE_RECEIVEPACK = 4
  168.   );
  169.   git_smart_service_t = Integer;
  170.   TGitError = (
  171.     GITERR_NONE = 0,
  172.     GITERR_NOMEMORY,
  173.     GITERR_OS,
  174.     GITERR_INVALID,
  175.     GITERR_REFERENCE,
  176.     GITERR_ZLIB,
  177.     GITERR_REPOSITORY,
  178.     GITERR_CONFIG,
  179.     GITERR_REGEX,
  180.     GITERR_ODB,
  181.     GITERR_INDEX,
  182.     GITERR_OBJECT,
  183.     GITERR_NET,
  184.     GITERR_TAG,
  185.     GITERR_TREE,
  186.     GITERR_INDEXER,
  187.     GITERR_SSL,
  188.     GITERR_SUBMODULE,
  189.     GITERR_THREAD,
  190.     GITERR_STASH,
  191.     GITERR_CHECKOUT,
  192.     GITERR_FETCHHEAD,
  193.     GITERR_MERGE,
  194.     GITERR_SSH,
  195.     GITERR_FILTER,
  196.     GITERR_REVERT,
  197.     GITERR_CALLBACK,
  198.     GITERR_CHERRYPICK,
  199.     GITERR_DESCRIBE,
  200.     GITERR_REBASE,
  201.     GITERR_FILESYSTEM,
  202.     GITERR_PATCH,
  203.     GITERR_WORKTREE,
  204.     GITERR_SHA1
  205.   );
  206.   git_error_t = Integer;
  207.   TGitErrorCode = (
  208.     GIT_OK              =  0,
  209.     GIT_ERROR           = -1,
  210.     GIT_ENOTFOUND       = -3,
  211.     GIT_EEXISTS         = -4,
  212.     GIT_EAMBIGUOUS      = -5,
  213.     GIT_EBUFS           = -6,
  214.     GIT_EUSER           = -7,
  215.     GIT_EBAREREPO       =  -8,
  216.     GIT_EUNBORNBRANCH   =  -9,
  217.     GIT_EUNMERGED       = -10,
  218.     GIT_ENONFASTFORWARD = -11,
  219.     GIT_EINVALIDSPEC    = -12,
  220.     GIT_ECONFLICT       = -13,
  221.     GIT_ELOCKED         = -14,
  222.     GIT_EMODIFIED       = -15,
  223.     GIT_EAUTH           = -16,
  224.     GIT_ECERTIFICATE    = -17,
  225.     GIT_EAPPLIED        = -18,
  226.     GIT_EPEEL           = -19,
  227.     GIT_EEOF            = -20,
  228.     GIT_EINVALID        = -21,
  229.     GIT_EUNCOMMITTED    = -22,
  230.     GIT_EDIRECTORY      = -23,
  231.     GIT_EMERGECONFLICT  = -24,
  232.     GIT_PASSTHROUGH     = -30,
  233.     GIT_ITEROVER        = -31,
  234.     GIT_RETRY           = -32,
  235.     GIT_EMISMATCH       = -33
  236.   );
  237.   git_error_code = Integer;
  238.   TGitObjectType = (
  239.     GIT_OBJ_BAD       = -1,
  240.     GIT_OBJ__EXT1     = 0,
  241.     GIT_OBJ_COMMIT    = 1,
  242.     GIT_OBJ_TREE      = 2,
  243.     GIT_OBJ_BLOB      = 3,
  244.     GIT_OBJ_TAG       = 4,
  245.     GIT_OBJ__EXT2     = 5,
  246.     GIT_OBJ_OFS_DELTA = 6,
  247.     GIT_OBJ_REF_DELTA = 7
  248.   );
  249.   TGitSort = (
  250.     GIT_SORT_NONE = 0,
  251.     GIT_SORT_TOPOLOGICAL = 1 Shl 0,
  252.     GIT_SORT_TIME = 1 Shl 1,
  253.     GIT_SORT_REVERSE = 1 Shl 2
  254.   );
  255.   git_sort_t = Integer;
  256.   { pointers }
  257.   Pgit_oid = Pointer;
  258.   Pgit_repository = Pointer;
  259.   Pgit_tag = Pointer;
  260.  
  261. implementation
  262.  
  263. var
  264.   libgit2: THandle;
  265.  
  266. procedure BindFuncs(aBind: Boolean);
  267. begin
  268. end;
  269.  
  270. function InitLibgit2: Boolean;
  271. begin
  272.   if libgit2 = 0 then
  273.   begin
  274.     libgit2 := DynLibs.LoadLibrary('libgit2.' + SharedSuffix);
  275.     if libgit2 <> DynLibs.NilHandle then
  276.       BindFuncs(true);
  277.   end;
  278.  
  279.   Result := libgit2 > 0;
  280. end;
  281.  
  282. procedure FreeLibgit2;
  283. begin
  284.   if libgit2 <> 0 then
  285.   begin
  286.     DynLibs.FreeLibrary(libgit2);
  287.     libgit2 := 0;
  288.     BindFuncs(false);
  289.   end;
  290. end;
  291.  
  292. { bindings }
  293.  
  294. initialization
  295.   libgit2 := 0;
  296.  
  297. finalization
  298.   FreeLibgit2;
  299.  
  300. end.
  301.  

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: How to create C library bindings (using SO or DLL)
« Reply #1 on: January 15, 2019, 09:15:22 am »
You could give the h2pas tool a try that is provided with FPC and also has a wizard in Lazarus. Additionally you might want to take a look at the wiki entry about Creating bindings for C libraries.

A little side note regarding your posted unit: you should not need to use units LCLIntf and LCLTypes in a interface unit for a C library. What you might find useful however is the ctypes unit which declares some types that are normally used in C suitable for the current platform.

istoica

  • New Member
  • *
  • Posts: 21
Re: How to create C library bindings (using SO or DLL)
« Reply #2 on: January 15, 2019, 05:42:05 pm »
Thank you for the tips, I am more interested in Runtime bindings, like uGitForDelphi does and achieve this using DynLibs.
There is hardly any documentation about this unit, except the tests.

For now, I think I am giving up interfacing libgit2 and create a command line wrapper against the git cli

istoica

  • New Member
  • *
  • Posts: 21
Re: How to create C library bindings (using SO or DLL)
« Reply #3 on: January 15, 2019, 06:08:23 pm »
I found this which is helpful http://dkhramov.dp.ua/Comp.CallingFortranDllFromFreePascal even if it is in Russian, trying to find as many projects that use Dynlibs

Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 1186
    • Burdjia
Re: How to create C library bindings (using SO or DLL)
« Reply #4 on: January 15, 2019, 07:55:05 pm »
If you plan to use the binding in different platforms, I recommend you to use CTypes unit. Or you can define your own binding types as I did with Allegro.pas (al5base.pas).
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: How to create C library bindings (using SO or DLL)
« Reply #5 on: January 16, 2019, 09:50:07 am »
Thank you for the tips, I am more interested in Runtime bindings, like uGitForDelphi does and achieve this using DynLibs.
There is hardly any documentation about this unit, except the tests.
h2pas has the -P option which uses function pointers and unit DynLibs.
Also unit DynLibs simply provides the LoadLibrary, GetProcAddress and FreeLibrary functions as known from Delphi and the Windows API (plus some other stuff, but those three are the important ones).

istoica

  • New Member
  • *
  • Posts: 21
Re: How to create C library bindings (using SO or DLL)
« Reply #6 on: January 16, 2019, 09:54:22 am »
This looks awesome, I will replicate almost all you did there :D, thank you!

istoica

  • New Member
  • *
  • Posts: 21
Re: How to create C library bindings (using SO or DLL)
« Reply #7 on: January 16, 2019, 12:47:23 pm »
Withe the Allegro library help I am finally progressing, How can string parameters be passed and initialized ? I am trying to port this:

Code: C  [Select][+][-]
  1. int git_repository_ident(const char **name, const char **email, const git_repository *repo);

from https://github.com/libgit2/libgit2/blob/788fccc40b28cea2e15bb7a0cafc706bb86d13c1/include/git2/repository.h#L851

What I have achieved is:

Code: Pascal  [Select][+][-]
  1. type
  2.  
  3. LG2_STR = ANSISTRING;
  4. {$IFDEF ISDELPHI2009ANDUP}
  5.     LG2_STRptr = PAnsiChar;
  6. {$ELSE}
  7.     LG2_STRptr = PCHAR;
  8. {$ENDIF}
  9.  
  10. function git_repository_ident(name: LG2_STRptr; email: LG2_STRptr; const repo: Pgit_repository): LG2_INT; cdecl; external LIBGIT2_LIB_NAME;
  11.  
  12. { then I invoke it }
  13.  
  14. constructor TGitRepository.Create(const repository_path: String);
  15. var  name: string;
  16.     email: string;
  17. begin
  18.    git_repository_ident(LG2_STRptr(string(name)), LG2_STRptr(string(email)), repo);
  19. end;
  20.  


But name and email are empty, any clues/tips ?

istoica

  • New Member
  • *
  • Posts: 21
Re: How to create C library bindings (using SO or DLL)
« Reply #8 on: January 16, 2019, 06:55:45 pm »
I have finally managed to create a more complete wrapper and it shows up good, but I have some questions:


1) Why does `git_repository_workdir` work only in the constructor of TGitRepository ? The property reader, which is the same code, returns an empty string.
Code: Pascal  [Select][+][-]
  1. function TGitRepository.GetWorkDir(): AnsiString;
  2. begin
  3.   Result := git_repository_workdir(self.repo^);
  4. end;
  5.  
2) Is the wrapped function
Code: [Select]
git_repository_ident correctly declared for this C function?
Code: C  [Select][+][-]
  1. GIT_EXTERN(int) git_repository_ident(const char **name, const char **email, const git_repository *repo);
  2.  
3) How do I call this wrapped function in FreePascal so that I get the values inside strings ?


The full wrapper can be found at https://pastebin.com/PH9P4ZFV


Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 1186
    • Burdjia
Re: How to create C library bindings (using SO or DLL)
« Reply #9 on: January 16, 2019, 08:11:17 pm »
Withe the Allegro library help I am finally progressing, How can string parameters be passed and initialized ? I am trying to port this:

Code: C  [Select][+][-]
  1. int git_repository_ident(const char **name, const char **email, const git_repository *repo);
This is a big guy.  Note that "name" and "email" are pointer to a pointer to char.  Note also that Free Pascal and Delphi are able to convert STRING to pointer to char "on the fly".  Also note that in C pointers are used to allow the function to modify the parameters, much like VAR and OUT in Pascal (actually, compiler converts them to pointers when using CDECL).  Yes, there's "const" but it just prevent you to change the pointer address not the content.  So I would translate to something like:

Code: Pascal  [Select][+][-]
  1. TYPE
  2.   LG2_STR = ANSISTRING;
  3.  
  4.   FUNCTION git_repository_indent (
  5.     VAR name, email: LG2_STR;
  6.     CONST repo: Pgit_repository  { <- Keep this as pointer assuming it's an object. }
  7.   ): LONGINT; { <- Maybe you should use other INTEGER type(?) }
  8.   CDECL;
  9.  

But, if function returns a string then better use a pointer:
Code: Pascal  [Select][+][-]
  1. type
  2. {$IFDEF ISDELPHI2009ANDUP}
  3.     LG2_STRptr = PAnsiChar;
  4. {$ELSE}
  5.     LG2_STRptr = PCHAR;
  6. {$ENDIF}
  7.  
  8.   FUNCTION git_example_string_function (CONST Param: INTEGER): LG2_STRptr; CDECL;
  9.  

String management in C (and C++) is quite complex.  I needed a lot of testing and debugging with GDB until I find the way it worked on Allegro.  May be you'll need to do it also until understand how that git stuff worked.
« Last Edit: January 16, 2019, 08:14:36 pm by Ñuño_Martínez »
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

HeavyUser

  • Sr. Member
  • ****
  • Posts: 397
Re: How to create C library bindings (using SO or DLL)
« Reply #10 on: January 16, 2019, 08:19:10 pm »
Withe the Allegro library help I am finally progressing, How can string parameters be passed and initialized ? I am trying to port this:

Code: C  [Select][+][-]
  1. int git_repository_ident(const char **name, const char **email, const git_repository *repo);
This is a big guy.  Note that "name" and "email" are pointer to a pointer to char.  Note also that Free Pascal and Delphi are able to convert STRING to pointer to char "on the fly".  Also note that in C pointers are used to allow the function to modify the parameters, much like VAR and OUT in Pascal (actually, compiler converts them to pointers when using CDECL).  Yes, there's "const" but it just prevent you to change the pointer address not the content.  So I would translate to something like:

Code: Pascal  [Select][+][-]
  1. TYPE
  2.   LG2_STR = ANSISTRING;
  3.  
  4.   FUNCTION git_repository_indent (
  5.     VAR name, email: LG2_STR;
  6.     CONST repo: Pgit_repository  { <- Keep this as pointer assuming it's an object. }
  7.   ): LONGINT; { <- Maybe you should use other INTEGER type(?) }
  8.   CDECL;
  9.  

But, if function returns a string then better use a pointer:
Code: Pascal  [Select][+][-]
  1. type
  2. {$IFDEF ISDELPHI2009ANDUP}
  3.     LG2_STRptr = PAnsiChar;
  4. {$ELSE}
  5.     LG2_STRptr = PCHAR;
  6. {$ENDIF}
  7.  
  8.   FUNCTION git_example_string_function (CONST Param: INTEGER): LG2_STRptr; CDECL;
  9.  

String management in C (and C++) is quite complex.  I needed a lot of testing and debugging with GDB until I find the way it worked on Allegro.
since the dll and the exe have their own distinct memory managers the above can only be used if
1) the dll exports a function to free what ever memory it created with its internal memory manager and the user makes sure that the strings returns are freed as soon they become obsolete.
2) the dll needs to attach it self to the exe's memory manager which will enable the exe's memory manager to create and destroy the memory that the dll requires.
3) the functions are changed to allow the exe to allocate the required memory and give it to the dll to fill it with the data.

As it is now it would be unstable to use.


PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: How to create C library bindings (using SO or DLL)
« Reply #11 on: January 17, 2019, 12:46:10 pm »
Withe the Allegro library help I am finally progressing, How can string parameters be passed and initialized ? I am trying to port this:

Code: C  [Select][+][-]
  1. int git_repository_ident(const char **name, const char **email, const git_repository *repo);
This is a big guy.  Note that "name" and "email" are pointer to a pointer to char.  Note also that Free Pascal and Delphi are able to convert STRING to pointer to char "on the fly".  Also note that in C pointers are used to allow the function to modify the parameters, much like VAR and OUT in Pascal (actually, compiler converts them to pointers when using CDECL).  Yes, there's "const" but it just prevent you to change the pointer address not the content.  So I would translate to something like:

Code: Pascal  [Select][+][-]
  1. TYPE
  2.   LG2_STR = ANSISTRING;
  3.  
  4.   FUNCTION git_repository_indent (
  5.     VAR name, email: LG2_STR;
  6.     CONST repo: Pgit_repository  { <- Keep this as pointer assuming it's an object. }
  7.   ): LONGINT; { <- Maybe you should use other INTEGER type(?) }
  8.   CDECL;
  9.  

A big NO here. Do not mix the Pascal string types with C PChars!

A more correct translation would be this:
Code: Pascal  [Select][+][-]
  1. function get_repository_ident(var name, email: PAnsiChar; repo: Pgit_repository): cint; cdecl;

It would then be used like this:
Code: Pascal  [Select][+][-]
  1. procedure GetRepositoryIdent(aRepo: Pgit_repository; var aName, aEMail: String);
  2. var
  3.   n, e: PAnsiChar;
  4.   res: cint;
  5. begin
  6.   res := get_repository_ident(n, e, aRepo);
  7.   // ToDo: check res for error and raise exception if error
  8.   aName := StrPas(n);
  9.   aEMail := StrPas(e);
  10.   // no need to free n and e as they are declared as "const char*", thus we assume they stay alive
  11. end;

Tz

  • Jr. Member
  • **
  • Posts: 54
  • Tz with FPC Pen Cil
Re: How to create C library bindings (using SO or DLL)
« Reply #12 on: January 17, 2019, 04:27:23 pm »
I have some problem too,  while learning examples like openssl, postgresql3dyn, mysql etc

I found the easiest way is using static loading
just use H2PAS then using cdecl; external 'procname';

but had problem too, so I try to create a small class, here
an example

Code: Pascal  [Select][+][-]
  1. unit zdynlib;
  2. {$MODE DELPHI}
  3. {$H+}
  4. interface
  5.  
  6. uses Classes, DynLibs, cTypes, SysUtils;
  7.  
  8. type
  9.  
  10.    TDynLib = class (TObject)
  11.    private
  12.       FCS:        TRTLCriticalSection;
  13.       FHandle:    TLibHandle;
  14.       FNotLoaded: TStringList;
  15.    public
  16.       constructor Create(const AName: string);
  17.       destructor  Destroy; override;
  18.       function    IsLoaded: boolean;
  19.       function    ExtName(const AName: string): pointer;
  20.       property    NotLoaded: TStringList read FNotLoaded;
  21.       procedure   Enter;
  22.       procedure   Leave;
  23.    end;
  24.  
  25. implementation
  26.  
  27.       constructor TDynLib.Create(const AName: string);
  28.       begin
  29.          inherited Create;
  30.          FNotLoaded := TStringList.Create;
  31.          InitCriticalSection(FCS);
  32.          FHandle    := LoadLibrary(AName);
  33.       end;
  34.  
  35.       destructor  TDynLib.Destroy;
  36.       begin
  37.          if    FHandle <> NilHandle
  38.          then  FreeLibrary(FHandle);
  39.          DoneCriticalSection(FCS);
  40.          if    Assigned(FNotLoaded)
  41.          then  FNotLoaded.Free;
  42.          inherited;
  43.       end;
  44.  
  45.       function    TDynLib.IsLoaded: boolean;
  46.       begin
  47.          result := (FHandle <> NilHandle) and (FNotLoaded.Count = 0);
  48.       end;
  49.  
  50.       function    TDynLib.ExtName(const AName: string): pointer;
  51.       begin
  52.          if    FHandle <> NilHandle
  53.          then  begin
  54.                result := GetProcAddress(FHandle, PChar(AName));
  55.                if    not Assigned(result)
  56.                then  FNotLoaded.Add(AName);
  57.                end;
  58.       end;
  59.  
  60.       procedure   TDynLib.Enter;
  61.       begin
  62.          EnterCriticalSection(FCS);
  63.       end;
  64.  
  65.       procedure   TDynLib.Leave;
  66.       begin
  67.          LeaveCriticalSection(FCS);
  68.       end;
  69.  
  70. end.
  71.  

example for  libssl, I m working for new openssl 1.1.0

Code: Pascal  [Select][+][-]
  1.  
  2. unit zlibssl;
  3. {$MODE DELPHI}
  4. {$H+}
  5. interface
  6.  
  7. uses zdynlib, ctypes;
  8.  
  9. var
  10.    libssl: TDynLib;
  11.  
  12.    // Using H2PAS from h file
  13.    OpenSSL_version: function(t: cint): pchar; cdecl;
  14.  
  15. implementation
  16.    
  17. initialization
  18.    libssl := TDynLib.Create('libssl.so.1.1');
  19.    
  20.    libssl.Enter;
  21.    OpenSSL_version := libssl.ExtName('OpenSSL_version');  
  22.    libssl.Leave;
  23.    
  24. finalization
  25.    libssl.Free;
  26.    
  27. end.
  28.  
  29.  

and the test program

Code: Pascal  [Select][+][-]
  1.  
  2. program ssl;
  3. {$MODE DELPHI}
  4. {$H+}
  5.  
  6. uses   zlibssl;
  7.  
  8. begin
  9.  
  10.    if    libssl.IsLoaded
  11.    then  WriteLn(OpenSSL_version(0))
  12.    else  WriteLn(libssl.NotLoaded.Text);
  13.  
  14. end.
  15.  
  16.  

not completed yet, but may be help full for start.



whza

  • New member
  • *
  • Posts: 9
Re: How to create C library bindings (using SO or DLL)
« Reply #13 on: January 18, 2019, 11:48:24 am »
Will this help? Still a lot of manual work, but not too bad if you use it in conjunction with h2pas.

https://github.com/williamhunter/pascal-bindings-for-c (look at the Creating Pascal bindings for C (v1.0).pdf file)

Regards,
William

istoica

  • New Member
  • *
  • Posts: 21
Re: How to create C library bindings (using SO or DLL)
« Reply #14 on: January 19, 2019, 10:26:51 am »
Thank you all so much, any info is well received, so big big thanks @whza @Tz @PascalDragon @HeavyUser @Ñuño_Martínez, your support is invaluable, I am using h2pas with manual tweaking, will try again to continue with my port today :D and come back with progress report.

 

TinyPortal © 2005-2018