Home > Back-end >  Nim: How to pass an array of varying size to an argument of a foreign function calling into a .dll?
Nim: How to pass an array of varying size to an argument of a foreign function calling into a .dll?

Time:09-20

Bellow is a minimal example when wrapping the OpenAL32.dll. The foreign function alcCreateContext has the argument attrlist which takes a ptr to an array of type ALCint or nil. The issue is the array can be of different lengths depending on the amount of different flags passed in. The array should be organized as [flag, int, flag, int, ...]. How can this be accomplished in a more dynamic way allowing the inclusion of ALC_FREQUENCY for example? The array size is currently hard coded into the procedure and its nasty.

when defined(windows):
  {.push cdecl, dynlib: "OpenAL32.dll", importc.}
else:
  {.push importc.}

type 
  ALCint = cint
  ALCdevice* = pointer
  ALCcontext* = pointer

const
  ALC_MONO_SOURCES* = 0x00001010
  ALC_STEREO_SOURCES* = 0x00001011
  ALC_FREQUENCY* = 0x00001007

proc alcCreateContext*(device: ALCdevice; attrlist: ptr array[0..3, ALCint]): ALCcontext
proc alcOpenDevice*(devicename: cstring): ALCdevice

const attributes = [ALC_MONO_SOURCES.ALCint, 65536.ALCint, ALC_STEREO_SOURCES.ALCint, 65536.ALCint]

discard alcOpenDevice(nil).alcCreateContext(attributes.unsafeAddr)

I experimented with openArray and other containers. Is the solution some sort of cast? This is also the workaround for getting more then 256 sounds out of OpenAL.

Answer from PMunch. Thank You.

The foreign function now wants ptr UncheckedArray[ALCint] and when passing the argument use cast[ptr UncheckedArray[ALCint]](attributes.unsafeAddr)

when defined(windows):
  {.push cdecl, dynlib: "OpenAL32.dll", importc.}
else:
  {.push importc.}

type 
  ALCint = cint
  ALCdevice* = pointer
  ALCcontext* = pointer

const
  ALC_MONO_SOURCES* = 0x00001010
  ALC_STEREO_SOURCES* = 0x00001011
  ALC_FREQUENCY* = 0x00001007

proc alcCreateContext*(device: ALCdevice; attrlist: ptr UncheckedArray[ALCint]): ALCcontext
proc alcOpenDevice*(devicename: cstring): ALCdevice

const attributes = [ALC_MONO_SOURCES.ALCint, 65536.ALCint, ALC_STEREO_SOURCES.ALCint, 65536.ALCint]

discard alcOpenDevice(nil).alcCreateContext(cast[ptr UncheckedArray[ALCint]](attributes.unsafeAddr))

CodePudding user response:

An array in C is simply a pointer to anywhere with one or more contiguous elements of the same type. So to pass a C array to a function you simply need to get such a pointer. Say for example you have a seq of integers then the address of the first element is a C array. Simply do mySeq[0].addr and you're good. Keep the lifecycle of the data in mind though. If Nim doesn't find any more references to the sequence then the memory will get freed. You can also manually get a pointer with create (https://nim-lang.org/docs/system.html#create,typedesc) and you can cast such pointers to ptr UncheckedArray[T] to be able to use [] on the data in Nim.

  • Related