2015-11-02 16 views
8

questa domanda si riferisce al 'nuovo' D: DMD32 D Compiler v2.068.2interfacciamento con D correttamente restituire un array di Struct

per TL; DR se non è necessario Salta alla domanda qui sotto

lavorare con visual Studio (sto usando v2010), con la creazione di un new project ->D ->Dynamic Library

quando il processo del progetto creartion è completa, nella soluzione Explorer c'è 2 file:

  • dllmain.d
  • dll.def

lasciando il file .def così com'è, sono riuscito per capire che con l'aggiunta di alcune nuove funzioni al dllmain.d e prefexing con:

extern (Windows) export 

esporterà la funzione e sarà richiamabile da c#, non provato con C o C++.

nota a margine, non toccare nessuno dei codici esistenti a meno che non si sappia cosa si sta facendo.

in modo che il codice qui sotto funziona come previsto

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

chiamandolo da C# con la seguente firma:

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

potrei facile usarlo come segue:

uint powD = D_mathPower(5); 

la mia domanda è

Come restituire un array di strutture (preferibilmente il modo più conveniente)?

struct dpack{ char* Name; uint Id; } 

ho provato utilizzando sia char[] e char* ma senza successo.

questo è il mio codice finora

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++ 
} 
+0

restituzione di array D per un altro linguaggio in grado di lavorare, ma di solito non perché i dettagli ABI non necessariamente corrispondere. Prova a creare il tuo tipo di struct con puntatore e lunghezza per l'interoperabilità o fai qualcosa come 'getArray (size_t * lengthPtr, dpack ** ptrPtr) {* lengthPtr = array.length; * ptrPtr = array.ptr; } ' –

+0

È consigliabile esercitarsi nella creazione di una DLL C e utilizzarla in C# prima di provarla con una DLL D. Scopri come usare le funzioni C che restituiscono "array" da C#, quindi usa la stessa tecnica in D. C# non ha alcuna conoscenza di come D memorizzi le slice, quindi non sarai in grado di usarle così com'è. –

risposta

1

Perché una matrice D ha un layout speciale si dovrebbe piuttosto restituire un puntatore alla prima voce. Poi in C# è possibile lanciare ogni elemento dal puntatore base con la lettura di 8 byte per 8 byte (questo corrisponde dpack.sizeof), dal momento che si conosce già il conteggio:

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; 
} 

anche per lanciare il membro .Name è necessario aggiungere un terminatore, altrimenti non puoi sapere la lunghezza della stringa.Questo è fatto da std.string.toStringz che aggiungerà un carattere null alla fine della stringa. Il membro char* Name può quindi essere lanciato come di solito sono le stringhe fornite da una funzione in una DLL con un'interfaccia C.

+0

hey grazie @nested type .. per il codice, ho provato tante varianti ma nessuna inclusa 'std.string' ho provato il tuo codice dopo che ho già trovato una risposta e dopo il benchmarking con l'implementazione' C++ ', ho quindi provato con 'std.string' e le prestazioni sono scese allo 0,75% rispetto al campo' char * '. e per quanto riguarda il codice più veloce in D (finora) che è quello che ho postato .. è ancora più lento dell'avvio più veloce in C++ è anche lo 0,75% del codice 'C++' così C++ era 45 ms, 'D (char *) 'era 57 ms e' D (std.string) 'era 79 ms. –

+0

ciò che è strano è che ogni volta che chiamo la funzione il contenuto è diverso la coda all'elemento [0] potrebbe essere una volta 'G', quindi eseguirla di nuovo è'; quindi è di nuovo 'G' non stabile, ha qualcosa fare con 'malloc' senza' free' o cosa potrebbe essere? –

0

questo è il modo più efficiente per implementarlo.

extern (Windows) export 
void D_getPacksPtr(uint size, dpack** DpArr) 
{ 
    *DpArr = cast(dpack*) malloc(size * dpack.sizeof); 
    dpack* curP = *DpArr; 
    char[] str = "abcdefghij".dup; 
    uint i=0; 
    while(i!=size){ 
     str[$ - 1] = cast(char)('0' + i % (126 - '0')); 
     curP.Name = cast(char*)str.dup; curP.Id = i; 
     ++i;++curP; 
    } 
} 

    [DllImport(@"PathTo...\DynamicLib1.dll", CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurity] 
    public static extern void D_getPacksPtr(uint size, dpack** DPArr); 

usarlo:

dpack* outDpack; 
    D_getPacksPtr(500000, &outDpack);