src/dlutils

Nim package for easy shared library loading.

Usage

dlgencalls "somelib", paths:
  # proc and var definitions

dlgencalls creates three procs: proc open_somelib_library(): bool, proc close_somelib_library() and proc last_somelib_error().

Open proc

The open proc tries to load shared library defined in paths and loads all symbols defined in body. Paths may be either single string or an array of strings. Subsequent calls are ignored.

The open proc returns true on success or false on error (no library found, one of symbols not found).

Procs and variables marked with unchecked pragma do not cause open function to faile and are set to nil.

Allowed definitions in body:

  • proc: proc (a: cint): cint
  • var: var a: cint
  • where statement

Not allowed in body:

  • export marker * in variable; all functions and variables are exported by default
  • multiple variables in single var statement; use single var statement per variable

Allowed proc pragmas:

  • {.importc: "source_name".} - used when original name is invalid in Nim or you want to change it
  • {.unchecked.} - used when definiton is optional - the proc pointer is set to nil if no such proc is found in library
  • {.varargs.} - used when proc takes varargs

Allowed var pragmas:

  • {.importc: "source_name".} - used when original name is invalid in Nim or you want to change it
  • {.unchecked.} - used when defintion is optional

All other proc/var pragmas are ignored.

Warning: Do not add ptr to variable type, it's done automatically (variable of type cint becomes ptr cint).

Close proc

The close proc unloads the shared library. All symbols loaded are set to nil. Subsequent calls are ignored.

Error proc

The error proc returns a human-readable string describing the most recent error that occured from a call to open_name_library() or empty string on no error.

The returned string does not include a trailing newline.

Example

Source:

import dlutils

# Create open_math_library, close_math_library, last_math_error
# and proc/var symbols defined in body.

dlgencalls "math", ["libm.so", "libm.so.6"]:
  # Required proc. open_math_library returns false if not found.
  proc cbrt (x: cdouble): cdouble
  
  # Optional proc. open_math_library sets sqrt to nil if not found.
  proc sqrt (x: cdouble): cdouble {.unchecked.}
  
  # Function "sqrtf" imported as "sqrt2".
  proc sqrt2 (x: cfloat): cfloat {.importc: "sqrtf".}
  
  # Required var of type ptr cint.
  var reqvar: cint
  
  # Optional var of type ptr clong.
  var optvar {.unchecked.}: clong

Generated code:

import std/dynlib

var math_handle: LibHandle = nil

var cbrt*: proc (x, y: cdouble): cdouble {.cdecl, gcsafe, raises: [].} = nil
var sqrt*: proc (x, y: cdouble): cdouble {.cdecl, gcsafe, raises: [].} = nil
var sqrt2*: proc (x, y: cfloat): cfloat {.cdecl, gcsafe, raises: [].} = nil
var reqvar*: ptr cint = nil
var optvar*: ptr clong = nil

proc open_math_library*(): bool =
  result =
    ##  Open library.
    if math_handle == nil:
      math_handle = loadLib "libm.so"
      if math_handle == nil:
        return false
      cbrt = cast[cbrt.type](symAddr(math_handle, "cbrt"))
      if cbrt == nil:
        return false
      sqrt = cast[sqrt.type](symAddr(math_handle, "sqrt"))
      sqrt2 = cast[sqrt2.type](symAddr(math_handle, "sqrtf"))
      if sqrt2 == nil:
        return false
      reqvar = cast[reqvar.type](symAddr(math_handle, "reqvar"))
      if reqvar == nil:
        return false
      optvar = cast[optvar.type](symAddr(math_handle, "optvar"))
    true

proc close_math_library*() =
  ##  Close library.
  if math_handle != nil:
    cbrt = nil
    sqrt = nil
    sqrt2 = nil
    reqvar = nil
    optvar = nil
    math_handle.unloadLib
    math_handle = nil

proc last_math_error*(): string =
  ##  Returns the most recent error that occured from a call to open proc.
  #[
    code follows…
  ]#

Macros

macro dlgencalls(name: static string; libpaths: static openArray[string];
                 body: untyped): untyped {....raises: [].}

Create open_name_library(): bool, close_name_library(), last_name_error(): string and C procedures/variables declared in body.

Function open_name_library() tries to load shared libary defined in libpaths and loads all symbols defined in body. This function returns true on success or false on error (no library found, symbol not found). Functions and variables marked with unchecked pragam are set to nil. Subsequent calls are ignored.

close_name_library() proc unloads the shared library. All symbols loaded are set to nil. Subsequent calls are ignored.

last_name_error() proc returns a human-readable string describing the most recent error that occured from a call to open_name_library() or empty string on no error.

The returned string does not include a trailing newline.

Templates

template dlgencalls(name: static string; libpath: static string; body: untyped): untyped

Create open_name_library(): bool, close_name_library(), last_name_error(): string and C procedures/variables declared in body.

Accepts single library path.

See dlgencalls for details.

template unchecked() {.pragma.}
Functions and variables marked with this pragma do not cause open proc to fail and are set to nil if not found in shared library.