Difference between revisions of "Dynamically loading headers"

From Free Pascal wiki
(Header implementation)
(Header implementation)
Line 124: Line 124:
 
   {$ELSE}
 
   {$ELSE}
 
     {$DEFINE S}
 
     {$DEFINE S}
 +
  {$ENDIF}
 +
 +
  ...header stuff
 +
 
 +
  {$IFDEF S}function{$ELSE}var{$ENDIF}foobar_dosomething{$IFDEF D}: function{$ENDIF}(a:cint; b:cdouble): cint; extdecl;{$IFDEF S}external foobarlib;{$ENDIF}
 +
  ...more external functions
 +
 
 +
 
 +
  {$IFDEF LOAD_DYNAMICALLY}
 +
  function InitializeFoobar(const LibraryName: String = ''): Integer;
 +
  function TryInitializeFoobar(const LibraryName: string = ''): Integer;
 +
  function ReleaseFoobar: Integer;
 +
 +
  var
 +
    FoobarLibrary: TLibHandler;
 +
  {$ENDIF LOAD_DYNAMICALLY}
 +
 +
  implementation
 +
 +
  {$IFDEF LOAD_DYNAMICALLY}
 +
  const
 +
    foobar_symbols: array[0..X] of TLibSymbol = (
 +
      (pvar:@foobar_dosomething; name:'foobar_dosomething'; weak:false), 
 +
      ...more external functions
 +
    );
 +
 +
  function TryInitializeZorba(const LibraryName: string): Integer;
 +
  begin
 +
    Result := TryInitializeLibrary(FoobarLibrary, LibraryName);
 +
  end;
 +
 +
  function InitializeZorba(const LibraryName: String): Integer;
 +
  begin
 +
    Result := InitializeLibrary(FoobarLibrary, LibraryName);
 +
  end;
 +
 
 +
  function ReleaseZorba: Integer;
 +
  begin
 +
    Result := ReleaseLibrary(FoobarLibrary);
 +
  end;
 +
 
 +
  initialization
 +
    FoobarLibrary := LibraryHandler('foobar', [foobarlib,foobarvlib,...moreversions], @foobar_symbols, Length(foobar_symbols));
 
   {$ENDIF}
 
   {$ENDIF}
  

Revision as of 00:54, 1 November 2009

x

Abstract

Currently, most library headers in fpc/packages support only static linking. Some exceptions are mysql, ibase, sqlite (and some other database headers). The idea is now to add dynamic linking support for other headers. This page should provide some place to list pro/contra, implementation details, etc...

Since dynamic linking is not supported by the compiler directly, it's required to define some framework, so that all headers are "implemented" in an uniform way and can be used easily.

Pro

  • loading libraries at runtime (plugin support)

Contra

Implementation (suggestions)

Framework

