|Posted by Ethan||PermalinkReply|
I needed some DirectX 12 bindings. Autogeneration was out of the question. The .idl files Microsoft uses internally is full of plain C++ code that is inserted in to headers without additional parsing, and DirectX is basically useless without those #defines and function definitions. So in between compile times elsewhere, I hand converted the necessary headers to D modules.
Then me being me, I wanted to go one step further. Why should it just be a direct translation? Why not have some convenience methods added?
So now there's two libraries.
combindings - A thin layer that defines COM compatible UDAs and autogenerates some convenience features
directxbindings - Little more than direct hand-done translations of DirectX headers to D
These will be updated as I use them. And they're essentially mostly untested right now, I'm testing as I go.
They're also released under a Creative Commons 0 license. It seems to be the only really valid public domain license internationally; and these are COM bindings. There's no way I should be attributed on a random project just because you're using a very thin glue layer I wrote for something that should just plain exist as a D feature anyway (ie COM interface autogeneration).
COM Bindings aims for compatability first, and convenience second. The idea is that you should be able to copy/paste code from C++, replace the -> with . and off you go. That's not _entirely_ possible for one very good reason: the IID_PPV_ARGS macro. This expands to two symbols meant to be inserted in a function call in C++. If you want to do something similar in D, both templates and mixin templates are entirely unsuitable. You need to resort to string mixins. *UGLY* *UGLY* *UGLY* why even bother moving to D if you're going to be uglier than C++.
Next step then. We analyse function attributes. If we find a @_COM_Outptr_ or @_COM_Outptr_opt_ attribute in a function and the previous parameter is a REFIID, then sweet. We know what to do. For example, the following C++ code:
HRESULT result = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&device));
Can be translated to the following thanks to the wrapper stubs generated:
HRESULT result = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, device);
And if you really really want, you can still just do a direct translation of equivalent C++ code:
HRESULT result = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, &IID_IDirect3D12Device, cast(void**)&device);
Or try something that doesn't require linking to external variables:
HRESULT result = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, device.IIDPtrOf, cast(void**)&device);
HRESULT result = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, IIDPtrOf!IDirect3DDevice12, cast(void**)&device);
Likewise when you have functions where the output device is optional, you can just straight up template instantiate them:
HRESULT result = D3D12CreateDevice!IDirect3DDevice12(adapter, D3D_FEATURE_LEVEL_12_0 );