Thread overview
D Bindings for C Opaque Pointers
Dec 03, 2020
Kyle Ingraham
Dec 03, 2020
Paul Backus
Dec 03, 2020
Kyle Ingraham
Dec 03, 2020
bachmeier
Dec 03, 2020
Kyle Ingraham
December 03, 2020
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/binding-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.
December 03, 2020
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.
December 03, 2020
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
December 03, 2020
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.
December 03, 2020
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.