www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - D Bindings for C Opaque Pointers

reply Kyle Ingraham <kyle kyleingraham.com> writes:
Hello all. I am new to D and loving the experience so far.

I am trying to make use of a C library from Canon that provides a 
header and a pre-compiled binary containing implementations of 
declarations found in the header. I used the excellent guide at 
https://www.gamedev.net/articles/programming/general-and-gameplay-programming/b
nding-d-to-c-r3122/ to get started and so far I can initialize the library and
embed it within my D application. So far so great!

Where I am running into problems is creating D bindings for this 
function declaration:

// EDSDK.h
EdsError EDSAPI EdsGetCameraList(EdsCameraListRef* 
outCameraListRef);
//

If accepts a pointer to EdsCameraListRef that is declared as:

// EDSDKTypes.h
typedef struct __EdsObject* EdsBaseRef;
typedef EdsBaseRef EdsCameraListRef;
//

 From my reading I learned that EdsCameraListRef is an alias of an 
alias for an opaque pointer. It allowed the library writers to 
declare an object for use that the end-user would not have access 
to the implementation of. These are the D bindings I created:

// edsdk.d
struct EdsBaseRef;
alias EdsBaseRef EdsCameraListRef;
alias uint EdsUInt32;

extern (System):
EdsError EdsGetCameraList(EdsCameraListRef*);
EdsError EdsGetChildCount(EdsBaseRef*, EdsUInt32*);
EdsUInt32 EdsRelease(EdsBaseRef*);
//

Calling the functions using the bindings and the following code 
results in invalid pointer errors however (reported from the 
library):

// camera_list.d
import edsdk;

class CameraList
{
     private EdsCameraListRef* list;
     private EdsUInt32 _count = 0;
     this()
     {
         // Returns EDS_ERR_INVALID_POINTER
         EdsGetCameraList(list);
         // Returns EDS_ERR_INVALID_HANDLE
         EdsGetChildCount(list, &_count);
     }

     ~this()
     {
         EdsRelease(list);
     }

     final uint count()
     {
         return cast(uint) _count;
     }
}
//

What did I do wrong in constructing the bindings? If it helps the 
library provides a function called EdsRelease for cleaning-up 
allocated objects. Is the management of pointers between D and C 
the issue? Please forgive me if I've mangled that concept.
Dec 02 2020
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 3 December 2020 at 00:30:06 UTC, Kyle Ingraham wrote:
 // EDSDKTypes.h
 typedef struct __EdsObject* EdsBaseRef;
 typedef EdsBaseRef EdsCameraListRef;
 //
[...]
 // edsdk.d
 struct EdsBaseRef;
 alias EdsBaseRef EdsCameraListRef;
You've dropped a level of indirection here. In the C header, EdsBaseRef is a pointer, but in your D code, it is an opaque struct. The correct way to translate these C declarations into D is: struct __EdsObject; alias EdsBaseRef = __EdsObject*; alias EdsCameraListRef = EdsBaseRef; Note that unlike C, D does not allow us to refer to an incomplete type without first declaring it.
Dec 02 2020
parent Kyle Ingraham <kyle kyleingraham.com> writes:
On Thursday, 3 December 2020 at 00:58:20 UTC, Paul Backus wrote:
 On Thursday, 3 December 2020 at 00:30:06 UTC, Kyle Ingraham 
 wrote:
 // EDSDKTypes.h
 typedef struct __EdsObject* EdsBaseRef;
 typedef EdsBaseRef EdsCameraListRef;
 //
[...]
 // edsdk.d
 struct EdsBaseRef;
 alias EdsBaseRef EdsCameraListRef;
You've dropped a level of indirection here. In the C header, EdsBaseRef is a pointer, but in your D code, it is an opaque struct. The correct way to translate these C declarations into D is: struct __EdsObject; alias EdsBaseRef = __EdsObject*; alias EdsCameraListRef = EdsBaseRef; Note that unlike C, D does not allow us to refer to an incomplete type without first declaring it.
That did it. The binding works without issue now. Thanks a ton for the direction. I'll keep that distinction between C and D in mind as I go forward. It's been night and day between D and C++. I struggled for a while getting this work going in C++ but was firmly blocked by CMake. D and dub have been champs for ease of use.
Dec 02 2020
prev sibling parent reply bachmeier <no spam.net> writes:
On Thursday, 3 December 2020 at 00:30:06 UTC, Kyle Ingraham wrote:

 What did I do wrong in constructing the bindings? If it helps 
 the library provides a function called EdsRelease for 
 cleaning-up allocated objects. Is the management of pointers 
 between D and C the issue? Please forgive me if I've mangled 
 that concept.
Not an answer to your question, but the "idiomatic" approach is to not write bindings yourself. dstep generates bindings: https://github.com/jacob-carlborg/dstep dpp lets you include C header files directly: https://github.com/atilaneves/dpp
Dec 02 2020
parent Kyle Ingraham <kyle kyleingraham.com> writes:
On Thursday, 3 December 2020 at 01:19:05 UTC, bachmeier wrote:
 On Thursday, 3 December 2020 at 00:30:06 UTC, Kyle Ingraham 
 wrote:

 What did I do wrong in constructing the bindings? If it helps 
 the library provides a function called EdsRelease for 
 cleaning-up allocated objects. Is the management of pointers 
 between D and C the issue? Please forgive me if I've mangled 
 that concept.
Not an answer to your question, but the "idiomatic" approach is to not write bindings yourself. dstep generates bindings: https://github.com/jacob-carlborg/dstep dpp lets you include C header files directly: https://github.com/atilaneves/dpp
Thanks for the suggestions here. I had no idea that these tools existed. Now that I've gotten my feet wet with doing it manually it'll be interesting to compare results with those from automation. I'm all for it they can take some of the heavy lifting away.
Dec 02 2020