Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

interfacing with D Properly return an Array of Struct

this question is referring to the 'new' D : DMD32 D Compiler v2.068.2

for TL;DR if you don't need details skip to the question below

working with visual studio(i am using v2010), by creating a new project -> D -> Dynamic Library

when the project creartion process is complete, in the solution explorer there's 2 files:

  • dllmain.d
  • dll.def

leaving the .def file as it is, i have managed to understand that by adding some new functions to the dllmain.d and prefexing with :

extern (Windows) export  

will export the the function and it will be callable from c#, didn't try it with C or C++.

side note, do not touch any of the existing code unless you know what you're doing.

so the code below works as expected

extern (Windows) export uint D_mathPower(uint p)
{     
    return p * p; 
}

calling it from C# with the following signature:

    [DllImport(@"pathTo...\DynamicLib1.dll", CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurity]
    public static extern uint D_mathPower(uint p);

I could easy use it as follows:

uint powD = D_mathPower(5);

my question is

how do i return an array of structs (preferably the most cost-efficient way) ?

struct dpack{ char* Name; uint Id; }

i have tried using both char[] and char* but with no success.

this is my code so far

extern (Windows) export
dpack[] D_getPacks(uint size)
{
    dpack[] rtDpArr = new dpack[size];
    char[] str = "someText".dup;

    for(uint i=0; i<size; i++)
    {

        str[$ - 1] = cast(char)('0' + i % (126 - '0'));
        rtDpArr[i].Id = i;
        rtDpArr[i].Name= str.dup;
    }
   return rtDpArr;
}


void getPacksPtr(uint size, dpack** DpArr)
{
 // this is the signature i have successfully implemented via c++
}
like image 553
Raj Felix Avatar asked Nov 02 '15 15:11

Raj Felix


1 Answers

Because a D array has a special layout you should rather return a pointer to the first item. Then in C# you can cast each item from the base pointer by reading 8 bytes per 8 bytes (this matches dpack.sizeof), since you already know the count:

struct dpack{ immutable(char)* Name; uint Id; }

extern (Windows) export
void* D_getPacks(uint count)
{
    dpack[] rtDpArr = new dpack[count];
    char[] str = "someText".dup;

    import std.string;
    for(uint i=0; i<count; i++)
    {
        rtDpArr[i].Id = i;
        // add a trailing '\0'
        rtDpArr[i].Name = toStringz(str);
    }
    // pointer to the first item
    return rtDpArr.ptr;
}

Also to cast the .Name member it's necessary to add a terminator, otherwise you cant know the length of the string. This is done by std.string.toStringz which will add a null character at the end of the string. The char* Name member can then be cast as are usually strings provided by a function in a dll with a C interface.

like image 51
Abstract type Avatar answered Sep 27 '22 23:09

Abstract type