dynlibs.pas

 type
   PLibHandler = ^TLibHandler;
   
   TLibEventLoading = function(User: Pointer; Handler: PLibHandler): Boolean;
   TLibEventUnloading = procedure(User: Pointer; Handler: PLibHandler);
   
   PPLibSymbol = ^PLibSymbol;
   PLibSymbol = ^TLibSymbol;
   TLibSymbol = record
     pvar: PPointer;  { pointer to Symbol variable }
     name: String;    { name of the Symbol }
     weak: Boolean;   { weak }
   end;
   
   TLibHandler = record
     InterfaceName: String;                { abstract name of the library }
     Defaults     : array of String;       { list of default library filenames }
     Filename     : String;                { handle of the current loaded library }
     Handle       : TLibHandle;            { filename of the current loaded library }
     Loading      : TLibEventLoading;      { loading event, called after the unit is loaded }
     Unloading    : TLibEventUnloading;    { unloading event, called before the unit is unloaded }
     SymCount     : Integer;               { number of symbols }
     Symbols      : PLibSymbol;            { symbol address- and namelist }
     ErrorMsg     : String;                { last error message }
     RefCount     : Integer;               { reference counter }
   end;
   
   
 { handler definition }
 function LibraryHandler(const InterfaceName: String; const DefaultLibraries: array of String;
   const Symbols: PLibSymbol; const SymCount: Integer; const AfterLoading: TLibEventLoading = nil;
   const BeforeUnloading: TLibEventUnloading = nil): TLibHandler;
 
 { initialization/finalization }
 function TryInitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
   const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
 function TryInitializeLibrary(var Handler: TLibHandler; const LibraryName: String = ;
   const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
 function InitializeLibrary(var Handler: TLibHandler; const LibraryNames: array of String;
   const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
 function InitializeLibrary(var Handler: TLibHandler; const LibraryName: String = ;
   const User: Pointer = nil; const NoSymbolErrors: Boolean = False): Integer;
 function ReleaseLibrary(var Handler: TLibHandler; User: Pointer = nil): Integer;
 
 { errors }
 procedure AppendLibraryError(var Handler: TLibHandler; const Msg: String);
 function GetLastLibraryError(var Handler: TLibHandler): String;
 procedure RaiseLibraryException(var Handler: TLibHandler);
 
 { symbol load/clear }
 function LoadLibrarySymbols(const Lib: TLibHandle; const Symbols: PLibSymbol; const Count: Integer;
   const ErrorSym: PPLibSymbol = nil): Boolean;
 procedure ClearLibrarySymbols(const Symbols: PLibSymbol; const Count: Integer);

Naming conventions

For the dynamic headers the suffix "dyn" is added

  • static: someheader.pas
  • dynamic: someheaderdyn.pas

Header implementation

foobar.pas

 unit foobar;
 
 {$i foobar.inc}
 
 end.

foobardyn.pas

 unit foobardyn;
 
 {$DEFINE LOAD_DYNAMICALLY}
 {$i foobar.inc}
 
 end.

foobar.inc (depending on lib)

 {$mode objfpc}{$H+}
 {$MACRO ON}
 ... other possible compiler flags
 
 interface
 
 uses
   ctypes, dynlibs;
 
 {$IFDEF UNIX}
   {$DEFINE extdecl:=cdecl}
   const
     foobarlib = 'libfoobar.'+sharedsuffix;
     foobarvlib = foobarlib+'.0.9.9';
 {$ENDIF}
 {$IFDEF WINDOWS}
   {$DEFINE extdecl:=stdcall}
   const
     foobarlib = 'libfoobar.dll';
     foobarvlib = foobarlib;
 {$ENDIF}
 
 {$IFDEF LOAD_DYNAMICALLY}
   {$DEFINE D}
 {$ELSE}
   {$DEFINE S}
 {$ENDIF}
 ...header stuff
 
 {$IFDEF S}function{$ELSE}var{$ENDIF}foobar_dosomething{$IFDEF D}: function{$ENDIF}(a:cint; b:cdouble): cint; extdecl;{$IFDEF S}external foobarlib;{$ENDIF}
 ...more external functions
 
 
 {$IFDEF LOAD_DYNAMICALLY}
 function InitializeFoobar(const LibraryName: String = ): Integer;
 function TryInitializeFoobar(const LibraryName: string = ): Integer;
 function ReleaseFoobar: Integer;
 var
   FoobarLibrary: TLibHandler;
 {$ENDIF LOAD_DYNAMICALLY}
 implementation
 {$IFDEF LOAD_DYNAMICALLY}
 const
   foobar_symbols: array[0..X] of TLibSymbol = (
     (pvar:@foobar_dosomething; name:'foobar_dosomething'; weak:false),  
     ...more external functions
   );
 function TryInitializeZorba(const LibraryName: string): Integer;
 begin
   Result := TryInitializeLibrary(FoobarLibrary, LibraryName);
 end;
 function InitializeZorba(const LibraryName: String): Integer;
 begin
   Result := InitializeLibrary(FoobarLibrary, LibraryName);
 end;
 
 function ReleaseZorba: Integer;
 begin
   Result := ReleaseLibrary(FoobarLibrary);
 end;
 
 initialization
   FoobarLibrary := LibraryHandler('foobar', [foobarlib,foobarvlib,...moreversions], @foobar_symbols, Length(foobar_symbols));
 {$ENDIF}

Example

Others