www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Passing multidimensional D arrays to C (sorry if duplicate)

reply Andrej Mitrovic <none none.com> writes:
I don't have a minimal working example right now so I'll just have to show you
the client code and the C function prototype. I have a C++ DLL with a C
interface for some of its functions and I am dynamically loading it in my D
application. Here's some code with the usual DLL loading bits left out:

alias extern(C) void function(float** inputs) ProcessProc;
// Some code that loads the DLL, calls Windows' GetProcAddress.. and now we
have a function pointer processFunc:
ProcessProc processFunc;

I have no trouble passing single pointer parameters to C functions, and even
callback functions. But I haven't figured out an elegant way of passing
multidimensional arrays to functions expecting pointers to pointers. I've tried
forcing a 2-dimensional array with a cast, or even changing the function
pointer signature to *fake* that it takes a 2-dimensional array and not a
ptr-2-ptr, but that doesn't work properly as the C code ends up working only
partially and I get back an access violation. 

So my calling code looks like the following. Disregard that I'm using GC's
malloc and not std.c.stdlib.malloc for now please :) :

float[][] _inputs;
float** inputs;

enum numInputs = 4;
enum kBlockSize = 512;

_inputs = new float[][](numInputs, kBlockSize);

// we would now fill the inputs with some random data

inputs = cast(float**)GC.malloc((float*).sizeof * numInputs);

foreach (index; 0 .. numInputs)
{
    inputs[index] = _inputs[index].ptr;
}

// some loop that runs for a number of cycles, im using while true just for
demonstration
while (true)
{
    processFunc(inputs);
}

I don't want to leave you in the dark, so let me explain. I'm using the popular
VST plugin standard, which is used for digital audio plugin effects. The loaded
DLL can be any DLL file that conforms to this standard and has a certain number
and type of functions ready to be called from the client code. 

Basically how it's used is: I send a pointer to a number of buffers to the
DLL's processing function, and the effect does some processing on the data and
writes it back to the buffers.

If it's necessary I'll try to write a simplified C DLL and calling code to show
what's wrong.

P.S. (for those in the know) I have a fully working Host example that can load
VST instruments/effects, check their capabilities, do some processing, and load
their GUI (if they have one). I have not ported any VST classes used to build
the VST instruments yet.

P.P.S. I also have a working ASIO Host, however it's only a wrapper around the
C++ classes. I'm currently having huge issues with COM, and I'm not yet sure if
there's a bug in DMD or in my implementation.

P.P.P.S. Hopefully I'll be able to provide a clean implementation of both VST
and ASIO, and after the necessary registration at Steinberg I'll create a
repository for the projects. Wish me luck. :>
Oct 14 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

 But I haven't figured out an elegant way of passing multidimensional arrays to
functions expecting pointers to pointers.

In D there are no nD dynamic arrays. nD arrays in D may be fixed-sized. The fixed-sized ones are just a contiguous sequence of values (so their rows may be not aligned to 16 bytes). The dynamic arrays may contain other dynamic arrays, so a 2D dynamic array is a 2-word struct that points to an array of 2-word structs, each of them points to an array of the items (so if the row lengths are all the same you are wasting memory). There are few different ways to allocate 2D arrays in C, if your C function expects a pointer to pointers, then it wants an array of pointers to the row, so the D structure is not fit, you need to build an array of pointers: void main() { enum int side = 10; alias int T; auto m = new T[][](side, side); auto m2 = new T*[side]; foreach (i, ref p; m2) p = m[i].ptr; // now you may read m2.ptr from C } Bye, bearophile
Oct 14 2010